土屋つかさの技術ブログは今か無しか

土屋つかさが主にプログラミングについて語るブログです。

#unity 小ネタ:Awake()とStart()の実行順序の話(か?)

 年に1回くらい「今なら僕の考えた最強のゲーム用ステートマシンを設計出来る気がする!」と考えるタイミングがありまして、今年は今がその状況です。今回書いたステートマシンはこちら。詳細についてはまた改めて記事を作ります。

ステートマシン本体
StateMachine.cs · GitHub
動作サンプル(2021/04/15追記。ステートマシン実装が更新されたのでこのサンプルは多分動きません)
Program.cs · GitHub

 今はこのステートマシンの実用性検証目的で、Unity用のテキストウィンドウフレームワークを実装しています。久し振りにMonoBehaviour継承クラスを書く際、Unityの作法を忘れていて、AddCompornent()でコンポーネントを生成した直後にメンバ変数にアクセスする時、Start()メソッド内で初期化した筈なのにnullになってて「なんで?」とハマりました。
 これは、コンポーネント生成直後に実行されるのはAwake()メソッドで、Start()メソッドは最初にUpdate()メソッドが呼ばれる前に実行される物だからです(なんか上手い事説明できないな)。

 公式ドキュメントでこの辺りが詳しく説明されています。日本語版の当該箇所が、間違ってはいないんですが、下記の「※」の箇所が一読で意図を読み取れなかったので私訳を置いておきます。
docs.unity3d.com
docs.unity3d.com

原文:Before the first frame update
公式訳:最初のフレームを更新する前
私訳:「初回のフレーム更新」の前(のフレーム)

原文:Start: Start is called before the first frame update only if the script instance is enabled.
公式訳:Start: スクリプトのインスタンスが有効な場合にのみ、最初のフレームのアップデート前に Start が呼び出されます。
私訳:Start:スクリプトインスタンスが有効な場合のみ、「初回のフレーム更新」の前(のフレーム)に、Start関数が呼びだされます。

原文:For objects that are part of a scene asset, the Start function is called on all scripts before Update, etc is called for any of them.
公式訳:シーンに追加されたオブジェクトの Start 関数はすべてのスクリプトで Update や他の関数が呼び出される前に呼び出されます。
私訳:シーンアセットを構成するオブジェクト群の場合、Update関数他が呼びだされるより前に、全てのスクリプトのStart関数が呼びだされます。


原文:Naturally, this cannot be enforced when you instantiate an object during gameplay.
公式訳:当然のことながらオブジェクトがゲームプレイ中にインスタンス化されたときに強制的に呼び出すことはできません。
私訳:当然ながら、gameplay中に生成されたオブジェクトには、これ(※)を強制できません。

※「これ」は「全てのUpdate関数が呼びだされる前にStart関数を実行すること」を指します。オブジェクトを生成した時点でUpdate関数実行タイミングに入っていることを禁止することなど出来ないわけで、そりゃまあ当然ですね。

2021/02/21追記”Before the first frame update”の訳について

 自分で確認した限り、Awake()とStart()は「同じフレーム中」に実行されます。そして、Update()が実行されるのは「その次のフレーム」からになります。
 記事を書いた当初これをよくわかっておらず直訳していたのですが、明確にするために「初回のフレーム更新」の前(のフレーム)に変更しました。

2021/02/21追記

 Awake()とStart()の使い分けですが、AddCompornentでMonoBehaivior継承クラスをインスタンス化した場合、その時点でAwake()が実行済みになる(Startはここでは呼ばれない。多分)ので、コンストラクタとして使えるのはAwakeのみになると思われます。←というのが現時点での認識なのだけど、Start()の実行タイミングも勘違いしていたので、これも今後の検証で変わるかも

2021/02/21追記

 Awake()/Start()と直接関係ないんですけど、Edtior上で実行直後の第3フレームあたりで突然1フレーム中に0.3秒とかありえない時間が経過することがあって、それを想定していないコードが意図しない挙動をしたのに今日苦労させられたのでメモ。