# 事件函数的执行顺序

> Understand the execution order of Unity's built-in event functions so you can respond to events and update the state of your game in the right order.

事件函数是一组内置事件，MonoBehaviour 脚本可通过实施相应方法（通常称为回调）来订阅这些事件。回调对应于核心 Unity 子系统中的事件，例如物理、渲染和用户输入，或者对应于脚本自身生命周期的各个阶段，例如脚本的创建、激活、依赖于帧和独立于帧的更新和销毁。事件发生时，Unity 会在脚本上调用关联的回调，从而有机会实现逻辑以响应事件。

如果 Unity 引发这些事件并按预定顺序调用关联的 MonoBehaviour 回调，则该顺序会被记录在此处。了解执行顺序非常重要，这样您就不会尝试用一个回调来执行依赖于另一个尚未调用的回调的工作。但请注意，有些回调是针对事件，例如由用户输入触发的事件，这些事件可能在游戏运行时随时发生。应结合 [MonoBehaviour](/engine/6000.0/script-reference/unity-engine/mono-behaviour.md) 脚本参考（此处的事件回调列在 **Messages** 下）参阅此页面，以全面了解各个事件的含义和限制。

## 脚本生命周期概述

下图概括了 Unity 如何在脚本的生命周期内对事件函数进行排序以及重复执行这些事件函数。

有关各种事件函数的更多信息，请参阅以下部分：

* [一般原则](#GeneralPrinciples)
* [加载第一个场景](#FirstSceneLoad)
* [编辑器 (Editor)](#Editor)
* [在第一次帧更新之前](#BeforeTheFirstFrameUpdate)
* [帧之间](#InBetweenFrames)
* [更新顺序](#UpdateOrder)
* [动画更新循环](#AnimationUpdateLoop)
* [渲染](#Rendering)
* [协程](#coroutines)
* [销毁对象时](#WhenTheObjectIsDestroyed)
* [退出时](#WhenQuitting)

### 流程图的范围

以下流程图的范围仅限于内置事件函数，您可以执行 [MonoBehaviour](/engine/6000.0/script-reference/unity-engine/mono-behaviour.md) 脚本参考中的 **Messages** 下记录的相应回调在任何 MonoBehaviour 脚本，从而订阅这些函数。此外，还显示了引发事件的子系统的一些其他内部方法，以供参考。

除了这些内置事件函数之外，还可以在脚本中订阅许多其他事件。[Application](/engine/6000.0/script-reference/unity-engine/application.md)、[SceneManager](/engine/6000.0/script-reference/unity-engine-scene-management/scene-manager.md) 和 [Camera](/engine/6000.0/script-reference/unity-engine/camera.md) 等几个主要类提供了可以注册自己的回调方法的委托。[RuntimeInitializeOnLoadMethodAttribute](/engine/6000.0/script-reference/unity-engine/runtime-initialize-on-load-method-attribute.md) 等方法属性也可用于在场景的特定阶段执行方法。请参阅您感兴趣的组件或子系统的脚本参考，了解您可以订阅的事件回调及其执行顺序的详细信息。

### 脚本生命周期流程图


**Frame:**
![](/api/media?file=/engine/6000.0/media/images/monobehaviour_flowchart.svg)

**注意**：有些浏览器不支持 SVG 图像文件。如果以上图像未正确显示（例如，如果看不到任何文字），请尝试其他浏览器，例如 [Google Chrome](https://www.google.com/chrome/) 或 [Mozilla Firefox](https://www.mozilla.org/)。

## 一般原则

通常，不应依赖于为不同游戏对象调用相同事件函数的顺序，除非该顺序有明确记录或可设置。如果需要更精细地控制播放器循环，可以使用 [PlayerLoop API](/engine/6000.0/script-reference/unity-engine-low-level/player-loop.md)。

为同一 MonoBehaviour 子类的不同实例调用事件函数时，不能指定顺序。例如，一个 MonoBehaviour 的 `Update` 函数可能会在另一个游戏对象（包括其父级或子级游戏对象）上的相同 MonoBehaviour 的 `Update` 函数之前或之后调用。

可以指定一个 MonoBehaviour 子类的事件函数应在不同子类的事件函数之前调用（使用 [Project Settings](/engine/6000.0/manual/unity-editor/editor-features/comp-manager-group.md) 窗口的 [Script Execution Order](/engine/6000.0/manual/unity-editor/editor-features/comp-manager-group/class-mono-manager.md) 面板）。例如，如果有两个脚本，EngineBehaviour 和 SteeringBehaviour，可以设置 Script Execution Order（脚本执行顺序），这样 EngineBehaviours 始终在 SteeringBehaviours 之前更新。如果[以累加方式加载多个场景](/engine/6000.0/script-reference/unity-engine-scene-management/load-scene-mode/additive.md)，则配置好的脚本执行顺序将一次完整地应用于一个场景，而不是跨场景部分应用，因此 EngineBehaviour 和 SteeringBehaviour 都会在一个场景上更新，然后再在下一个场景上更新。

## 加载第一个场景

场景开始时将调用以下函数（为场景中的每个对象调用一次）。

* `Awake`：当对象的新实例被创建时调用的第一个生命周期函数。始终在任何 `Start` 函数之前调用。如果游戏对象在启动期间处于非活动状态，则在激活之后才会调用 `Awake`。
* `OnEnable`：在对象启用并处于活跃状态时调用，始终在 `Awake` 之后（在同一对象上）和任何 `Start` 之前调用。

对于属于场景资源一部分的对象，在为其中\_任何\_脚本调用 `Start` 和后续函数之前，会为\_所有\_脚本调用 `Awake` 和 `OnEnable` 函数。但是，在实例化对象运行时无法强制执行此设置。

仅在每个单独对象的作用域内，才能保证 `Awake` 在 `OnEnable` 之前被调用。多个对象之间的调用顺序不确定，不能指望在一个对象的 `OnEnable` 之前调用另一个对象的 `Awake`。任何依赖于为场景中的所有对象调用 `Awake` 的工作都应在 `Start` 中完成。

### 在加载和卸载场景之前

上图中未显示的 [SceneManager.sceneLoaded](/engine/6000.0/script-reference/unity-engine-scene-management/scene-manager/scene-loaded.md) 和 [SceneManager.sceneUnloaded](/engine/6000.0/script-reference/unity-engine-scene-management/scene-manager/scene-unloaded.md) 事件，可以分别在加载和卸载场景时接收回调。请参阅相关脚本参考页面以了解详情和示例用法。对于场景中的所有对象，可以预期在 `OnEnable` 之后但在 `Start` 之前收到 `sceneLoaded` 通知。请参阅[禁用 Domain 和 Scene Reload 的详细信息](/engine/6000.0/manual/scripting/compilation-and-code-reload/code-reloading-editor/configurable-enter-play-mode/details.md)以了解包含场景加载作为执行流程一部分的图表。

还可以使用 [RuntimeInitializeOnLoadMethodAttribute](/engine/6000.0/script-reference/unity-engine/runtime-initialize-on-load-method-attribute.md) 及其类型 [BeforeSceneLoad](/engine/6000.0/script-reference/unity-engine/runtime-initialize-load-type/before-scene-load.md) 和 [AfterSceneLoad](/engine/6000.0/script-reference/unity-engine/runtime-initialize-load-type/after-scene-load.md)，分别让使方法在场景加载之前或之后运行。请参阅 [RuntimeInitializeOnLoadMethodAttribute](/engine/6000.0/script-reference/unity-engine/runtime-initialize-on-load-method-attribute.md) 脚本参考主页，了解以这些类型标记的方法的执行顺序。

## Editor

* `Reset`: 在脚本首次附加到对象上时以及使用 *Reset* 命令时被调用，用于初始化脚本的属性。
* `OnValidate`: 每当设置脚本的属性时都会调用 OnValidate，包括反序列化对象时，这可能发生在不同的时间，例如在编辑器中打开场景时和域重新加载后。

## 在第一次帧更新之前

* `Start`: 仅当启用脚本实例后，才会在第一次帧更新之前调用 Start。

对于场景资源中的对象，在为任何脚本调用 `Update` 等函数之前，将在所有脚本上调用 `Start` 函数。但是，在游戏运行过程中实例化对象时，不能强制执行此调用。例如，如果从一个对象的 `Update` 函数实例化另一个对象，则在 `Update` 首次在原始对象上运行之前，无法调用实例化对象的 `Start`。

## 帧之间

* `OnApplicationPause`：在检测到暂停的帧结束时调用，实际上是在正常帧更新之间调用。在调用 `OnApplicationPause` 之后，将发出一个额外帧，从而允许游戏显示图形来指示暂停状态。

## 更新顺序

跟踪游戏逻辑和交互、动画、摄像机位置等的时候，可以使用一些不同事件。常见方案是在 `Update` 函数中执行大多数任务，但是也可以使用其他函数。

* `FixedUpdate` 以固定的游戏内时间间隔发生，并不是每帧发生。由于这些更新是固定的，并且帧率是可变的，因此当帧率很高时，一帧中可能没有固定更新，当帧率很低时，每帧可能有多个固定更新。所有物理计算和更新都在 `FixedUpdate` 后立即进行，并且由于独立于帧率，因此在计算 `FixedUpdate` 中的移动时无需将值乘以 [Time.deltaTime](/engine/6000.0/script-reference/unity-engine/time/delta-time.md)。固定更新发生的时间间隔由 [Time.fixedDeltaTime](/engine/6000.0/script-reference/unity-engine/time/fixed-delta-time.md) 定义，可以直接在脚本中或通过 Editor 的 [Time 设置](/engine/6000.0/manual/unity-editor/editor-features/comp-manager-group/class-time-manager.md)中的 **Fixed Timestep** 属性进行设置。更多信息，包括用于确定是否执行 `Update` 或 `FixedUpdate` 的时间计算，请参阅 [Time](/engine/6000.0/manual/scripting/object-oriented-development/managing-time-and-frame-rate.md)。

* 每帧调用一次 `Update`。这是用于帧更新的主要函数。

* 每帧调用一次 `LateUpdate`（在 `Update` 完成后）。在 `LateUpdate` 开始时，在 `Update` 中执行的任何计算都会完成。`LateUpdate` 的一种常见用途是用于后置的第三人称视角摄像机。如果在 `Update` 内让角色移动和转向，可以在 `LateUpdate` 中执行所有摄像机移动和旋转计算。这样可以确保角色在摄像机跟踪其位置之前已完全移动。

## 动画更新循环

在派生自 [MonoBehaviour](/engine/6000.0/script-reference/unity-engine/mono-behaviour.md) 的脚本上调用以上[流程图](/engine/6000.0/manual.md#ScriptLifecycleFlowchart\))中显示的以下动画循环回调：

* [MonoBehaviour.OnAnimatorMove](/engine/6000.0/script-reference/unity-engine/mono-behaviour/on-animator-move.md)
* [MonoBehaviour.OnAnimatorIK](/engine/6000.0/script-reference/unity-engine/mono-behaviour/on-animator-ik.md)

在派生自 [StateMachineBehaviour](/engine/6000.0/script-reference/unity-engine/state-machine-behaviour.md) 的脚本上调用其他与动画相关的事件函数：

* [StateMachineBehaviour.OnStateMachineEnter](/engine/6000.0/script-reference/unity-engine/state-machine-behaviour/on-state-machine-enter.md)
* [StateMachineBehaviour.OnStateMachineExit](/engine/6000.0/script-reference/unity-engine/state-machine-behaviour/on-state-machine-exit.md)
* [StateMachineBehaviour.OnStateEnter](/engine/6000.0/script-reference/unity-engine/state-machine-behaviour/on-state-enter.md)
* [StateMachineBehaviour.OnStateUpdate](/engine/6000.0/script-reference/unity-engine/state-machine-behaviour/on-state-update.md)
* [StateMachineBehaviour.OnStateExit](/engine/6000.0/script-reference/unity-engine/state-machine-behaviour/on-state-exit.md)
* [StateMachineBehaviour.OnStateMove](/engine/6000.0/script-reference/unity-engine/state-machine-behaviour/on-state-move.md)
* [StateMachineBehaviour.OnStateIK](/engine/6000.0/script-reference/unity-engine/state-machine-behaviour/on-state-ik.md)

有关这些回调的含义和限制，请参阅相关的脚本参考页面。

流程图中显示的其他动画函数是动画系统的内部函数，仅供参考。这些函数具有相关的 Profiler 标记，因此您可以使用 [Profiler](/engine/6000.0/manual/analysis/profiler.md) 查看 Unity 在帧中调用这些函数的时间。知道 Unity 调用这些函数的时间有助于准确了解所调用的事件函数的具体执行时间。有关动画函数和 Profiler 标记的完整执行顺序，请参阅 [Profiler 标记](/engine/6000.0/manual/analysis/profiler/markers.md#animation)。

## 渲染

此执行顺序仅适用于[内置渲染管线](/engine/6000.0/manual/rendering-and-post-processing/render-pipelines/built-in-render-pipeline.md)。有关基于[可编程渲染管线](/engine/6000.0/manual/rendering-and-post-processing/render-pipelines/scriptable-render-pipeline-introduction.md)的渲染管线执行顺序的详细信息，请参阅[通用渲染管线](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/index.html?subfolder=/manual/customize/custom-pass-injection-points.html)或[高清渲染管线](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@latest/index.html?subfolder=/manual/rendering-execution-order.html)文档的相关部分。如果要在渲染之前立即执行操作，请参阅 [Application.onBeforeRender](/engine/6000.0/script-reference/unity-engine/application/on-before-render.md)。

* `OnPreCull`：在摄像机剔除场景之前调用。剔除确定哪些对象对摄像机可见。在剔除之前会调用 `OnPreCull`。
* `OnBecameVisible`/`OnBecameInvisible`:当对象对任何摄像机可见/不可见时调用。上面的流程图中未显示 `OnBecameInvisible`，因为对象可能随时变得不可见。
* `OnWillRenderObject`：如果对象可见，则为每个摄像机调用**一次**。
* `OnPreRender`：在摄像机开始渲染场景之前调用。
* `OnRenderObject`：完成所有常规场景渲染之后调用。此时，可以使用 [GL](/engine/6000.0/script-reference/unity-engine/gl.md) 类或 [Graphics.DrawMeshNow](/engine/6000.0/script-reference/unity-engine/graphics/draw-mesh-now.md) 来绘制自定义几何形状。
* `OnPostRender`：在摄像机完成场景渲染之后调用。
* `OnRenderImage`：在场景渲染完成后调用以允许对图像进行后处理，请参阅[后处理效果](/engine/6000.0/manual/rendering-and-post-processing/post-processing-and-full-screen-effects/overview.md)。
* `OnGUI`：每帧调用多次以响应 GUI 事件。首先处理布局和重新绘制事件，然后为每个输入事件处理布局和键盘/鼠标事件。
* `OnDrawGizmos` 用于在场景视图中绘制辅助图标以实现可视化。

**注意**：[OnPreCull](/engine/6000.0/script-reference/unity-engine/camera/on-pre-cull.md)、[OnPreRender](/engine/6000.0/script-reference/unity-engine/camera/on-pre-render.md)、[OnPostRender](/engine/6000.0/script-reference/unity-engine/camera/on-post-render.md) 和 [OnRenderImage](/engine/6000.0/script-reference/unity-engine/camera/on-render-image.md) 是在 MonoBehaviour 脚本上调用的内置 Unity 事件函数，但**仅当这些脚本附加到与已启用的摄像机组件相同的对象上时**，才调用这些函数。如果要接收附加到**不同**对象的 MonoBehaviour 上的 `OnPreCull`、`OnPreRender` 和 `OnPostRender` 的等效回调，必须使用等效委托（注意名称中的小写 `on`）[Camera.onPreCull](/engine/6000.0/script-reference/unity-engine/camera/on-pre-cull.md)、[Camera.onPreRender](/engine/6000.0/script-reference/unity-engine/camera/on-pre-render.md) 和 [Camera.onPostRender](/engine/6000.0/script-reference/unity-engine/camera/on-post-render.md)，如脚本参考相关页面的代码示例所示。

## 协程

常规的协程更新会在`Update` 函数返回之后运行。协程是一种函数，它能够暂停自身的执行 (`yield`) 直到给定的 YieldInstruction 执行完毕。

协程的不同用法：

* `yield` 在下一帧上调用所有 `Update` 函数后，协程将继续。
* `yield WaitForSeconds` 在为帧调用所有 `Update` 函数后，在指定的时间延迟后继续协程。
* `yield WaitForFixedUpdate` 在所有脚本上调用所有 `FixedUpdate` 后继续。如果协同程序在 `FixedUpdate` 之前生成，那么它会在当前帧的 `FixedUpdate` 之后继续运行。
* `yield WWW` 在 WWW 下载完成后继续。
* `yield StartCoroutine` 链接协程，以便如果理论协程 `coroutineA` 以 `yield StartCoroutine(coroutineB());` 启动另一个 `coroutineB`，则 `coroutineA` 暂停，并等待 `coroutineB` 完成，然后再继续。有关示例，请参阅 [MonoBehaviour.StartCoroutine](/engine/6000.0/script-reference/unity-engine/mono-behaviour/start-coroutine.md)。

## 销毁对象时

* `OnDestroy`：此函数会在对象存在的最后一帧的所有帧更新完成后被调用（该对象可能应 Object.Destroy 要求或在场景关闭时被销毁）。

## 退出时

在场景中的所有活动对象上调用以下函数：

* `OnApplicationQuit`：在退出应用程序之前，在所有游戏对象上调用此函数。在编辑器中，用户停止播放模式时，调用函数。
* `OnDisable`：行为被禁用或处于非活动状态时，调用此函数。

## 其他资源

* [事件函数](/engine/6000.0/manual/scripting/object-oriented-development/event-handling/event-functions.md)
* [MonoBehaviour](/engine/6000.0/manual/scripting/get-started/fundamental-unity-types/class-mono-behaviour.md)
