MonoBehaviourを継承したクラスのメンバ変数について考える
前提
UnityのGameObjectにコンポーネントをアタッチするには、コンポーネントクラスにMonoBehaviour
クラスを継承させておく必要があります。
そして、MonoBehaviour
クラスを継承したクラスは、メンバ変数をシリアライズ可能にすることでUnityのInspectorに表示させることができるようになります。
問題
MonoBehaviur
を継承したクラスのメンバ変数について、複数のインスタンスが作られる場合がある。
検証
パターン
本項では、検証をおこなった3つのパターンを示します。
各パターンが記述されたコンポーネントを用意し、適当なGameObjectにアタッチ後、アプリケーションを実行させることで検証しました。
コードを読めばやってることは明確なので、プログラムの説明は省きます。
なお、本記事の検証ではUnity 2020.1.13f1を使用しました。
パターン1
using UnityEngine;
using System;
public class NewBehaviourScript : MonoBehaviour
{
private class Test
{
public Guid Guid = Guid.Empty;
public Test()
{
this.Guid = Guid.NewGuid();
Debug.Log("C : " + this.Guid);
}
}
private Test test = new Test();
private void Awake()
{
Debug.Log("S : " + this.test.Guid);
}
}
パターン2
using UnityEngine;
using System;
public class NewBehaviourScript : MonoBehaviour
{
// パターン1と中身は同じ
private class Test {}
private Test test = null;
private void Awake()
{
this.test = new Test();
Debug.Log("S : " + this.test.Guid);
}
}
パターン3
using UnityEngine;
using System;
public class NewBehaviourScript : MonoBehaviour
{
// パターン1と中身は同じ
[Serializable]
private class Test {}
[SerializeField]
private Test test = new Test();
private void Awake()
{
Debug.Log("S : " + this.test.Guid);
}
}
結果
本項では、Unity Editor環境とexe実行時環境の2つについて、検証結果を示します。
Editor環境
Unity Editor上で実行させた結果を示します。
パターン1
// C : d8a171b6-9c7c-41f4-b40d-108df71a527b
// C : 4d1d9aa8-b9a0-45aa-9eae-e3615e70fc92
// S : 4d1d9aa8-b9a0-45aa-9eae-e3615e70fc92
パターン2
// C : ae3439da-e01c-47f8-9726-8b14c8621022
// S : ae3439da-e01c-47f8-9726-8b14c8621022
パターン3
// C : adcc2211-6103-4ec9-8c17-2c6c3c851b7f
// C : 27e791fd-d83e-4692-a6e7-b5e8c6c5fff3
// C : 0762d7fc-c0b0-478a-91b4-fed8338c2ec2
// S : 0762d7fc-c0b0-478a-91b4-fed8338c2ec2
exe実行時環境
Windows10を対象にビルドして用意したexeで実行させた結果を示します。
パターン1
// <i>WindowsPlayer</i> C : 1892e6a7-013d-4eee-a99e-9920531ab5d0
// <i>WindowsPlayer</i> S : 1892e6a7-013d-4eee-a99e-9920531ab5d0
パターン2
// <i>WindowsPlayer</i> C : 1a5e1afc-1e18-4ec7-972c-f84271bad0f0
// <i>WindowsPlayer</i> S : 1a5e1afc-1e18-4ec7-972c-f84271bad0f0
パターン3
// <i>WindowsPlayer</i> C : 906777c3-9e2e-4ed8-a9f5-e66f9b9fcefb
// <i>WindowsPlayer</i> S : 906777c3-9e2e-4ed8-a9f5-e66f9b9fcefb
考察
検証結果より、Editor上ではMonoBehaviour
クラスを継承したクラスのメンバ変数のインスタンスが、複数生成される場合があることが確認できました。
メンバ変数に[SerializeField]
が付与されている時にインスタンスが1つ余計に生成されるのは理解できますが、宣言時初期化をおこなっているメンバ変数は全てインスタンスが2つ生成されることになります。
おそらくコンポーネントとしてUnityのシステムであるGameObjectにアタッチするためだと考えることはできますが、非常に弱い考察です。
他方、ビルドしたexeを実行した環境では複数のインスタンが生成されるのは確認できませんでした。
これは、ビルド後はUnity Editorシステムからは独立したアプリケーションになり、Editorシステムが無くなるためだと考えることができます(よかった…)。
結論
MonoBehaviour
クラスを継承したコンポーネントのメンバ変数について、そのメンバ変数のクラスにコンストラクタを持たせた場合、Editor上では複数回呼ばれる可能性があることを考慮する必要があります。
また、exe実行時には問題は確認できませんでしたが、そもそもの原因が不明ではあるため絶対ではありません。
もしexe実行時にも起き得るのだとしたら、どうしたら良いんだろう。
Discussion