几种场景下 Awake 、OnEnable 和 Start 的调用顺序

今天我们来讨论几个Unity3D的生命周期事件的执行顺序的问题。首先,我们来看一下我们的测试代码 Test.cs

public class Test : MonoBehaviour 
{
    void Awake()
    {
        Debug.LogFormat("in awake, frame = {0}", Time.frameCount);
    }

    void OnEnable()
    {
        Debug.LogFormat("in OnEnable, frame = {0}", Time.frameCount);
    }

    // Use this for initialization
    void Start () 
    {
        Debug.LogFormat("in Start, frame = {0}", Time.frameCount);
    }
}

测试代码非常简单,主要关心 Awake、OnEnable 和 Start 函数的什么时候被调用以及调用时的帧数。
问题如下,你可以看看自己是否能够回答正确呢?

  1. 在一个 GameObject 上面挂上 Test.cs ,然后直接运行该场景,请写出控制台输出的Log信息。场景结构如下所示,注意 GameObject 默认是 Active 状态的。

  2. 在一个 GameObject 上面挂上 Test.cs ,然后直接运行该场景,请写出控制台输出的Log信息。场景结构如下所示,注意 GameObject 默认是 Deactive 状态的。当场景运行运行后,再手动把 GameObject 设置为 Active 状态,请写出控制台输出的 Log 信息。

  3. 在一个 GameObject 上面挂上 Test.cs ,然后直接运行该场景,请写出控制台输出的Log信息。场景结构如下所示,注意 GameObject 默认是 Active 状态的,但是 Test.cs 这个组件是 Disable 状态的。当场景运行后,再手动把 Test.cs 这个组件设置为 Enable 状态,请写出控制台输出的 Log 信息。

上面三道题非常细致的测试了 Awake 、OnEnable 和 Start 三个事件回调的先后顺序及之间的关系。下面我们来看一下测试结果。

  1. Awake 首先被调用,然后再调用 OnEnable ,这是在第0帧被调用的。然后在下一帧,也就是第1帧,Start 函数被调用。所以从总体顺序上依然是我们熟悉的 Awake -> OnEnable -> Start 这个固定的顺序,但是需要注意的是 Start 比 Awake 和 OnEnable 晚一帧被调用。

  2. 由于 GameObject 是 Deactive 的,所以不会输出任何 Log 信息,当手动把 GameObject 设为 Active 后,输出结果类似与第1种情况,只是 FrameCount 为当时的FrameCount。

  3. 由于 GameObject 是 Active 的,所以即使 Test.cs 是 Disable 的,引擎也会把这个 GameObject 下面所挂载的所有的组件都加载进来,所以 Awake 函数会被调用。但是由于组件是 Disable 的,所以不会触发 OnEnable ,自然也不会触发 Start 方法。但是当手动把组件设为 Enable 状态时,会依次触发 OnEnable 和 Start 函数,OnEnable 和 Start 仍然相隔一帧。

怎么样,你有没有理解对呢?我之前一直以为 OnEnable 和 Start 是在同一帧里面被调用的,但实际上 Start 要比 OnEnable 晚一帧。
最后,附上 Unity3D 的官方文档供查阅 Execution Order of Event Functions