Skip to main content

クラス

C#の純粋クラス vs MonoBehaviour

お気づきかもしれないが、通常のC#クラスとUnityの「MonoBehaviour」クラスにはいくつかの違いがある。基本的に、どちらのクラスもプロパティ(クラス変数)とメソッドを持てる。しかし、大きな違いもある。

  1. 通常のC#クラスはUnityエディターでは使用できない。GameObjectにアタッチできるのはMonoBehaviourのみである。
  2. 通常のC#クラスはnewキーワードを使ってインスタンス化するが、MonoBehaviourは直接インスタンス化できない。
通常のC#クラス MonoBehaviour

一般 C# のものであり、Unity 以外でも使える

Unity専用のキーワードである(Unity以外に使えない)

: MonoBehaviour」がつかない

 

class Player
{
  // メンバー変数とメソッド
}

: MonoBehaviour」がつく

 

class Enemy : MonoBehaviour
{
  // メンバー変数とメソッド
}

new をつかい、クラスからインスタンスを作成

 

// クラスから具体的に使える「p1」のプレーヤーを作成
Player p1 = new Player(); 

new を使えない

 

Enemy e = new Enemy(); // エラーが発生する

UnityでGameObjectにアタッチ不可

 

image.png

UnityでGameObjectにアタッチ可能

 

image.png

どれを使う?

では、どちらをいつ使うべきか?基本的な目安として、GameObjectに機能を追加したい場合はMonoBehaviourを使う。例えば、プレイヤーの入力に基づいてGameObjectを制御したい場合や、画面上のスコアを更新したい場合は、その処理に専念するMonoBehaviourを作ればよい。

ゲーム内で通常表示されない内部処理にはC#クラスを使うパターンはよくある。例えば、プレイヤーのインベントリを管理するC#クラスや、ファイルのセーブ・ロードを制御するC#クラスを作ることができる。

アクセス修飾子

基本的な目安として、データを保護しクラスの外部から誤って値を変更しないよう、クラスのメンバー変数は常に「private」に設定する。

クラスの外部から値にアクセスする必要がある場合は、publicメソッドまたはpublicゲッター(下記の例を参照)を使うことができる。

MonoBehaviourの場合、Unityがエディターで変数を変更できるようにする必要がある。これは変数をprivateのままにし、変数に[SerializeField]を追加することで実現できる。

  • やりたいこと:Unityにある数値を公開し、エディター(Inspector)で編集できるようにしたい 例:車の速度
NG 正しい方法

上記の例と同様に、Unityに限らず、どこからも編集できる変数なので、大変危ない!

 

class Car : MonoBehaviour
{
   // 車の速度をUnityから編集したいため、publicにした!
    public int Speed; 
}

privateにし、[SerializeField] をつければ良い。

 

 

class Car : MonoBehaviour
{
   // UnityのInspectorから変えることができるが、
    // 他のクラスからアクセス不可
    [SerializeField]
    private int speed; 
}
  • やりたいこと:ある数値を別のクラスで読みたい。
     例:プレーヤーを持っているお金
NG 正しい方法

public(公開)にすると、Player以外からアクセスでき、誤って数値を消したり可能性があるので、「public」を避けよう。

 

class Player
{
   // これを避けましょう!
    public int Money; 
}

1.まず、メンバー変数を非公開にし(private)、メソッドで取得。

 

class Player
{
   // privateで変数を保護
    private int money;

   // メソッドで数値を返し、外から代入できない
    public int GetMoney()
    {
        return money;
    }
}

2.もう少し賢い方法は、「ゲッター」を使うこと:

 

class Player
{
   // Moneyというメンバー変数は:
    //「get」(取得)は公開である
    //「set」(代入)は非公開である
    public int Money { get; private set; }
}
  • やりたいこと:ある数値を別のクラスで更新した。
     例:プレーヤーの体力。
NG 正しい方法

この場合は、攻撃や回復アイテムで現在の体力の数値を別のクラスから変えたい場合もあると思い、「public」にした。

 

class PlayerHP : MonoBehaviour
{
    // 最大体力はUnityで調整できるようにする
    [SerializeField]
    private int maxHP;
  
   // 当然、攻撃などで、現在の体力を外からでも変えたい
    public int nowHP; 
}

ただ、ここは大きなお落とし穴がある。次の例を考えてください:

・最大耐力 maxHP は 120 に設定にした

・現在の体力 nowHP は 50 である

・回復アイテムを使用し、+100を回復

 

とすると、nowHP + 100 = 150 であり、最大体力を超えてしまう!

 

同様に 80 の攻撃を受け取るとしたら:nowHP - 80 = -30 

マイナス体力になってしまった!

1.まず、メンバー変数を非公開にし(private)、代入の取得のメソッドを作成。

 

class PlayerHP : MonoBehaviour
{
    // 最大体力はUnityで調整できるようにする
    [SerializeField]
    private int maxHP;
  
   // 体力を保護する
    private int nowHP; 

    // 読み込む専用
    public int GetHP() 
    {
        return nowHP;
    }

    // 書き込む専用
    public int SetHP(int value)
    {
        // 数値の制限を確認
        // Mathf.Min は 2つの引数の最小値を返す
        nowHP = Mathf.Min(value, maxHP); 
        
        // Mathf.Max は 2つの引数の最大値を返す
        nowHP = Mathf.Max(value, 0); 
    }
}

これで、SetHPでは、体力が必ず 0 ~ maxHPの間になることを保証できる。

 

 

2.もう少し賢い方法は、「ゲッター」と「セッター」を使うこと:

 

class PlayerHP : MonoBehaviour
{
    // 最大体力はUnityで調整できるようにする
    [SerializeField]
    private int maxHP;
  
   // 非公開で中身を守る
    private int nowHP;

    // 公開ゲッターとセッター
    public int NowHP
    {
        // 取得
        get { return nowHP; } 

        // 代入
        set { 
          nowHP = Mathf.Min(value, maxHP);
          nowHP = Mathf.Max(value, 0);
        }
    }
}

練習課題

単純なインベントリを作成してみましょう:

  • やりたいこと:ある数値を別のクラスで更新した。
     例:プレーヤーの体力。アイテムを表すクラスを作る(ファイル名:Item
    • アイテム名を代入と取得できるようにする
    数も代入と取得できるようにする 使用例

    C#の純粋クラス

    vs
    Item coin = new Item();
    coin.Name = "コイン";
    coin.Count = 20;
    答え
    // 何かのアイテムを表すクラス
    class Item
    {
        // アイテム名(非公開)
        private string name;
        
        // アイテムの数(非公開)
        private int count;
    
        // アイテム名(公開)
        public string Name { 
            get { return name }
            set { name = value; }
        }
        
        // アイテムの数(公開)
        public string Count { 
            get { return count }        
    
            // マイナスアイテムは無意味なので、必ず0以上にする
            set { name = Mathf.Max(0, count); } 
        }    
    }
      アイテムを管理する「Inventory」クラスを作成(ファイル名:Inventory
        アイテムを何個も追加できるようにする(ヒント:自由に伸びる配列!) 中身のすべてを確認できるようにする 使用例
        Inventory inv = new Inventory();
        inv.Add(coin); // 先作ったコインを追加
        inv.Print();   // Debug.Logで中身を表示する:「コイン:20」
        答え
        // アイテムを管理する
        class Inventory
        {
            // アイテムのリスト
            private List<Item> items = new List<Item>();
            
            // 追加メソッド
            public void Add(Item item)
            {
                items.Add(item);
            }
            
            // 表示メソッド
            public void Print()
            {
                foreach(Item i in items)
                {
                    Debug.Log($"{i.Name} : {i.Count}");
                }
            }    
        }
          テスト用のスクリプトを作成(ファイル名:RunTest
            コイン20個を作成し、インベントリに追加 ポーション2個を作成し、インベントリに追加 ナイフ1個を作成し、インベントリに追加 インベントリの中身を表示
            答え
            // 確認用のスクリプト
            class RunTest : MonoBehaviour
            

            練習課題

            {     void Start()     {         Item coin = new Item();         coin.Name = "コイン";         coin.Count = 20;                  Item potion = new Item();         potion.Name = "ポーション";         potion.Count = 2;                  Item knife = new Item();         knife.Name = "ナイフ";         knife.Count = 1;                  Inventory inv = new Inventory();         inv.Add(coin);         inv.Add(potion);         inv.Add(knife);                  inv.Print();             } }