UnityのMonoBehaviourのメッセージのいくつかをシーケンス図にしてみた
複数のGameObjectがあったときにどのような順番でAwake,OnEnable,Start,OnApplicationQuit,OnDisable,OnDestroyが呼ばれるかをシーケンス図にしてみた。
GameObjectが生成されるタイミングでAwake,OnEnableが呼ばれ、同じタイミングで生成されたGameObjectは同じタイミングでStartが呼ばれている。OnApplicationQuitも同じタイミングで呼ばれ、個々のオブジェクトに対しOnDisable,OnDestroyが呼ばれている。
Hierarchy上はMain,Sub1,Sub2と表示されているが、Mainが最後に呼ばれている。はじめの頃は最初に呼ばれていたと思うが、色々編集しているうちにSub1,Sub2より後に呼ばれるようになった。クラス名を変えたのでそのタイミングで後に追加されたのかもしれない。Hierarchy順に呼ばれることを期待してはいけないようだ。
登場するGameObject(スクリプト)
- シーンの最初からあるGameObject: Main(Main), Sub1(Sub1), Sub2(Sub2)
- Main.UpdateでCreatePrimitive: Cube0(Derived), Cube1(Derived), Cube2(Derived)
- Main.UpdateでPrefabをInstantiate: Clone0(PrefabScript),Clone1(PrefabScript), Clone2(PrefabScript)
シーケンス図
シーケンス図はhttps://www.websequencediagrams.com/で作成した。
次のようにテキストを入力すると上のシーケンス図が生成される。便利だ。上の図をクリックすると上のサイトで大きくみれる。
title MonoBehaviour Message
Core->Sub2: Awake()
Core->Sub2: OnEnable()
Core->Sub1: Awake()
Core->Sub1: OnEnable()
Core->Main: Awake()
Core->Main: OnEnable()
Core->Sub2: Start()
Core->Sub1: Start()
Core->Main: Start()
Main->GameObject: CreatePrimitive()
GameObject->Cube0: <<create>>
Main->Cube0: AddComponent()
Core->Cube0: Awake()
Core->Cube0: OnEnable()
Main->Main: Instantiate()
Main->Clone0: <<create>>
Core->Clone0: Awake()
Core->Clone0: OnEnable()
Main->GameObject: CreatePrimitive()
GameObject->Cube1: <<create>>
Main->Cube1: AddComponent()
Core->Cube1: Awake()
Core->Cube1: OnEnable()
Main->Main: Instantiate()
Main->Clone1: <<create>>
Core->Clone1: Awake()
Core->Clone1: OnEnable()
Main->GameObject: CreatePrimitive()
GameObject->Cube2: <<create>>
Main->Cube2: AddComponent()
Core->Cube2: Awake()
Core->Cube2: OnEnable()
Main->Main: Instantiate()
Main->Clone2: <<create>>
Core->Clone2: Awake()
Core->Clone2: OnEnable()
Core->Cube0: Start()
Core->Clone0: Start()
Core->Cube1: Start()
Core->Clone1: Start()
Core->Cube2: Start()
Core->Clone2: Start()
Core->Cube1: OnApplicationQuit()
Core->Sub1: OnApplicationQuit()
Core->Sub2: OnApplicationQuit()
Core->Clone0: OnApplicationQuit()
Core->Main: OnApplicationQuit()
Core->Clone2: OnApplicationQuit()
Core->Cube2: OnApplicationQuit()
Core->Clone1: OnApplicationQuit()
Core->Cube0: OnApplicationQuit()
Core->Cube1: OnDisable()
Core->Cube1: OnDestroy()
Core->Sub1: OnDisable()
Core->Sub1: OnDestroy()
Core->Sub2: OnDisable()
Core->Sub2: OnDestroy()
Core->Clone0: OnDisable()
Core->Clone0: OnDestroy()
Core->Main: OnDisable()
Core->Main: OnDestroy()
Core->Clone2: OnDisable()
Core->Clone2: OnDestroy()
Core->Cube2: OnDisable()
Core->Cube2: OnDestroy()
Core->Clone1: OnDisable()
Core->Clone1: OnDestroy()
Core->Cube0: OnDisable()
Core->Cube0: OnDestroy()
クラス図
Visual Studioで作ったクラス図
使ったことない人は次のページが参考になる。
C#スクリプト
ほぼ各メソッドにDebug.Log()を入れてシーケンス図に入力しやすいテキストをコンソールに出力した。MonoBehaivourから派生したBaseクラスを作り、そこからMain,Sub1,Sub2,Derived,PrefabScriptを派生させて、Main.UpdateでCreatePrimitive(Cube)とInstantinate(Prefab)を行った。
Baseクラス
myNameメンバに名前を設定することで派生クラスのオブジェクトを区別できるようにしてコンソールへ出力している。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Base : MonoBehaviour
{
#region Initialization
public Base() { }
public Base(string name)
{
myName = name;
}
public void SetMyName(string name)
{
myName = name;
}
protected string myName = "Base";
void Awake()
{
Debug.Log("Core->" + myName + ": Awake()");
}
private void OnEnable()
{
Debug.Log("Core->" + myName + ": OnEnable()");
}
// Start is called before the first frame update
void Start()
{
Debug.Log("Core->" + myName + ": Start()");
}
#endregion
#region Physics
private void FixedUpdate()
{
//Debug.Log("Core->" + myName + ": FixedUpdate()");
}
#endregion
#region Game logic
// Update is called once per frame
public virtual void Update()
{
//Debug.Log("Core->" + myName + ": Update()");
}
void LastUpdate()
{
Debug.Log("Core->" + myName + ": LastUpdate()");
}
#endregion
#region Decommisioning
private void OnApplicationQuit()
{
Debug.Log("Core->" + myName + ": OnApplicationQuit()");
}
private void OnDisable()
{
Debug.Log("Core->" + myName + ": OnDisable()");
}
private void OnDestroy()
{
Debug.Log("Core->" + myName + ": OnDestroy()");
}
#endregion
}
Mainクラス
AddComponentでDerivedクラスインスタンスを生成するときに引数ありコンストラクタが使えなかったので、しかたなく生成後、SetMyNameで名前を設定している。AwakeとOnEnableは名前設定が間に合ってないのでBaseと出力されるので、シーケンス図作成用に手動で名前を変更した。Instantiateも同様。prefabメンバはInspectorで指定できるようにpublicにしている。UpdateはBaseでvirtual宣言して、Mainでoverrideしてみたがちゃんと呼ばれている。中身のコードは一度だけ実行するようにした。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Main : Base
{
Main() : base("Main")
{
}
bool first = true;
public GameObject prefab;
public override void Update()
{
if (first)
{
first = false;
for (int i = 0; i < 3; ++i)
{
Debug.Log(myName + "->GameObject: CreatePrimitive()");
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Debug.Log(myName + "->Cube" + i.ToString() + ": AddComponent()");
Derived derived = cube.AddComponent<Derived>();
derived.SetMyName("Cube" + i.ToString());
Debug.Log(myName + "->" + myName + ": Instantiate()");
GameObject clone = Instantiate(prefab);
PrefabScript script = clone.GetComponent<PrefabScript>();
script.SetMyName("Clone" + i.ToString());
}
}
}
}
Sub1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sub1 : Base
{
Sub1() : base("Sub1")
{
}
}
Sub2
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sub2 : Base
{
Sub2() : base("Sub2")
{
}
}
Derived
Main.Update()でインスタンスにCube0,Cube1,Cube2と名前を付けている。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Derived : Base
{
public Derived() : base() { }
}
PrefabScript
Main.Update()でインスタンスにClone0,Clone1,Clone2と名前を付けている。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PrefabScript : Base
{
}