这篇笔记主要讲述一下关于 MonoBehavior 这个 Unity 默认创建的脚本父类中的一些需要知道的内容。
生命周期
下图来自于 Unity 官方。在这张图中,我们可以看到 MonoBehavior 中的各个函数的先后顺序。
初始化
在 Initialization 阶段,如上图所示,会用到的函数为 Awake(), Reset(), OnEnable() 和 Start()。
Awake
Awake 在加载脚本实例的时候被调用。这是所有函数中第一个被调用的函数。它用于初始化(通常是局部的或是与其他脚本无关的)对象和变量。
这个函数会在所有对象被初始化之后被调用,但这个函数也会在游戏对象被激活(Enable)之前调用,并且即使这个脚本组件没有被激活,它也会被调用。
Awake 仅会被调用一次。
Reset
Reset 是在编辑器中,用户首次添加组件或者重置脚本的时候调用。通常会被用来设置默认值。
这个函数不会在游戏运行时调用。在编辑器模式下,添加组件到游戏对象或点击组件的 Reset 选项时(在右上角那三个点里面),这个函数会被调用一次。
OnEnable
当脚本实例被激活的时候调用。
激活状态可以在编辑器模式看,就是脚本名字左边那个方框中的 √ 被点掉,那就是 Disabled。如果点上了 ,那就是 Enabled。
在 C# 中,可以通过.isEnabled = true/false 来激活/禁用这个脚本实例。
这个函数在 Awake 之后,在 Start 之前。因为只要这个脚本实例被激活(从 Disabled 状态变成 Enabled 状态就算做激活)这个函数就会被调用,所以这个函数可以被调用多次。
Start
在脚本首次 Update 之前调用一次,这个函数也通常用于设置初始化,尤其是需要依赖其他对象、其他脚本的初始化。
这个函数在 Awake 和 OnEnable 之后被调用。如果脚本没有被激活,这个函数并不会被调用。
物理
在 Physics 阶段,我们主要讨论 FixedUpdate, OnCollisionXXX, OnTriggerXXX。其中也会涉及到一些跟动画相关的函数,如 OnAnimatorMove(), OnAnimatorIK(int layerIndex), OnStateMachineEnter/Exit,但它们的调用顺序并不是很重要,我们先看前面三个。
FixedUpdate
FixedUpdate 用于处理与物理相关的代码,比如刚体的移动、旋转、物理计算之类的。它是固定时间更新的(每秒调用 50 次,即步长 0.02 秒),与帧率无关,所以物理模拟可以在不同帧率下保持一致。它调用的频率可以在 Unity 的 TimeManager 中调整。
OnCollisionEnter/Stay/Exit (Collision collision)
OnCollisionXXX 函数都是处理碰撞事件的。它们都使用一个 Collision 作为参数。
Enter:当两个物体的碰撞器首次接触时,对于每个碰撞的对象,这个函数会被调用一次。
Stay:当碰撞持续存在的每个物理更新中被调用。物理更新的固定步长与 FixedUpdate 一样是 0.02 秒(或者也可以说 FixedUpdate 就是在每个物理更新中被调用的)。
Exit:当碰撞结束时,这个函数被调用一次。
注意,OnCollision 系列函数需要碰撞的双方至少有一个携带 Rigidbody 组件。
OnTriggerEnter/Stay/Exit (Collider other)
OnTriggerEnXXX 函数都是处理触发器事件的。它们都使用一个 Collider 作为参数。与 OnCollision 系列类似,这里不予赘述。
OnControllerColliderHit(ColliderHit hit)
特殊地,处理 Character Controller 组件与其他碰撞器基础时,我们可以使用这个函数。
输入与游戏逻辑
在 Input 和 Game Logic 阶段,我们要提及的函数主要是 OnMouseXXX, Update 和 LateUpdate。
OnMouseDown/Up/Drag/Over/Exit
OnMouse 系列处理的是当用户使用鼠标与游戏对象的 Collider 产生交互的时候触发的事件。
Down: 用户点击时触发的事件;
Up: 用户释放在游戏对象 Collider 按下的鼠标按钮时触发;
Drag: 当用户按住鼠标按钮并在游戏对象 Collider 上移动时触发;
Over:当用户鼠标悬停在游戏对象 Collider 上时持续调用;
Exit:当用户鼠标不再悬停在游戏对象 Collider 上时调用。
需要注意的是游戏对象必须要有 Collider。
Update
Update 函数每一帧调用一次,用以处理常规游戏的逻辑。
由于每帧调用,并且调用频率与帧率相同,因此适用处理动画和平滑地移动。
LateUpdate
LateUpdate 函数在 Update 函数之后调用,而且是在场景中所有的 GameObject 的 Update 执行完成之后执行。因此,这个函数通常用于解决一些需要依赖顺序的更新。
这个函数常用于调整摄像机和跟随效果,因为它确保所有对象都已经完成了它们的 Update 移动。
渲染
在 Scene Rendering 阶段,这一部分的函数主要是为了提供渲染管线中 CPU 向 GPU 提供素材的那一部分数据。注意,这一部分函数在 URP 和 Built-In 中的表现可能不同,因为 Unity 提供了不同的渲染管线。
OnPreCull
在摄像机开始剔除前调用,可以用于修改摄像机的属性或场景的状态。、
OnWillRenderObject
这个函数会在渲染对象之前在每个摄像机上调用。常用于对象级别的渲染前设置,也可以用于多摄像机的视图调整。
OnBecameVisible / OnBecameInvisible
这是当对象变为可见或不可见的时候自动调用的函数。
OnPreRender / OnRenderObject / OnPostRender
在摄像机开始渲染场景之前 / 渲染场景之时 / 渲染场景之后调用的函数。都应该谨慎使用,因为可能会影响渲染管线的性能。
OnRenderImage
在 URP 中没有效果。所有图像并准备显示在屏幕上之前调用,通常会用于图像后处理效果。容易对性能产生一定影响。
参考资料:
Unity Manual, https://docs.unity3d.com/Manual/ExecutionOrder.html
Comments