Unity 生命周期详解
Unity 中常说的“生命周期”,通常指的是 MonoBehaviour 脚本从初始化、启用、运行到禁用、销毁这一整套事件函数的执行过程。理解它们的调用顺序后,就能更合理地安排初始化、输入、物理、相机跟随和资源清理等逻辑。
一、生命周期整体流程
Unity 常见的脚本生命周期可以分为几部分:
1. 编辑器阶段
ResetOnValidate
2. 初始化阶段
AwakeOnEnableStart
3. 每帧更新阶段
FixedUpdateUpdateLateUpdate
4. 应用状态阶段
OnApplicationPauseOnApplicationFocusOnApplicationQuit
5. 结束阶段
OnDisableOnDestroy
最常见的一条主线可以概括为:
Awake -> OnEnable -> Start -> FixedUpdate / Update / LateUpdate -> OnDisable -> OnDestroy
二、初始化阶段
1. Awake
Awake 会在脚本实例被加载时调用,并且一定先于 Start 执行。它适合做“脚本自身的初始化工作”。
常见用途:
- 缓存组件引用
- 初始化私有变量
- 单例赋值
- 不依赖其他对象的准备工作
示例:
1 | private Rigidbody rb; |
2. OnEnable
OnEnable 会在对象或组件启用时调用。只要对象重新激活,或者脚本从禁用变为启用,它都可能再次执行。
常见用途:
- 注册事件
- 开始监听
- 恢复状态
示例:
1 | void OnEnable() |
通常它会和 OnDisable 配对使用。
3. Start
Start 会在第一帧更新前调用,并且通常只执行一次。它适合做“依赖其他对象已经初始化完成之后”的逻辑。
常见用途:
- 获取场景中其他对象引用
- 执行依赖外部系统的初始化
- 首次刷新 UI
示例:
1 | void Start() |
可以简单记成:
Awake:先把自己准备好Start:大家准备差不多了再开始协作
三、每帧更新阶段
1. FixedUpdate
FixedUpdate 按固定时间步长执行,主要用于物理系统。它不一定每帧执行一次,可能一帧执行多次,也可能某一帧不执行。
适合放在这里的内容:
- 刚体移动
AddForce- 物理计算
- 与物理强相关的逻辑
示例:
1 | void FixedUpdate() |
注意:输入读取通常不要放在 FixedUpdate 中。
2. Update
Update 每一帧执行一次,是最常用的逻辑更新函数。
适合放在这里的内容:
- 玩家输入
- 普通逻辑判断
- 倒计时
- 状态机更新
- UI 刷新
示例:
1 | void Update() |
3. LateUpdate
LateUpdate 会在当前帧所有 Update 执行完成后再执行,通常用于“本帧收尾工作”。
适合放在这里的内容:
- 摄像机跟随
- 位置修正
- 依赖其他对象本帧已完成更新后的逻辑
示例:
1 | void LateUpdate() |
最经典的用途就是:角色先移动,摄像机后跟随。
四、禁用与销毁阶段
1. OnDisable
OnDisable 会在对象失活或脚本禁用时调用。它可能被调用多次,因为对象可以反复启用和禁用。
常见用途:
- 取消事件订阅
- 停止监听
- 暂停部分逻辑
示例:
1 | void OnDisable() |
2. OnDestroy
OnDestroy 会在对象被真正销毁时调用,通常用于最终清理。
常见用途:
- 释放资源
- 保存状态
- 清理引用
示例:
1 | void OnDestroy() |
五、编辑器阶段的生命周期
1. Reset
Reset 会在脚本第一次挂载到对象上时调用,或者在 Inspector 中点击 Reset 时调用。
用途:
- 设置默认值
- 初始化组件配置
2. OnValidate
OnValidate 会在 Inspector 中修改字段时调用,适合做参数校验。
示例:
1 | void OnValidate() |
六、应用状态相关函数
1. OnApplicationPause
当应用暂停时调用,比如切到后台。
2. OnApplicationFocus
当应用获得或失去焦点时调用。
3. OnApplicationQuit
当应用退出时调用。
这些函数常用于:
- 自动暂停游戏
- 存档
- 静音处理
- 退出前清理
七、常见使用原则
在实际开发中,可以这样分工:
Awake
- 初始化自身
GetComponent- 单例赋值
OnEnable
- 注册事件
- 开启监听
Start
- 获取其他对象
- 启动依赖外部的逻辑
Update
- 输入处理
- 普通逻辑更新
FixedUpdate
- 物理运算
- 刚体移动
LateUpdate
- 摄像机跟随
- 本帧收尾修正
OnDisable / OnDestroy
- 取消订阅
- 清理资源
八、常见坑点
1. 在 Awake 中依赖其他对象初始化
如果其他对象也在 Awake 中初始化,执行顺序不一定可靠,容易出现空引用问题。更稳妥的做法是:
- 自己的初始化放
Awake - 与别人协作的初始化放
Start
2. 在 Update 中做物理移动
如果对象使用 Rigidbody,通常应该把物理移动逻辑放在 FixedUpdate 中。
3. 在 FixedUpdate 中读取输入
输入一般应放在 Update 中读取,再把结果交给 FixedUpdate 处理。
4. 事件注册后忘记取消
推荐搭配写法:
OnEnable中注册OnDisable中取消注册
5. 依赖不同脚本的默认执行顺序
不同对象之间同名生命周期函数的调用顺序通常不应该被强依赖。若确实需要控制执行先后,可以使用 Script Execution Order。
九、完整示例代码
1 | using UnityEngine; |
十、总结
Unity 生命周期最核心的理解就是:
Awake:自身初始化OnEnable:启用时执行Start:开始运行前初始化Update:每帧逻辑FixedUpdate:物理更新LateUpdate:本帧收尾OnDisable:禁用时清理OnDestroy:销毁时最终清理
一句口诀:
自己初始化用
Awake,
启用订阅用OnEnable,
协作启动用Start,
输入逻辑用Update,
物理运算用FixedUpdate,
摄像机跟随用LateUpdate,
清理解绑用OnDisable,
最终销毁用OnDestroy。