yotiky Tech Blog

とあるエンジニアの備忘録

複数のComponentのイベント関数の実行順

Unity 基礎シリーズ 目次

目次

検証環境

Unity:2017.4.5f1

Componentが複数の場合の実行順

基本的にHierarchyやInspectorの配置順に関係なく、Componentの実行順は保証されない。

1つのGameObjectに複数のComponent

Editor上でComponentをAddした順番を記憶してて、最後に追加したComponentから逆順に実行されているように見える。上から順にComponentを貼っつけていった場合、綺麗に降順になる。

f:id:yotiky:20181120132245p:plain

  1. Component3.Awake
  2. Component2.Awake
  3. Component1.Awake
  4. Component3.Start
  5. Component2.Start
  6. Component1.Start
  7. Component3.Update
  8. Component2.Update
  9. Component1.Update

次に下のように、Component3、Component2、Component1の順に貼っ付けると結果はこうなる。並び順でもなく追加した順なのがわかる。

f:id:yotiky:20181121011040p:plain

  1. Component1.Awake
  2. Component2.Awake
  3. Component3.Awake
  4. Component1.Start
  5. Component2.Start
  6. Component3.Start
  7. Component1.Update
  8. Component2.Update
  9. Component3.Update

だがしかしUnityを再起動したりSceneをUnload/Loadしようものなら、手中に収めていた実行順がたちまちランダムになる。1つのGameObjectについている複数のComponentはおそらく下から順に呼ばれるようになる。この辺の挙動を紐解く。

Componentは何順に実行されるのか

InspectorをDebug表示にするとInstanceIDが表示されるのだが、Componentの呼び出し順はこのIDの昇順になってそうだ。問題なのはこのIDはシリアライズされないので、Unityの再起動やSceneの読み込みなどのタイミングで一定のロジックを通って採番されなおすことになる。

こちらは上から順にComponentを貼っつけた状態。新たにくっつけたComponentは、マイナス方向にある程度幅を取った数字が割り振られていく。IDの昇順なので、このまま実行すればComponent1、Component2、Component3の順に呼び出される。

f:id:yotiky:20181122005354p:plain

  1. Component1.Awake
  2. Component2.Awake
  3. Component3.Awake

続いて、1度SceneをUnloadしてLoadし直す。するとプラス方向に下から上へIDが採番され直しているのがわかる。仮にランダムな順番にComponentをくっつけた場合でも下から順に採番され直す。この例だと実行結果は先程と変わらない。

f:id:yotiky:20181122005416p:plain

  1. Component1.Awake
  2. Component2.Awake
  3. Component3.Awake

1つのGameObject内であれば採番され直したタイミングで下から順に呼び出されるようになる。たぶん。
これが編集中は新たに貼っつけたComponentとIDの順番があべこべ状態になって実行順がランダムになると錯乱する原因かも。

ツリー状のGameObjectの場合の実行順

以前の記事では他への参照のありなしや、他のGameObjectを使ってみたりして順番に変化が起きるか検証したりもしたが影響がないので分けずに書く。

こちらも上から順にComponentを貼っつけていった場合、そのまま実行すれば綺麗に降順になる。

f:id:yotiky:20181120132228p:plain

  1. GameObject3-1-1.Awake
  2. GameObject3-1.Awake
  3. GameObject3.Awake
  4. GameObject2-1-1.Awake
  5. GameObject2-1.Awake
  6. GameObject2.Awake
  7. GameObject1-1-1.Awake
  8. GameObject1-1.Awake
  9. GameObject1.Awake

次に孫、子、親の順で下からつけた場合の結果。親、子、孫の順に呼び出される。

  1. GameObject1.Awake
  2. GameObject2.Awake
  3. GameObject3.Awake
  4. GameObject1-1.Awake
  5. GameObject2-1.Awake
  6. GameObject3-1.Awake
  7. GameObject1-1-1.Awake
  8. GameObject2-1-1.Awake
  9. GameObject3-1-1.Awake

そしてUnityを再起動したりSceneをUnload/Loadしようものなら、手中に収めていた実行順がたちまちランダムになる。 今度は先程のように予測可能な状態ではない。一度ランダムになると変更を一切加えなければUnityを再起動してもシャッフルされないので、何かしらの採番ルールはありそうではあるが、凡人には到底予測不可能な値で採番される。

こちらはComponentを追加した直後のID。実行すれば降順で呼び出される。

name InstanceID
GameObject1 -26518
GameObject1-1 -26790
GameObject1-1-1 -26980
GameObject2 -27150
GameObject2-1 -27386
GameObject2-1-1 -27552
GameObject3 -27746
GameObject3-1 -27980
GameObject3-1-1 -28174

続いて、1度SceneをUnloadしてLoadし直す。実行結果はランダムと言っても過言ではない。

name InstanceID
GameObject1 13096
GameObject1-1 13160
GameObject1-1-1 13274
GameObject2 13222
GameObject2-1 13308
GameObject2-1-1 13124
GameObject3 13292
GameObject3-1 13216
GameObject3-1-1 13200
  1. GameObject1.Awake
  2. GameObject2-1-1.Awake
  3. GameObject1-1.Awake
  4. GameObject3-1-1.Awake
  5. GameObject3-1.Awake
  6. GameObject2.Awake
  7. GameObject1-1-1.Awake
  8. GameObject3.Awake
  9. GameObject2-1.Awake

まとめ

InstanceIDはGameObjectの一意な識別子として扱われることがあるIDだが、例に上げたように実行時に変更されることはないが、SceneのLoadやUnityの再起動で変わる可能性がある。また、GameObjectとして書かれてることが多いが、GetInstanceIDメソッドは実際はComponentクラスが持っており、GameObjectに対して呼び出した場合はTransformのIDが返される。
Componentの実行順は予測できないので保証されない(常に変わる可能性がある)として理解しておく必要がある。 それでも制御したい場合は、「Script Execution Order」というものもある。1

参考