一.Unity 中常见的生命周期方法
1Awake(),2OnEnable(), 3Start(), 4FixedUpdate(), 5Update(), 6LateUpdate(), 7OnGUI(), 8OnDisable(), 9OnDestroy().
二.通常的执行顺序
特殊一点的方法Reset()
Reset
方法是在编辑器中调用的,并且仅在以下情况下执行:
-
脚本组件被添加到 GameObject 上: 当你将一个脚本组件添加到一个 GameObject 上时,如果该脚本包含
Reset
方法,该方法会在组件添加后立即被调用。 -
编辑器中修改了脚本的公共字段或属性: 如果在 Inspector 中修改了脚本的公共字段或属性,
Reset
方法会在你停止编辑器播放或重新加载场景时调用。 -
重置组件: 在 Inspector 中,右键点击脚本组件的标题栏,选择 "Reset" 选项,可以手动触发
Reset
方法。
这样设计的目的是确保在编辑器中对脚本属性的修改能够及时地反映到场景中,同时提供了一个机会来确保编辑器中的属性值与脚本的初始状态保持一致。Reset
方法对于在编辑器中进行调试、初始化和同步属性状态等方面非常有用。在运行时(在构建后的实际游戏中),Reset
方法不会被调用。
构造函数甚至在脚本编辑完游戏没有启动就会调用。
1.Awake():
在所有的游戏对象被实例化之后调用,必须在一个被激活的对象中第一次添加脚本时会调用一次Awake(),或者在未激活的对象中添加脚本后,再进行激活对象后会调用一次Awake().(即使脚本组件未被激活也会执行)
2.OnEnable():
如果对象被启用(enabled),接着调用 OnEnable。这是对象变为激活状态时的第一个方法。
3.Start():
方法调用时机:在对象第一次更新时调用。用于初始化,但是在 Awake 后调用。
4.FixedUpdate() 和 Update():
在 Start 方法执行完毕后,按照固定的时间间隔调用 FixedUpdate 方法,然后在每一帧调用 Update 方法。
5.LateUpdate():
在 Update 方法执行完毕后,调用 LateUpdate 方法。通常用于相机跟随等操作,确保在所有 Update 方法执行完毕后执行。
6.OnGUI():
在 LateUpdate 方法执行完毕后,调用 OnGUI 方法。用于渲染 GUI 元素。
7.OnDisable():
如果对象被禁用,调用 OnDisable 方法。在对象被禁用时执行一些清理工作。
8.OnDestroy():
如果对象被销毁,最后调用 OnDestroy 方法。在对象销毁时执行最终的清理工作。
三.一些使用建议
1.在使用awake和start时:
1.在生命周期中都只调用一次
2.awake在start之前调用,并且在所有游戏对象的awake方法都调用完才能调用某一个游戏对象的start方法,所以如果start方法中有需要提前获取才能运行的需求,一定要写在awake中,以免报错。
3.避免在 Awake
或 Start
中执行过于耗时的操作,以免影响游戏启动性能。这两个方法应该用于轻量级的初始化工作。
4.脚本的构造函数一般先于Awake
和Start,但在unity中一般不直接使用构造函数。
5.Awake即使在脚本组件未被启用时也会执行,而Start必须在脚本组件被启用后才能进行执行。
2.Update
、FixedUpdate
和 LateUpdate的区别
- Update:
- 调用频率: 在每一帧都会调用。
- 用途: 用于处理与游戏逻辑和用户输入相关的代码。
- 建议用法: 处理输入、游戏逻辑、玩家控制等。
- FixedUpdate:
- 调用频率: 固定的时间间隔内调用,通常默认为每秒 50 次。
- 用途: 用于处理物理计算和更新,确保在不同平台上物理模拟的结果一致。
- 建议用法: 处理刚体运动、物理碰撞等。
- LateUpdate:
- 调用频率: 在所有
Update
和FixedUpdate
方法执行完毕后调用。 - 用途: 用于处理相机跟随逻辑,确保在所有对象的位置和旋转更新后再执行。
- 建议用法: 处理相机跟随、后处理效果等。
- 调用频率: 在所有
建议使用:
- 尽量将用户输入和游戏逻辑放在
Update
中,因为它在每一帧都被调用,适合处理实时性较高的操作。 - 将物理相关的计算放在
FixedUpdate
中,确保在固定时间间隔内进行物理更新,使得物理模拟更加准确。 - 使用
LateUpdate
处理相机跟随逻辑,确保在所有对象的位置和旋转更新后再执行,避免相机跟随的抖动或不稳定情况。
3.onEnable和ondisable的区别
OnEnable
:- 调用时机: 在对象变为激活状态(enabled)时调用,无论是在场景开始运行时还是在脚本实例化时。
- 用途: 通常用于初始化工作、资源加载、订阅事件或启动协程等操作,确保在对象激活时执行。
- 注意事项: 不要在
OnEnable
中执行耗时的操作,因为它可能会影响游戏的启动性能。
OnDisable
:- 调用时机: 在对象变为非激活状态(disabled)时调用,例如对象被禁用或销毁。
- 用途: 通常用于清理工作、取消订阅事件、停止协程等,确保在对象禁用或销毁时执行。
- 注意事项: 不要在
OnDisable
中执行需要对象处于激活状态的操作。
建议使用:
- 在
OnEnable
中进行资源加载、初始化、订阅事件等操作,确保在对象激活时执行。 - 在
OnDisable
中进行清理工作、取消订阅事件、停止协程等,确保在对象禁用或销毁时执行。 - 避免在
OnEnable
和OnDisable
中执行过多耗时的操作,以免影响游戏性能。
综合使用 OnEnable
和 OnDisable
方法,可以在对象的生命周期中实现一些必要的初始化和清理逻辑,确保对象在激活和禁用时能够执行特定的操作。
四.面试可能会问的问题
1.生命周期方法的执行顺序是什么?
Awake
, OnEnable
, Start
, FixedUpdate
, Update
, LateUpdate
, OnGUI
, OnDisable
, OnDestroy
.
2.为什么有时候使用 Awake
而不是 Start
?
Awake
在对象被实例化时调用,通常用于初始化,而 Start
在 Awake
后执行,适合进行一次性的初始化工作。如果需要确保在所有脚本的 Awake
阶段执行,可以使用 Awake
。
3.LateUpdate
适用于什么样的情况?
LateUpdate
适合处理相机跟随逻辑,确保在所有对象的位置和旋转更新后再执行,避免相机抖动或不稳定情况。
4.如何处理对象被禁用和销毁时的清理工作?
OnDisable
用于在对象被禁用时执行清理操作,OnDestroy
用于在对象被销毁时执行最终的清理工作。
5.为什么不在 Update
中进行物理计算?
FixedUpdate
适合处理物理计算,因为它以固定的时间间隔调用,确保在不同平台上物理模拟的结果一致。
6.如何在编辑器中执行初始化操作?
在 Reset
方法中执行初始化操作,以确保在编辑器中对脚本属性的修改能够及时地反映到场景中。
7.协程能在哪些生命周期方法中使用?
协程通常在 Start
或之后的生命周期方法中使用,不建议在 Awake
中使用。
8.生命周期方法执行的频率和如何优化性能?
生命周期方法的执行频率取决于引擎调用的频率。避免在这些方法中执行过多的计算,可以提高性能。
9.如何处理单例模式在 Unity 生命周期中的问题?
使用 Awake
方法来确保单例模式在对象实例化时初始化,并使用 OnDestroy
方法来处理对象销毁时的清理工作。
例子
public class GameManager : MonoBehaviour
{
// 单例模式实例
private static GameManager instance;
// 获取单例实例的方法
public static GameManager Instance
{
get
{
if (instance == null)
{
// 如果实例不存在,则尝试在场景中找到已有的实例
instance = FindObjectOfType<GameManager>();
// 如果场景中不存在实例,则创建一个新的实例
if (instance == null)
{
GameObject obj = new GameObject("GameManager");
instance = obj.AddComponent<GameManager>();
}
}
return instance;
}
}
// 在 Awake 中确保单例的正确性
private void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject); // 防止在场景切换时被销毁
}
else
{
Destroy(gameObject); // 如果有重复的实例,销毁新创建的实例
}
}
// 游戏初始化逻辑
private void Start()
{
// 这里可以添加游戏初始化的逻辑
}
// 游戏结束时的清理工作
private void OnDestroy()
{
// 这里可以添加游戏结束时的清理工作
}
}
10.MonoBehaviour 的生命周期方法和非 MonoBehaviour 类的区别是什么?
MonoBehaviour 类的生命周期方法由引擎调用,用于管理对象的生命周期,而非 MonoBehaviour 类则没有这些特殊的生命周期方法。在非 MonoBehaviour 类中,需要手动管理对象的初始化和清理。