# クラス

## C#の純粋クラス vs MonoBehaviour

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

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

<table border="1" id="bkmrk-%E9%80%9A%E5%B8%B8%E3%81%AEc%23%E3%82%AF%E3%83%A9%E3%82%B9-monobehavio" style="border-collapse: collapse; width: 100%; height: 517.834px;"><colgroup><col style="width: 50%;"></col><col style="width: 50%;"></col></colgroup><thead><tr style="height: 29.8px;"><td class="align-center" style="height: 29.8px;">**通常のC#クラス**</td><td class="align-center" style="height: 29.8px;">**MonoBehaviour**</td></tr></thead><tbody><tr><td>一般 C# のものであり、Unity 以外でも使える

</td><td>Unity専用のキーワードである（Unity以外に使えない）

</td></tr><tr style="height: 158.567px;"><td style="height: 158.567px;">「`: MonoBehaviour`」がつかない

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

</td><td style="height: 158.567px;">「`: MonoBehaviour`」がつく

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

</td></tr><tr style="height: 120.167px;"><td style="height: 120.167px;">`new` をつかい、クラスからインスタンスを作成

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

</td><td style="height: 120.167px;">`new` を使えない

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

</td></tr><tr style="height: 209.3px;"><td style="height: 209.3px;">UnityでGameObjectにアタッチ不可

[![image.png](https://class.illogic.games/uploads/images/gallery/2026-04/scaled-1680-/spTimage.png)](https://class.illogic.games/uploads/images/gallery/2026-04/spTimage.png)

</td><td style="height: 209.3px;">UnityでGameObjectにアタッチ可能

[![image.png](https://class.illogic.games/uploads/images/gallery/2026-04/scaled-1680-/image.png)](https://class.illogic.games/uploads/images/gallery/2026-04/image.png)

</td></tr></tbody></table>

### どれを使う？

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

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

## アクセス修飾子

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

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

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

- <span style="text-decoration: underline;">やりたいこと</span>：Unityにある数値を公開し、エディター（Inspector）で編集できるようにしたい 例：車の速度

<table border="1" id="bkmrk-ng-%E6%AD%A3%E3%81%97%E3%81%84%E6%96%B9%E6%B3%95-%E4%B8%8A%E8%A8%98%E3%81%AE%E4%BE%8B%E3%81%A8%E5%90%8C%E6%A7%98%E3%81%AB%E3%80%81un" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50.0596%;"></col><col style="width: 50.0596%;"></col></colgroup><thead><tr><td class="align-center">**NG**</td><td class="align-center">**正しい方法**</td></tr></thead><tbody><tr><td>上記の例と同様に、Unityに限らず、どこからも編集できる変数なので、大変危ない！

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

</td><td>`private`にし、`[SerializeField]` をつければ良い。

```c#
class Car : MonoBehaviour
{
  　// UnityのInspectorから変えることができるが、
    // 他のクラスからアクセス不可
    [SerializeField]
    private int speed; 
}
```

</td></tr></tbody></table>

- <span style="text-decoration: underline;">やりたいこと</span>：ある数値を別のクラスで読みたい。  
     例：プレーヤーを持っているお金

<table border="1" id="bkmrk-ng-%E6%AD%A3%E3%81%97%E3%81%84%E6%96%B9%E6%B3%95-public%EF%BC%88%E5%85%AC%E9%96%8B%EF%BC%89%E3%81%AB" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50.0596%;"></col><col style="width: 50.0596%;"></col></colgroup><thead><tr><td class="align-center">**NG**</td><td class="align-center">**正しい方法**</td></tr></thead><tbody><tr><td>public（公開）にすると、Player以外からアクセスでき、誤って数値を消したり可能性があるので、「public」を避けよう。

```c#
class Player
{
  　// これを避けましょう！
    public int Money; 
}
```

</td><td>１．まず、メンバー変数を非公開にし（`private`）、メソッドで取得。

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

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

２．もう少し賢い方法は、「ゲッター」を使うこと：

```c#
class Player
{
  　// Moneyというメンバー変数は：
    //「get」（取得）は公開である
    //「set」（代入）は非公開である
    public int Money { get; private set; }
}
```

</td></tr></tbody></table>

- <span style="text-decoration: underline;">やりたいこと</span>：ある数値を別のクラスで更新した。  
     例：プレーヤーの体力。

<table border="1" id="bkmrk-ng-%E6%AD%A3%E3%81%97%E3%81%84%E6%96%B9%E6%B3%95-%E3%81%93%E3%81%AE%E5%A0%B4%E5%90%88%E3%81%AF%E3%80%81%E6%94%BB%E6%92%83%E3%82%84%E5%9B%9E%E5%BE%A9" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50.0596%;"></col><col style="width: 50.0596%;"></col></colgroup><thead><tr><td class="align-center">**NG**</td><td class="align-center">**正しい方法**</td></tr></thead><tbody><tr><td>この場合は、攻撃や回復アイテムで現在の体力の数値を別のクラスから変えたい場合もあると思い、「public」にした。

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

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

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

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

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

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

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

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

</td><td>１．まず、メンバー変数を非公開にし（`private`）、代入の取得のメソッドを作成。

```c#
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`の間になることを保証できる。

２．もう少し賢い方法は、「ゲッター」と「セッター」を使うこと：

```c#
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);
        }
    }
}
```

</td></tr></tbody></table>

## 練習課題

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

- アイテムを表すクラスを作る（ファイル名：`Item`） 
    - アイテム名を代入と取得できるようにする
    - 数も代入と取得できるようにする
    - 使用例

```c#
Item coin = new Item();
coin.Name = "コイン";
coin.Count = 20;
```

<details id="bkmrk-%E7%AD%94%E3%81%88-%2F%2F-%E4%BD%95%E3%81%8B%E3%81%AE%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0%E3%82%92%E8%A1%A8%E3%81%99%E3%82%AF%E3%83%A9%E3%82%B9-"><summary>答え</summary>

```c#
// 何かのアイテムを表すクラス
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); } 
    }    
}
```

</details>- アイテムを管理する「`Inventory`」クラスを作成（ファイル名：`Inventory`） 
    - アイテムを何個も追加できるようにする（ヒント：[自由に伸びる配列](https://class.illogic.games/books/c/page/0353a#bkmrk-%E8%87%AA%E7%94%B1%E3%81%AB%E4%BC%B8%E3%81%B3%E3%82%8B%E9%85%8D%E5%88%97-%3A-list)！）
    - 中身のすべてを確認できるようにする
    - 使用例

```c#
Inventory inv = new Inventory();
inv.Add(coin); // 先作ったコインを追加
inv.Print();   // Debug.Logで中身を表示する：「コイン：20」
```

<details id="bkmrk-%E7%AD%94%E3%81%88-%2F%2F-%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0%E3%82%92%E7%AE%A1%E7%90%86%E3%81%99%E3%82%8B-clas"><summary>答え</summary>

```c#
// アイテムを管理する
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}");
        }
    }    
}
```

</details>- テスト用のスクリプトを作成（ファイル名：`RunTest`） 
    - コイン20個を作成し、インベントリに追加
    - ポーション２個を作成し、インベントリに追加
    - ナイフ１個を作成し、インベントリに追加
    - インベントリの中身を表示

<details id="bkmrk-%E7%AD%94%E3%81%88-%2F%2F-%E7%A2%BA%E8%AA%8D%E7%94%A8%E3%81%AE%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88-clas"><summary>答え</summary>

```c#
// 確認用のスクリプト
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();        
    }
}
```

</details>