C#の癖なのか、Unityの癖なのか、どっちも初心者の中途半端C++プログラマーが若干悩んだ問題。なぜインスタンスを生成していないのに、thisでインスタンスを代入しているのか?
シングルトンパターンとは
クラスに対して1つのインスタンスしか生成しないようにする設計方法。あとはWikipediaをご参考あれ。
クラスのメンバーに自分自身の変数を定義する
クラス内にstatic領域の自分自身の定義を作って、thisでインスタンスを入れている。目的は、他のクラスからこのクラスにアクセスしたいから。
ConfigLoader.cs
public class ConfigLoader : MonoBehaviour
{
public static ConfigLoader Instance { get; private set; }
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
LoadDemoSceneSetup();
}
}
なぜnewしていないのにthisが存在するのか
結論から言うと、thisが使える時点でConfigLoaderのインスタンスはすでに存在しています。ただし、そのインスタンスを通常のC#クラスのように自分でnewしているのではなく、Unity側がGameObjectのComponentとして生成・管理しています。
Unityの公式マニュアルでも、MonoBehaviourを継承したスクリプトはGameObjectにアタッチされるComponentの「設計図」であり、GameObjectにアタッチするたびにそのスクリプトのインスタンスが作られると説明されています。実行時に追加する場合はGameObject.AddComponent<T>()、PrefabやGameObjectを複製する場合はObject.Instantiateなどを通してUnity側がComponentのインスタンスを扱います。
そのため、Awake()の中で
Instance = this;
としているのは、「ここでインスタンスを作っている」のではなく、UnityがすでにロードしたこのComponent自身への参照をstatic変数へ保存しているということです。Awake()も、Unity公式では「スクリプトコンポーネントのインスタンスをロードしたときに呼ばれる」と説明されています。
・Unity Manual: MonoBehaviour
・Unity Scripting API: MonoBehaviour.Awake()
・Unity Scripting API: GameObject.AddComponent
これは本当にシングルトンなのか
このコードは他のクラスから1つのInstanceへアクセスするための、シングルトン風の書き方ではあります。ただし、このコードだけではConfigLoaderのインスタンスが物理的に1個しか存在しないことまでは保証していません。
例えばScene上にConfigLoaderを2つ配置した場合、最初にAwake()された方がInstanceへ入りますが、2個目のConfigLoader自体も存在し、そのAwake()も実行されます。このコードではInstanceへの代入をしないだけで、重複したComponentをDestroyしていません。
したがって、厳密に1インスタンスへ制限したいなら、Scene構成で1個しか置かないようにするか、重複時にDestroyするなど別の制御が必要です。元の疑問に対する答えとしては、MonoBehaviourのインスタンスはUnityがGameObjectのComponentとして生成しており、thisはその既存インスタンスを指している、となります。


コメント