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

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

Unity Tips(8:MonoBehavior派生クラスのコンストラクターが呼びだされるタイミングについて)

 MonoBehavior派生クラスに設定されたpublicフィールドは、Inspector上から値を設定できます(SerializeField属性を設定すれば非publicフィールドでも編集できる)。設定された値は、ゲーム実行時にスクリプト側から取得できます。サンプルなどで非常に多用されるスタイルで、Unityの大きな特徴の一つと言えます。

 このフィールドにはGameObject型も受け付けてくれるので、prefabを使って任意のGameObjectをセットすることができます。セットしたGameObjectは既に既に生成してある物としてスクリプトからアクセスできます。しかし、このGameObjectは、いつ生成され、元のクラスのフィールドに設定されているのでしょうか?

 これについての明確な説明は見つけられませんでした(誰か知ってたら教えてもらえると嬉しいです)。

 ただ、それとは別に、MonoBehavior派生クラスのコンストラクターが呼びだされるタイミングが、なんとUnityEditorにハンドリングされている(!)ということに気づきました。公式訳が微妙なので訳しておきます。

元:

https://docs.unity3d.com/ja/540/Manual/CreatingAndUsingScripts.html
プログラミング上級者への注意:オブジェクトの初期化がコンストラクター関数でないことが意外かもしれません。この理由はオブジェクトの作成はエディターによりハンドリングされて意外にもゲーム開始直後に行なわれないためです。スクリプトコンポーネントでコンストラクターを定義しようとすると、Unity の通常処理と干渉するため、プロジェクトで問題を発生させます。

私訳:

オブジェクトの初期化にコンストラクター関数を使用しないことに驚いているかもしれません。これは、オブジェクトの生成処理が(Unity)Editorによって管理されていて、通常期待されるゲームプレイ開始直後に行われないためです。スクリプトコンポーネントのコンストラクターを定義した場合、Unityの通常処理と干渉し、プロジェクトに問題が発生します。

原文:

https://docs.unity3d.com/Manual/CreatingAndUsingScripts.html
Note to experienced programmers: you may be surprised that initialization of an object is not done using a constructor function. This is because the construction of objects is handled by the editor and does not take place at the start of gameplay as you might expect. If you attempt to define a constructor for a script component, it will interfere with the normal operation of Unity and can cause major problems with the project.

補足(Unity4.xのマニュアルから)

また、「スクリプトの編集中にもコンストラクタが呼ばれる(!)」という記述も見つけたので、こちらも私訳を置いておきます。ただし、こっちはUnity4系のマニュアルの記述で、最新版にはないので、現在は仕様が変わっている可能性があります。

(有志による日本語訳)

http://www.cis.sojo-u.ac.jp/~izumi/Unity_Documentation_jp/Documentation/ScriptReference/index.Writing_Scripts_in_Csharp.html
7. コンストラクタは使用しないでください(Avoid using the constructor)
値をコンストラクタで初期化しないでください。AwakeかStartを代わりに使用してください。 UnityはEditモードに入ると自動的にコンストラクタを呼び出します。これは、スクリプトのコンパイル直後に発生します。コンストラクタはスクリプ トのデフォルト値を設定する必要があるからです。コンストラクタは、気がつかないうちに呼ばれるばかりでなく、プレハブや無効なゲームオブジェクトでも呼 ばれる場合があるでしょう。

私訳:

MonoBehaviourスクリプトに含まれるいかなる値も、コンスタラクターや変数初期化子で初期化しないでください。初期化するにはAwakeかStartを代わりに使ってください。UnityはEditモードの最中でも、コンストラクターを自動的に実行します。通常は、そのコンストラクターの変数のデフォルト値を検索するために、スクリプトを編集した直後に実行されます。コンストラクターは気づかないうちに呼ばれるばかりでなく、prefabやアクティブでないGameObjectからも呼ばれることがあります。

原文(ただし、上記有志による日本語訳が参照した物とはバージョンが異なる):

Overview: Writing Scripts in C# & Boo
https://docs.unity3d.com/401/Documentation/ScriptReference/index.Writing_Scripts_in_Csharp_26_Boo.html
Never initialize any values in the constructor or variable initializers in a MonoBehaviour script. Instead use Awake or Start for this purpose. Unity automatically invokes the constructor even when in edit mode. This usually happens directly after compilation of a script because the constructor needs to be invoked in order to retrieve default variable values. Not only will the constructor be called at unforeseen times, it might also be called for prefabs or inactive game objects.


GameObjectのコンストラクタを実装者が使用できないのは、この辺りの事情が複雑に絡み合っているからっぽいですね。