Unityでクラスのインスタンスに別スクリプトからアクセスしたい

![SS 192.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/a41cd967-f549-8677-1efa-2e99bf69518d.png) こっちのデータにあっちからアクセスしたい...

## やること
UnityとC#の話です。
スクリプトAで作成したクラスのインスタンスのプロパティに、
スクリプトBからアクセスします。
簡単そうで手こずりました。
![SS 1151.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/914edde7-c190-34b2-39f5-0f585c603f3a.png)

基本的な構造でまず理解し、少しずつ変更しながら進めます。
自分用メモです。

## 作業環境
* Unity (バージョン 2022.3.8f1)

# 基本の動作確認

### STEP:1　クラスとインスタンス用のスクリプト

クラスを設定してインスタンスを宣言します。
さきほどの図ではスクリプトAに対応します。
このインスタンスを本丸とし、その本丸が持つプロパティの値に、外部からアクセスしたいのです。

```C#:ClassX.cs
using UnityEngine;
public class ClassX : MonoBehaviour

{
    // publicでNestedClassクラスを設定
    public class NestedClass
    {
        public int propertyA;
        public string propertyB;
    }

    // publicでNestedClassクラスのインスタンスを作成
    public NestedClass nestedClassInstance; //これが今回の本丸

    // Awakeでインスタンスを初期化
    private void Awake()
    {
        nestedClassInstance = new NestedClass(); //重要な儀式
    }

}
```
Awakeのタイミングで作成したインスタンスを初期化するのがポイントです。
（それに気づくのに時間がかかりました。）

### STEP:2　アクセスする側のスクリプト

先ほどの図では忍者がいたほうのスクリプトBに該当します。
スクリプト名をMain.csとし、さきほどスクリプトAで設定したクラスのインスタンス（本丸）にアクセスします。

```C#:Main.cs
using UnityEngine;

public class Main : MonoBehaviour
{

    private void Start()
    {
        // ClassXのインスタンスを取得
        ClassX classX = GetComponent<ClassX>();
        //ClassX classX = FindObjectOfType<ClassX>(); なら全検索

        // classXクラスのインスタンスにアクセス
        ClassX.NestedClass instanceX = classX.nestedClassInstance;//本丸を取得

        // インスタンスのプロパティにアクセス
        instanceX.propertyA = -10000;
        instanceX.propertyB = "Hoge";
    }

    public void Update()
    {
        // 名前を変えてClassXのインスタンスを取得
        ClassX classYY = GetComponent<ClassX>();

        // 名前を変えてclassXクラスのインスタンスにアクセス
        ClassX.NestedClass instanceYYY = classYY.nestedClassInstance;//本丸を取得

        // インスタンスのプロパティにアクセス
        instanceYYY.propertyA++;
        instanceYYY.propertyB = "Fuga";

        // コンソール表示
        Debug.Log(instanceYYY.propertyA);
        Debug.Log(instanceYYY.propertyB);
    }
}
```
同じオブジェクトにClassX.cs、Main.csの両方のスクリプトをアタッチすることを前提としています。

StartとUpdateでインスタンス名を変えていますが、
どちらも同じく、本丸であるClassX.cs内のnestedClassInstansインスタンスにアクセスできています。

### STEP:3　Unityを実行する
Unityの空オブジェクトに、ClassX.csとMain.csの両方をアタッチして実行します。

![SS 1148.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/2dbb3c1e-87b8-fae6-4479-ebaa26bbc248.png)

コンソールに結果が表示され、インスタンスのプロパティにアクセスできていることがわかります。
Start時にpropertyAに-10000を設定し、その後Updateのたびに値に+1しています。

![SS 1149.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/159cdffa-dc5e-e016-21dd-6e3d9942af68.png)


# ゲッター/セッターを使ったアクセス

プロパティやインスタンスに { get; set; }をつけるだけでOKです。
ゲッター/セッターは変数への直接的なアクセスを制限することができ、データの整合性を保つのに役立ちます。Main.csは同じもので動きます。

```C#:ClassX.cs
using UnityEngine;

public class ClassX : MonoBehaviour
{
    // NestedClassクラス
    public class NestedClass
    {
        // プロパティをget,setアクセサ付きで定義
        public int propertyA { get; set; }
        public string propertyB { get; set; }
    }

    // NestedClassプロパティを設定
    public NestedClass nestedClassInstance { get; private set; }

    // Awakeでインスタンスを初期化
    private void Awake()
    {
        nestedClassInstance = new NestedClass();
    }
}
```
実行すると、さきほどと同じ結果になります。


# フィールド利用でアクセス回数を減らす

フィールドにインスタンス領域を確保することで、起動時のみ、該当のクラスの検索と登録行うようにします。
下記の例ではFindObjectOfType<ClassX>()を使用しているため、ClassX.csのアタッチ場所はMain.csと以外のオブジェクトでも動きます。


```C#:Main.cs
using UnityEngine;

public class Main : MonoBehaviour
{
    private ClassX classX; // ClassXのインスタンスを格納するフィールド

    private void Start()
    {
        // ClassXのインスタンスを取得し、フィールドに格納
        classX = FindObjectOfType<ClassX>(); //今回は全検索版

        // classXクラスのインスタンスにアクセス
        ClassX.NestedClass instanceX = classX.nestedClassInstance;//本丸を取得

        // インスタンスのプロパティにアクセス
        instanceX.propertyA = -10000;
        instanceX.propertyB = "Hoge";
    }

    public void Update()
    {
        // 名前を変えてclassXクラスのインスタンスにアクセス
        ClassX.NestedClass instanceX = classX.nestedClassInstance;//本丸を取得

        // インスタンスのプロパティにアクセス
        instanceX.propertyA++;
        instanceX.propertyB = "Fuga";

        // コンソール表示
        Debug.Log(instanceX.propertyA);
        Debug.Log(instanceX.propertyB);
    }
}
```
こちらも実行すると、さきほどと同じ結果になります。

フィールド、ゲッター、セッターの全てを適用したラスト２つスクリプトの組み合わせを基本にすると良さそうです。




## 参考
chatGPTさんに教えてもらいました。