Unity在插入新组件时的生命周期执行顺序

众所周知,在Unity中挂载了MonoBehaviour脚本的物体将遵循Unity的生命周期执行Tick,常用的api和顺序如下:

1
2
3
4
Awake();
Start();
Update();
LateUpdate();

但是,如果在物体A的任意一个生命周期阶段中通过AddComponent方法添加了另一个组件B,组件B和组件A的执行顺序是怎么样的呢?

写一个简单的测试脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using UnityEngine;

public class TestComponent : MonoBehaviour {
private bool hasAwake = false;
private bool hasStarted = false;
private bool hasUpdated = false;
private bool hasLateUpdated = false;

private void Awake() {
if (!hasAwake) {
Debug.Log($"{Time.frameCount} Test Comp Awake");
}
hasAwake = true;
}

void Start() {
if (!hasStarted) {
Debug.Log($"{Time.frameCount} Test Comp Start");
}
hasStarted = true;
}

void Update() {
if (!hasUpdated) {
Debug.Log($"{Time.frameCount} Test Comp Update");
}
hasUpdated = true;
}

private void LateUpdate() {
if (!hasLateUpdated) {
Debug.Log($"{Time.frameCount} Test Comp LateUpdate");
}
hasLateUpdated = true;
}
}

同时创建一个逻辑完全相同的TestComponet2组件,并在TestComponent中各个生命周期阶段分别添加gameObject.AddComponent<TestComponent2>();

Awake中添加:

1
2
3
4
5
6
7
8
0  Test Comp Awake
0 Test Comp2 Awake
1 Test Comp Start
1 Test Comp2 Start
1 Test Comp Update
1 Test Comp2 Update
1 Test Comp LateUpdate
1 Test Comp2 LateUpdate

可以看到,两组件完全同步。


Start中添加:

1
2
3
4
5
6
7
8
0  Test Comp Awake
1 Test Comp Start
1 Test Comp2 Awake
1 Test Comp2 Start
1 Test Comp Update
1 Test Comp2 Update
1 Test Comp LateUpdate
1 Test Comp2 LateUpdate

可以看到,因为Comp2是在Start中添加上去的,所以 Comp2Awake也推迟了一帧,但后续Tick依然同步。


Update中添加:

1
2
3
4
5
6
7
8
0  Test Comp Awake
1 Test Comp Start
1 Test Comp Update
1 Test Comp2 Awake
1 Test Comp2 Start
1 Test Comp LateUpdate
1 Test Comp2 LateUpdate
2 Test Comp2 Update

发现Comp2生命周期中的Update被推迟到了下一帧!但是其LateUpdate依然同步。


LateUpdate中添加:

1
2
3
4
5
6
7
8
0  Test Comp Awake
1 Test Comp Start
1 Test Comp Update
1 Test Comp LateUpdate
1 Test Comp2 Awake
1 Test Comp2 Start
2 Test Comp2 Update
2 Test Comp2 LateUpdate

可以看到,Comp2在最后一个阶段(此处举例中四个的最后一个)才被添加,所以它的UpdateLateUpdate都被推迟到了下一帧。


上述四种情况中,除了Update的情况都相对符合直觉。为什么唯独它这么奇怪?

猜想:在引擎每一次执行Update()之前,整个世界需要Update()的组件数量就会确定,并在执行过程中不会改变这个集合。所以猜测LateUpdate()FixedUpdated()等方法的结果应该也是相同的。

试了一下,确实是这样:

1
2
3
4
5
6
7
8
9
10
0  Test Comp Awake
1 Test Comp Start
1 Test Comp FixedUpdate
1 Test Comp2 Awake
1 Test Comp2 Start
1 Test Comp Update
1 Test Comp2 Update
1 Test Comp LateUpdate
1 Test Comp2 LateUpdate
2 Test Comp2 FixedUpdate