第19回で、リストの内容をリストの外(セーブデータなど)から渡す処理を実装するにあたって、ゲームオブジェクト間の連携がうまくいかない点がいくつかあったため、Awake()、Enable()/Disable()、Start()/Update() のコールタイミングを調べました。
最初にアクティブになる時のメッセージコール順
最初にアクティブになる時のメッセージコール順をシーケンス図にすると、上のような感じです。
Awake() と Start() は、それぞれ最初の GameObject.SetActive(true) 後に1回だけ呼ばれます。
Awake() は OnEnable() の直前に、Start() は Update() の直前に呼ばれます。複数のゲームオブジェクトが同時にアクティブになる時は、Awake() と Start() は少し呼ばれ方が異なる点に注意が必要と思われます。
Awake()
Awake() は OnEnable() はゲームオブジェクト単位でセットで呼ばれます。そのため、自身のAwake() が終わって最初のOnEnable() が呼ばれた時、まだ他のゲームオブジェクトはAwake()されていない可能性があるということです。
Start()
一方、Start() は Update() とセットで呼ばれるわけではありません。複数のゲームオブジェクトが同時にアクティブになる時、すべてのゲームオブジェクトが Update() されるより前に、すべてのゲームオブジェクトに Start() が呼ばれます。そのため、最初のUpdate() が呼ばれた時、他のゲームオブジェクトは Start() しています。※ただし、プリファブが混じっていたりする時は、挙動が違うかもしれません。
OnEnable()
OnEnable() は、ゲームオブジェクトがActive()になる度にコールされます。そのため、この処理の中でtransformに変更を加えたりしたくなりますが、最初にアクティブになる時にAwake()とセットで呼ばれることから、他のオブジェクトと協調動作には向いていません。OnEnable() を活用する場合は、Awake() 直後の OnEnable() だけ動作を変えるための状態フラグを定義する必要があります。
各タイミングでできそうな初期化処理
メッセージ | 可能な初期化処理 |
---|---|
Awake() | 参照(gameObject, transformなど)だけを使って内部変数を初期化する |
OnEnable() | アクティブになる度に初期化すべき内部変数を初期化する |
Start() | GameObject.Find()など、他のゲームオブジェクトがアクティブでないと使えない方法を使って内部変数を初期化する |
Update() | アクティブになった時に必要なその他の処理を行う(状態フラグで処理を切り替える) |
どのゲームオブジェクトからコールされるのか?
尚、Awake()、OnEnable()、Start()、Update() 時にどのゲームオブジェクトから順に呼ばれるかは、ゲームオブジェクトが作られた順、またはゲームオブジェクトが保存されるときに付加されるID(GUID)で決まります。GUID は乱数を使って生成されるため、そのままではゲームオブジェクトが呼ばれる順番を制御できません。ゲームオブジェクトを呼ぶ順番を決めるには、Project Settings の Script Execution Order を使います。
非アクティブ時の動作
OnDisable()
スクリプトが所属する GameObject が SetActive(false) されるとき、OnDisable() が呼ばれます。OnDisable() は、上記と異なり、親子関係に沿って呼ばれます。親、子、孫ゲームオブジェクトがあって、親ゲームオブジェクトがSetActive(false)されると、孫→子→親の順にゲームオブジェクトが非アクティブになります。