うら干物書き

ゲームを作っています。

【Unity】僕はAwake()のことを知っているようで知らなかったんだ

 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上に配置します。
f:id:urahimono:20170728080731p:plain

 これを再生してみましょう。
f:id:urahimono:20170728080741p:plain

Awake()

 Awake()が呼ばれていますね。
 では今度はこのGameObjectを非アクティブ状態にして、再生してみましょう。

f:id:urahimono:20170728080753p:plain
f:id:urahimono:20170728080801p:plain

 Awake()が呼ばれていない!
 一体どういうことだ。
 Awake()はインスタンス化したときに必ず呼ばれるのではないのか!

 ドキュメントを見てみましょう。
docs.unity3d.com

Awake: この関数は常に Start 関数の前およびプレハブのインスタンス化直後に呼び出されます。

 うん、だよね。そうだよね。
 でも呼ばれていないんだよ。
 おっと、まだ続きがありますね。

Awake: この関数は常に Start 関数の前およびプレハブのインスタンス化直後に呼び出されます。(もしゲームオブジェクトがスタートアップ時に無効である場合、有効化にされるまで Awake は呼び出されません。)

もしゲームオブジェクトがスタートアップ時に無効である場合、有効化にされるまで Awake は呼び出されません。

 そうだったのかー。
 こ、これは勘違いしていた。

 もう少しテストしておきましょう。

 このGameObjectを非アクティブ状態のまま、Prefab化します。
f:id:urahimono:20170728081116p:plain

 そして以下のコンポーネントを作成し、先ほど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

f:id:urahimono:20170728081129p:plain
f:id:urahimono:20170728081138p:plain

Before Instantiate()
After Instantiate()
Number = 20

 この場合でもAwake()は呼ばれない。
 でも変数はシリアライズ化された数値がちゃんと取得できているようです。

 Prefabをアクティブ状態にして、再度読み込みインスタンス化してみましょう。

f:id:urahimono:20170728081152p:plain
f:id:urahimono:20170728081201p:plain

Before Instantiate()
Awake()
After Instantiate()
Number = 20

 この状態ならInstantiate()を呼ばれた直後にちゃんとAwake()が呼ばれていますね。

 うーむ、この仕様は気を付けなければ。
 Awake()をコンストラクタと同様なものと思って開発していったら、とんでもないバグを生み出してしまいそうだ。

 この世のすべてのバグが成仏しますように。