事件函数执行顺序

在 Unity 脚本中,有大量的事件函数以特定的顺序被执行。执行顺序描述如下:

编辑器

  • Reset

    当第一次把脚本绑定到对象上,或者使用了 Reset 命令时,该事件被触发,用以初始化脚本的属性。

加载第一个场景

当某个场景开始时,下面的事件被触发(场景中的每个对象都会执行一次):

  • Awake

    该函数总是在所有 Start 函数之前被调用,并且只会在某个 prefab 被实例化之后才会被调用。(如果一个 GameObject 在启动时是非激活状态,那么 Awake 函数不会被调用,直到这个 GameObject 处于激活状态。)

  • OnEnable

    (只有对象处于激活状态才会被调用)该函数在对象处于可用状态之后被调用。当一个 MonoBehaviour 实例被创建时,就会调用该函数。例如关卡加载完成、某个带有脚本组件的 GameObject 被实例化后。

  • OnLevelWasLoaded

    该函数在某个新关卡加载完成后被执行,用来向游戏通知新关卡已经被载入。

请注意,对于场景 scene 中已有对象上附加的脚本组件,函数 Awake 和 OnEnable 将在所有 Start、Update 等函数之前被调用。当然,如果某个对象是游戏运行时实例化的,那么不遵循这条原则。

第一帧更新之前

  • Start

    该函数在第一帧更新之前被调用,前提是该脚本实例必须是可用的。

对于场景 scene 中已有对象上附加的脚本组件,函数 Start 在 Update 等函数之前被调用。当然,如果某个对象是游戏运行时实例化的,那么不遵循这条原则。

帧间

  • OnApplicationPause

    如果游戏处于暂停状态,该函数在某一个帧的末尾被调用,也就是说,是在正常帧(更新)之间被调用。当 OnApplicationPause 被调用后,一个特殊的帧被创建,这样游戏可以显示暂停状态的图形。

帧更新顺序

当你跟踪游戏的逻辑、交互、动画、摄像机位置等时,也有几个事件可供使用。通常我们是在 Update 函数中执行大部分任务,但也可以使用其他的函数。

  • FixedUpdate

    通常,FixedUpdate 比 Update 调用的更频繁。如果帧率很低,可以在每一帧上多次调用 FixedUpdate;如果帧率很高,FixedUpdate 更本不会被调用。调用 FixedUpdate 之后,所有的物理计划和更新会立即生效,如果在 FixedUpdate 中执行位移计算,你就不需要基于 Time.deltaTime 来计算。因为 FixedUpdate 基于一个可靠的计时器,与帧率无关。

  • Update

    每帧调用一次 Update。对于帧更新来说,Update 是主要的任务承载函数。

  • LateUpdate

    LateUpdate 在 Update 完成之后被调用,每帧调用一次。当 LateUpdate 开始执行时,Update 中执行的所以计算都已完成。如果你在 Update 中移动和旋转角色,那么你可以在 LateUpdate 中移动和旋转摄像机。这样,在摄像机跟随角色的位置之前,可以确保角色的移动已经完成。

渲染

  • OnPreCull

    在摄像机对场景进行 Culling 之前被调用。Culling 确定了哪些对象对于摄像机是可见的。

    译注 Unity中的优化技术

  • OnBecameVisible/OnBecameInvisible

    当某个对象对于任意摄像机变为可见或不可见时被调用。

  • OnWillRenderObject

    为每个摄像机调用一次,如果该对象是可见的。

  • OnPreRender

    在摄像机开始渲染场景之前被调用。

  • OnRenderObject

    在常规场景渲染完成之后被调用。此时,你可以使用类 GLGraphics.DrawMeshNow 绘制自定义的几何体。

  • OnPostRender

    当某个摄像机完成渲染场景之后被调用。

  • OnRenderImage

    在场景渲染完成之后被调用,用于图像后处理,请查看 ImageEffects

  • OnGUI

    用于响应 GUI 事件,每一帧会多次调用。首先处理 Layout 和 Repaint 事件,然后是 Layout,以及每次用户输入触发的 keyboard/mouse 事件。

  • OnDrawGizmos

    用于在场景视图中绘制 Gizmos,使之可视化。

协同程序

通常,协同更新在函数 Update 返回后运行。一个协同程序是一个可以暂停运行过程的函数,当给定的 YieldInstruction 完成时继续执行。协同程序的不同用法如下:

  • yield

    下一帧中的所有 Update 函数被调用后,协同程序将继续执行。

  • yield WaitForSeconds

    当前帧的所有 Update 函数被调用后,并且延迟给定的时间,协同程序将继续执行。

  • yield WaitForFixedUpdate

    所有脚本的 FixedUpdate 函数被调用后,协同程序将继续执行。

  • yield WWW

    网络下载完成后,协同程序将继续执行。

  • yield StartCoroutine

    将协同程序串联起来,等待 MyFunc 执行完成后,继续链式执行。

当对象被销毁时

  • OnDestroy

    在对象存在的最后一帧,当所有帧更新都完成后,该函数被调用(该对象可能因为调用了 Object.Destroy 而被销毁,也可能随着场景的关闭而销毁)。

当退出时

这些函数会在场景中的所有激活对象上调用。

  • OnApplicationQuit

    在应用程序退出前,在所有游戏对象调用该方法。如果是在编辑器中,那么当用户停止游戏模式时,该函数被调用。

  • OnDisable

    当游戏对象的行为变为禁用或不活动时,该函数被调用。

脚本生命周期流程图

下图总结了脚本生命周期中事件函数的执行顺序和重复周期。

事件函数执行顺序 - 图1