MonoBehaviourのAwake、Start、Updateという関数が呼ばれる仕組み

MonoBehaviourを継承してAwake、Start、Update等という関数を作るとUnity側で勝手に呼んでくれますが、これがいったいどういう仕組で動いているのかが謎でした。

最初、MonoBehaviour側にvirtualな関数、もしくはabstractな関数で定義されているのかとも思ったのですが、overrideしなくても書けますし、定義を書かなくてもコンパイルエラーにもなりません。

C#にはReflectionという機能があり、これを使うと実行時にクラス名やメンバ名などが取得できます。これを使っているのではという説もあったのですが、どうもそうではないようです。

答えは、ここにありました。

blogs.unity3d.com

一部引用すると、

任意の型のMonoBehaviourが初めてその基底のスクリプトにアクセスしたときに、スクリプティングランタイム(MonoもしくはIL2CPP)によって何かのマジックメソッドが定義されているかを調査され、この情報がキャッシュされます。もしMonoBehaviourが特定のメソッドを持っていたら所定のリストに追加されます。例えばスクリプトがUpdateメソッドを持っていたら「毎フレームUpdateを呼ぶべきスクリプトのリスト」に追加されるわけです。


ゲーム中は、Unityは短にこのリストをイテレーションしてメソッドを呼んでいきます – シンプルです。また、これがUpdateメソッドのアクセス権がpublicであろうとprivateであろうと関係ない理由でもあります。

ということです。言い方は悪いですが、Unityのコンパイラが言語規約に無いことを勝手にゴニョゴニョやっているということみたいです。

これは無駄な仮想関数呼び出しを避けるためのようです。

例えば、もしMonoBehaviour の中身が

public abstract class MonoBehaviour {
    protected virtual void Update() {}
}

というものであった場合、Updateをoverrideしていないクラスに対しても空のUpdateを呼び出すことになってしまい、結構な処理時間を食います。

これを避けるための仕組みなわけですね。ゲーム作っている人としては非常にありがたい仕組みです。

しかし、Unityが.Net Foundationに参加したことで、この辺りももしかしたら変わってくるかもしれませんね。