浅谈Unity生命周期

Unity 的生命周期指的是游戏对象和组件在其整个生命周期内经历的不同阶段和事件。这些生命周期事件会在特定时刻触发,让你可以在不同的时机执行代码,来控制游戏对象和组件的行为。在使用 Unity 引擎开发之前,一定要先了解它脚本的生命周期。这篇文章我们就对 Unity 的生命周期中重要的事件函数来进行一些讲解。

编译期生命周期

Reset() 初始化

Reset()重置函数:Reset函数是一种特殊的编辑器脚本函数,用于自定义Unity编辑器中的组件的默认属性值和行为。当你在脚本中实现Reset函数时,它将在以下情况下自动调用:

  • 当你将脚本组件拖拽到Unity编辑器的组件面板上时。

如果在运行期间动态将脚本挂到一个游戏对象上,这个函数也不会执行。

OnDrawGizmos() 更新

OnDrawGizmos()函数:OnDrawGizmos函数是Unity中的一个特殊编辑器脚本函数,用于在场景视图中绘制可视化的辅助信息和标记,这些可视化元素通常用于帮助开发人员调试和可视化游戏对象的状态和行为。这个函数在编辑期间和在运行期间调用频率不同。

  • 编译期间:OnDrawGizmos 函数在Unity编辑器中运行时频繁调用,用于在场景视图中绘制可视化辅助信息。这使得开发人员能够实时查看游戏对象的可视化状态和调试信息,以便更好地理解游戏场景。在编辑器中进行场景构建和调试时,这些可视化元素对于快速迭代和问题排查非常有用。
    它不是每帧执行,而是只在 Scene 视图打开的时候,在这个视图下进行任何操作时都会调用这个函数,包括运行时也会调用。如果不在Scene视图下,就算进行操作也不会调用这个函数。

运行期生命周期

我们先来看这样一张图(官方手册2023.3版本):
Unity生命周期

Awake()

Awake 是 Unity MonoBehaviour 脚本中的一个特殊函数,用于初始化对象的状态、引用和配置,以准备好在游戏运行时执行。这是一个常用于设置变量、引用其他组件、加载资源等的地方。
它在游戏对象首次被实例化后立即调用整个脚本生命周期只执行一次。注意,如果游戏对象开始运行时未激活,Awake也不会执行,直到游戏对象第一次被激活。如果游戏对象开始运行时就已经激活,那么无论这个脚本是否启用了,Awake都会执行。

OnEnable()

OnEnable 通常用于在脚本组件激活时进行初始化、订阅事件、或执行其他操作。
OnEnable 与 OnDisable() 配套结合,当脚本启用(注意,游戏对象激活时会去调用被启用的脚本,OnEnable会执行;不改变游戏对象,只启用脚本时OnEnable也会执行)的时候触发,运行期间可以执行多次。只有挂载的游戏对象和脚本都激活时,这个方法才会被调用。

OnApplicationPause(bool pause)

OnApplicationPause 是一个Unity中的特殊生命周期函数,用于处理应用程序暂停和恢复时的事件。这个函数在MonoBehaviour脚本中使用,主要用于移动平台(如iOS和Android)上的应用程序,以处理应用程序暂停和恢复时的逻辑。当你切换后台,打开别的软件时,这个函数会自动调用,并且将 “pause” 修改为 “true”,当你再切回来的时候这个函数又自动调用,并将 “pause” 修改为 “false”,在脚本的生命周期可以执行多次

Start()

Start 在 Awake 和 OnEnable 之后执行,用于执行一次性的初始化操作,一般用于给脚本字段初始值。但需要注意,Start 函数只会在脚本所附着的游戏对象首次进入游戏时执行,整个脚本的生命周期只执行一次。只有挂载的游戏对象和脚本都激活时,这个方法才会被调用。

以上几个函数,当游戏开始运行时,如果游戏对象和脚本都是启动的,那么执行顺序如图所示:
启动时函数的执行顺序

FixedUpdate() 物理更新

FixedUpdate 是Unity中的一个特殊生命周期函数,用于处理物理更新和固定时间间隔的逻辑。例如给刚体加一个作用力时,你必须应用作用力在FixedUpdate里的固定帧,而不是Update中的帧。(两者帧长不同)。FixedUpdate 函数在每秒之间以固定的时间间隔执行。这个时间间隔由Unity的物理时间步长决定,通常为每秒50次(默认情况下),但可以根据项目设置进行调整。

  • 实际帧率低于固定帧率:例如固定帧率设置为60帧每秒,实际帧率为30帧每秒,Unity仍然会以固定帧率的频率调用 FixedUpdate()。物理模拟仍然以固定时间步骤的频率进行更新,与 FixedUpdate() 同步。但游戏渲染帧可能会跳过一些帧,以匹配实际帧率,相当于一次渲染帧之间可能进行了多次物理计算

  • 实际帧率高于固定帧率:例如固定帧率设置为30帧每秒,实际帧率为60帧每秒,如果实际帧率高于固定帧率,那么 FixedUpdate() 将仍然以固定时间步骤频率调用,但在两次调用之间可能有额外的帧渲染。这可能导致多次帧渲染之间没有物理更新,因为 FixedUpdate() 是按固定频率进行调用的相当于多次渲染帧之间可能只进行了一次物理计算

固定帧率(Fixed Frame Rate)是你在Unity项目设置中指定的每秒渲染多少次。这是一个目标帧率,Unity将尝试以这个帧率来渲染画面和执行物理计算。
实际帧率(Actual Frame Rate)是实际情况下你的应用程序每秒渲染的帧数,通常是由计算机性能、场景复杂性等因素决定的。

Update() 逻辑更新

Update() 函数在每一帧渲染之前都会被调用,这意味着它的调用频率与实际帧率一致,执行的频率不固定,取决于计算机性能等因素。通常用于处理用户输入、游戏逻辑和交互,可以在这个函数中编写代码来响应玩家的操作,移动游戏角色,检测碰撞,更新游戏状态等。

LateUpdate() 逻辑后更新

LateUpdate() 函数在每一帧的最后被调用,它是在 Update() 和物理更新函数(如 FixedUpdate())之后执行的。这意味着在 LateUpdate() 中的逻辑会在其他更新逻辑之后执行,通常在渲染之前。LateUpdate() 主要用于处理与其他游戏对象、相机和渲染相关的逻辑。它通常用于摄像机跟随、玩家控制、相机视野调整以及其他可能受到游戏对象位置和状态影响的操作。
许多相机操作通常在 LateUpdate() 中执行,以确保相机位置和视野在渲染之前正确更新,包括摄像机跟随、插值相机位置、处理相机震动效果等。
如果你的逻辑需要考虑其他游戏对象的位置和状态,或者需要与相机有关的操作,那么 LateUpdate() 可能更适合这些情况。

对于一个相机跟随角色脚本,相机的操作应该放在什么函数里呢?
应该放在 LateUpdate() 函数里,因为相机的状态与角色的状态相关,如果放在 Update() 函数里,可能出现角色状态还没有更新,相机位置就已经更新了的情况,而在 LateUpdate() 中执行相机操作可以确保它们基于其他对象的最新位置和状态。
但如果相机状态与其他游戏对象没有关系,那么放在 Update() 函数里也可以。

OnWillRenderObject() 场景渲染

OnWillRenderObject() 函数在相机渲染某个可见物体时被调用。这个函数通常用于处理物体在相机视图中的可见性以及与相机有关的操作。如果对象可见,每个照相机调用一次,通常是在物体的渲染帧之前。对于一些确定要渲染的对象,可以在正式渲染前再做一些特殊处理,比如裁剪,材质重置等。

OnDrawGizmos() 线框渲染

在 Scene 视图打开的时候每一帧进行更新,和在编辑器模式下调用的频率不同。

OnGUI() 图形绘制

OnGUI() 函数在渲染和处理GUI事件时调用,用于处理游戏的图形用户界面,这个函数通常用于在游戏屏幕上绘制各种用户界面元素,如按钮、标签、文本框等。OnGUI() 函数在每一帧渲染之后调用,用于绘制游戏界面。它是在 Update() 和 LateUpdate() 后执行的,通常在渲染帧之后。
OnGUI() 通常与其他更新函数(如 Update()、LateUpdate())一起使用,以便根据游戏逻辑更新用户界面。你可以在这些函数中设置用户界面的状态,然后在 OnGUI() 中将其绘制出来。如果Monobehaviour的enabled属性设为false,OnGUI()将不会被调用。
一般情况下,它在每一帧中只会执行一次。然而,有一些特殊情况下,OnGUI() 可能会被调用多次。例如,如果你使用GUI布局系统中的 Repaint() 函数来强制重新绘制界面,或者在特定事件中触发了GUI的刷新,这可能导致 OnGUI() 被多次调用。但这些情况属于特殊情况,不是每一帧都会发生的情况。

OnApplicationQuit() 函数

OnApplicationQuit() 函数用于在应用程序即将退出时执行一些清理和处理操作。它会在用户关闭游戏或应用程序时调用。通常,你可以在这个函数中执行例如保存游戏状态、释放资源、关闭网络连接等必要的清理工作。如果你的应用程序有多个场景,该函数将在退出当前场景时触发,而不是在整个应用程序退出时触发。

注意,Unity 仅在正常退出时(用户手动关闭应用程序)才会调用 OnApplicationQuit()。如果应用程序因为崩溃或其他异常情况而关闭,该函数可能不会被触发。

OnDisable() 禁用函数

OnDisable() 函数用于处理脚本被禁用时的逻辑。当你在 Unity 编辑器中手动禁用脚本组件,或者通过代码设置脚本的 enabled 属性为 false 时,OnDisable() 函数将被调用。
OnDisable() 主要用于执行脚本组件在被禁用时的清理操作。这可以包括停止协程、释放资源、取消事件订阅等。它通常用于确保脚本在被禁用时不会继续执行不必要的逻辑。

OnDestory() 销毁函数

OnDestroy() 函数用于处理游戏对象或脚本组件的销毁逻辑。当游戏对象被销毁时,或者脚本组件从游戏对象上移除时,OnDestroy() 函数将被调用。
销毁游戏对象通常发生在场景切换、关卡结束或手动销毁游戏对象时。OnDestroy() 主要用于执行在游戏对象销毁前的清理操作。这可以包括释放资源、取消事件订阅、清理协程等。它用于确保游戏对象或脚本在被销毁时不会留下不必要的遗留物。