Awake()
はインスタンス化したときに必ず呼ばれる。
そんなふうに考えていた時期が僕にもありました。
今回はMonoBehaviour
クラスを継承することで使えるAwake()
について、勘違いしていたことについてのお話です。
この記事にはUnity2017.1.0f3を使用しています。
こんな簡単なコンポーネントを作成しましょう。
TestComponent.cs
using UnityEngine; public class TestComponent : MonoBehaviour { [SerializeField] private int m_number = 0; public int Number { get { return m_number; } } private void Awake() { Debug.Log( "Awake()" ); } } // class TestComponent
このコンポーネントをアタッチしたGameObjectをHierarchy上に配置します。
これを再生してみましょう。
Awake()
Awake()
が呼ばれていますね。
では今度はこのGameObjectを非アクティブ状態にして、再生してみましょう。
Awake()
が呼ばれていない!
一体どういうことだ。
Awake()
はインスタンス化したときに必ず呼ばれるのではないのか!
ドキュメントを見てみましょう。
docs.unity3d.com
Awake: この関数は常に Start 関数の前およびプレハブのインスタンス化直後に呼び出されます。
うん、だよね。そうだよね。
でも呼ばれていないんだよ。
おっと、まだ続きがありますね。
Awake: この関数は常に Start 関数の前およびプレハブのインスタンス化直後に呼び出されます。(もしゲームオブジェクトがスタートアップ時に無効である場合、有効化にされるまで Awake は呼び出されません。)
もしゲームオブジェクトがスタートアップ時に無効である場合、有効化にされるまで Awake は呼び出されません。
そうだったのかー。
こ、これは勘違いしていた。
もう少しテストしておきましょう。
このGameObjectを非アクティブ状態のまま、Prefab化します。
そして以下のコンポーネントを作成し、先ほどPrefab化したオブジェクトを読み込みインスタンス化します。
TestController.cs
using UnityEngine; public class TestController : MonoBehaviour { private void Start() { Debug.LogFormat( "Before Instantiate()" ); var test = Instantiate( Resources.Load<TestComponent>( "TestComponent" ) ); Debug.LogFormat( "After Instantiate()" ); Debug.LogFormat( "Number = {0}", test.Number ); } } // class TestController
Before Instantiate()
After Instantiate()
Number = 20
この場合でもAwake()
は呼ばれない。
でも変数はシリアライズ化された数値がちゃんと取得できているようです。
Prefabをアクティブ状態にして、再度読み込みインスタンス化してみましょう。
Before Instantiate()
Awake()
After Instantiate()
Number = 20
この状態ならInstantiate()
を呼ばれた直後にちゃんとAwake()
が呼ばれていますね。
うーむ、この仕様は気を付けなければ。
Awake()
をコンストラクタと同様なものと思って開発していったら、とんでもないバグを生み出してしまいそうだ。
この世のすべてのバグが成仏しますように。