テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】コンポーネントのイベント実行順についてのTips

GameObjectにアタッチしたスクリプトの実行順番は、特に何もしなければ割とランダムです。今回はその辺りについて少し整理します。

スクリプトの実行順番

UnityはコンポーネントをGameObjectに設定すればコンポーネントに記述したイベントを呼び出してくれます。自動的に呼ばれる代表格はAwakeやOnEnable、StartやUpdate等です。

f:id:tsubaki_t1:20170204224959j:plain

若干面倒くさい事に、このコンポーネントの処理実行順は特に設定しなければ不透明…というか、どの順番で呼ばれるかは分かりません。
たとえばマネージャークラスのような物よりも先に末端のコンポーネントが先に呼ばれるといったケースも有りえます。

スクリプトの実行順を制御する

一応、スクリプトの実行順番は、Script Execution Orderのキーワードで設定する事が出来ます。

Script Execution OrderをGUIで設定

一番GUI的なのはScript Execution OrderのUIで書き換える事です。

  1. Edit > Project Settings > Script Execution Orderを開く
  2. +を押して登録したいスクリプトを選択
  3. スクリプトの実行順を設定

実行順は、上が優先度が高く下が低いです。
正確には、数値が低い方が優先されます。標準のコンポーネントは実行順は0です。

f:id:tsubaki_t1:20170204225757j:plain

f:id:tsubaki_t1:20170204225802j:plain

なお、一覧表示中にコンポーネント頭文字のキーを押すと、該当のスクリプトまで選択がジャンプします。一つしか無い場合はその瞬間に選択が確定します。

f:id:tsubaki_t1:20170204230454g:plain

メタデータに直接書き込む

スクリプトの量が多くなってくると、Script Execution Orderでは設定が面倒くさくなってきます。そんな時の力技は.metaを直接書き換えてしまうアプローチがあります。

Script Execution Orderの情報は各スクリプトメタデータに有ります。

metaを開いて、  executionOrderの指定する数値を書き換えます。無ければ追加します。

スクリプト単位で書き込めるので、手軽といえば手軽に設定できます。

f:id:tsubaki_t1:20170204231106j:plain

f:id:tsubaki_t1:20170204231147j:plain

DefaultExecutionOrderを使用

一方、スクリプト側から設定する方法も(多分)5.5辺りから追加されてるっぽいです。*1

コンポーネントのAttributeとして設定すると、指定のコンポーネントの実行順を制御します。またScript Execution Orderと同様、数値が低い方が優先され、標準コンポーネントの実行順は0です。

なおScript Execution Orderが優先されます。

f:id:tsubaki_t1:20170204231704j:plain

イベントの実行順

イベントの実行順ですが、Unity - マニュアル: イベント関数の実行順に絵付きで分かりやすく書いてあります。

要するに、

  1. Awake
  2. OnEnable
  3. Start
  4. Update

の順で呼ばれます。但し、StartとUpdateはインスタンス化直後ではなく、スタートのタイミングで呼ばれます。

 

  • Awake
    (シーンに配置orAddComponent直後
     GameObjectがActive時)
  • OnEnable
    (シーンに配置orAddComponent直後
     ComponentがEnable時)
  • Start
    (他のStartと同時)
  • Update
    (他のUpdateと同時)

つまりコンポーネントやオブジェクトを生成したタイミングで即座にAwake→OnEnableが呼ばれ、他の全てのAwakeとOnEnableが呼ばれた後にStartが呼ばれ始めます。

OnEnableとAwakeの違い

一見一気に呼ばれるAwakeとOnEnableですが、よく見ると若干挙動が異なります。

Awake: この関数は常に Start 関数の前およびプレハブのインスタンス化直後に呼び出されます。(もしゲームオブジェクトがスタートアップ時に無効である場合、有効化にされるまで Awake は呼び出されません。)
OnEnable: (オブジェクトがアクティブな場合にのみ呼び出されます): この関数は、オブジェクトを有効にした直後に呼び出されます。例えば、MonoBehaviour インスタンスが作成されたとき、レベルロードあるいはスクリプトコンポーネントのアタッチされたゲームオブジェクトがインスタンス化されたときに実行します。

要するに、

Awake:

OnEnable:

つまりGameObjectのインスタンス化後、コンポーネントがdisableでもAwakeは呼ばれ、enable時にOnEnableが呼ばれるという事です。

f:id:tsubaki_t1:20170205220200g:plain

これを上手く使えば、階層の有るPrefabのような場合、GameObjectのアクティブ/非アクティブやコンポーネントのenable/disableを上手く使うと、先に全てのAwakeを実行してからOnEnableを実行するといった事も出来なくは無いかもしれません。

ScriptableObjectのOnEnableは、大抵Awakeより先に呼ばれる

ScriptableObjectをシリアライズしている場合、参照先のScriptableObjectのOnEnableが呼ばれるタイミングは、他のAwakeよりも早いです。

  • シーンに参照があれば、まず呼ばれる
  • シーンが参照するPrefabから参照されていたら呼ばれる
  • Instantiateで生成する前に参照を辿って呼ばれる
  • PreloadAssetsに登録されていればシーン読み込む前に呼ばれる

ただし、エディターで操作中に意図せずOnEnableが呼ばれる事もあります。

tsubakit1.hateblo.jp

 Execution Orderが効かないケース

残念ながら、Execution Orderが効かないケースもあります。

と言うより、Execution Orderはどうも「生成時にコンポーネント呼出順を並べる機能」っぽく、逐次生成すると効果を発揮しないみたいです。
なので、下のような現象が発生します。

コンポーネントやオブジェクトを動的に追加した場合は、生成順になる

動的にコンポーネントを生成した場合、コンポーネントの実行順は(StartやUpdate等も)オブジェクトの生成順になります。

例えば下のようなコードが合った場合、Execution Orderを設定してもStartやUpdateの呼出順はScript1が先、Script2が後になります。

f:id:tsubaki_t1:20170204234348j:plain

同様に、これがGameObjectの生成であった場合も、先に生成されたGameObjectのStart、Updateが優先されます。

Script2がScript1より優先的に実行される状況を作っても、Script1がアタッチされているオブジェクトを先に生成すれば、StartはScript1が先に呼ばれます。

f:id:tsubaki_t1:20170204234944j:plain

f:id:tsubaki_t1:20170204235139j:plain

しかし”同時に”生成した場合は、Script Execution Order準拠

この生成した場合のルールですが、複数のコンポーネントがアタッチしているやPrefabの子オブジェクトにアタッチされているといった状況で、一気に複数のオブジェクトを生成出来る場合、Execution Orderが適応されます。

f:id:tsubaki_t1:20170204235802j:plain

f:id:tsubaki_t1:20170204235811j:plain

f:id:tsubaki_t1:20170205000016j:plain

関連

旧記事

tsubakit1.hateblo.jp

docs.unity3d.com

tsubakit1.hateblo.jp

*1:何故多分かと言えば、マニュアルに書いて無いからです。自分も5.6の新しいNavmesh機能のサンプル(ランタイムにNavmeshを構築するやつ)に使われてた時に気づきました。