Unity3D的生命周期函数
Unity3D的生命周期可分为七个阶段:
初始化阶段、物理阶段、输入事件阶段、逻辑阶段、渲染阶段、暂停阶段、退出阶段
1、初始化阶段
-
Awake()
(唤醒):当一个脚本实例被载入时Awake被调用,无论脚本是否可用,只要物体被加载就会调用,Awake常用于在游戏开始之前初始化变量或游戏状态。可以判断当满足条件后执行此脚本(只调用一次)。 -
OnEnable()
(当可用):当对象变为可用或激活状态(IsActive
)时此函数被调用。(可多次调用) -
Reset()
(重置):(Editor)Reset是在用户点击检视面板的Reset按钮或者首次添加该组件时被调用。此函数只在编辑模式下被调用。Reset最常用于在检视面板中给定一个最常用的默认值。 -
Start()
(开始):物体被载入后脚本对象启用时被调用1次,常用于数据或逻辑对象初始化(只调用一次)。Start仅在Update函数第一次被调用前调用,且只会在脚本实例启用时被调用一次。Awake总是在Start之前执行。可以按需调整延迟初始化代码。
2、物理阶段
FixedUpdate()
(固定更新):
-
FixedUpdate基于一个可靠的定时器被调用,独立于帧率之外。如果固定的时间步长小于实际的帧更新时间,那么每帧的物理循环可能会发生不止一次。
-
处理物体的物理属性(Rigidbody、Force、Collider)或者输入事件时,需要用FixedUpdate代替Update,以使物体的物理表现更平滑。
-
实际上,FixedUpdate并不是真的按照现实时间间隔执行的,而是按照 C# 中Timer时间间隔执行的,但Timer并不是真正意义上的现实时间,它的作用是在运行环境下创造一个与现实时间高度相近的变量来实现物理帧的逻辑稳定。因为FixedUpdate的这个特质,强烈建议在此环节只做物理相关的处理,不要把其他类型(如网络帧同步)的处理也放入此步骤。默认频率大概为0.02s,该频率可手动修改。
固定更新结束后,系统内部会进行一系列的操作(碰撞检测函数、更新协程yieldWaitForFixedUpdate() )
3、输入事件阶段
OnMouseEnter()
OnMouseOver()
OnMouseExit()
等
这些函数只会在带有输入检测能力的游戏对象上调用,例如带有 MeshCollider、BoxCollider、SphereCollider 或自定义的 Collider 等。鼠标、键盘、触屏、手柄等各类输入事件会在这个阶段触发,这个时间点物理更新已经执行(如果需要物理更新的话),而逻辑更新和渲染并未执行。
4、游戏逻辑阶段
-
Update()
(更新):Update是真正的每帧调用的。由于系统性能以及游戏体量的区别,每一帧的刷新频率也是不同的,所以不要过分期待在Update方法中按时完成任务。Update与FixedUpdate实际上是使用同一个线程的,update在loop中的处理方式是本次更新完毕再根据上一帧到现在的偏移时间判断是否进行下一次更新,Update的本质就是回调函数。只要是回调函数就存在上下文传递的损耗,所以如果想减少回调,可以考虑自己实现一套update机制,使用虚函数来代替update。
-
LateUpdate()
(延后更新):每帧Update方法调用之后会调用本方法。因为游戏开发过程中经常会有一个二次计算的情况,比如主角移动,相机跟着移动。如果相机也在主角移动时跟随,当有物体跟玩家之间产生了相位,就可能会出现抽搐抖动等情况(因为并没有在这一帧逻辑完全结束后调用跟随)。所以LateUpdate的出现能够使程序更加顺畅。
5、渲染阶段
-
OnWillRenderObject()
当即将渲染物体时调用。 -
OnPreCull()
这个函数仅用于宿主为摄像机的脚本。当此摄像机剔除了某个渲染场景时候触发此消息。 -
OnBecameVisable()
(即将可见)当物体即将可见时调用。 -
OnBecameInvisible()
(即将不可见)当物体即将不可见时调用。 -
OnPreRender()
(即将渲染)这个函数仅用于宿主为摄像机的脚本。当此摄像机开始渲染某个场景时候触发此消息。在所有渲染开始之前调用,这个方法其实是很考究的,我看到大部分网友对于这个方法都是抄了几种用法:加入脚本、设定标题、设定按钮客户端事件、设定控件的状态、加入脚本块。个人觉得渲染前可以做的事并不仅仅如此,比如是不是可以考虑在渲染前做一些渲染优化,虽然现在已经有很多插件了,但是如果程序设计的好可以考虑自己实现一套优化,这样更贴合自己的程序。 -
OnRenderObject()
这个函数仅用于宿主为摄像机的脚本。当使用Graphics.DrawMeshNow 或者其他函数绘制自己建立的物体渲染完毕时触发。 -
OnPostRender()
这个函数仅用于宿主为摄像机的脚本。当此摄像机范围内所有渲染都完成时候触发此消息。 -
OnRenderImage()
当所有渲染完成image的post-processing effects后触发。 -
OnDrawGizmos()
(Gizmos渲染)Gizmos一般是为开发者使用的,指的是开发时场景编辑器中所展示的那些相机、线框之类的物体。所以此方法里的内容一般不会需要发布到生产环境中。
GUI渲染用户界面渲染的工作会在这一步执行。
6、暂停阶段
OnApplicationPause()
(应用暂停)
应用暂停时会调用此方法
OnApplicationFocus()
函数
取消暂停后会调用,取消暂停后会从FixedUpdate开始重新执行。
7、退出阶段
-
OnDestroy()
(销毁)当物体被销毁时调用,一般用于清理内存。 -
OnApplicationQuit()
(应用退出)当应用退出时调用,但有时会失效,此方法为不稳定的方法,正常情况下可以用于保存退出前的信息,但最好使用更稳妥的方式,因为此方法有时不会被调用,比如Android环境。
总结
初始化阶段常用生命周期函数有Awake()
在脚本被载入的时候调用,只调用一次,常用于初始化游戏状态、获取组件;OnEnable()
在物体被激活的时候调用,可以调用多次;Start()
,在Awake()
之后,开始游戏的时候调用一次,常用于变量的初始化。
物理阶段常用的生命周期函数有FixedUpdate()
,FixedUpdate()
函数是基于C#提供的Timer类以固定时间间隔执行的函数,在每个固定时间间隔中都一定会被调用一次,即使在性能上出现卡顿的情况下,也保证了场景物理运动的准确性。这个固定时间间隔由物理引擎决定,通常是0.02秒(即50次每秒),碰撞检测触发检测等函数也是在物理阶段被调用的。
输入事件阶段OnMouseEnter()
OnMouseOver()
OnMouseExit()
等函数只会在带有输入检测能力的游戏对象上调用,这个时间点物理更新已经执行(如果需要物理更新的话),而逻辑更新和渲染并未执行。
逻辑阶段常用的生命周期函数有Update()
、LateUpdate()。
Update()
是真正的每帧调用,按照每一帧刷新率(FPS)执行的,但是由于系统性能以及游戏体量的区别,每一帧的刷新频率也是不同的,所以为了保证游戏场景的准确性,每帧的物理循环可能会发生不止一次;LateUpdate()
每帧Update()
方法调用之后会调用本方法,主要用于游戏逻辑的二次计算,比如相机跟随等。
之后就是渲染阶段,初学阶段常用的有OnDrawGizmos()
画辅助线的函数,其他还有OnRenderObject()
在场景渲染时被调用的函数等。
然后是游戏的暂停阶段和退出阶段,暂停阶段函数OnApplicationPause()
应用暂停时会调用此方法,取消暂停后会调用OnApplicationFocus()
函数,然后从FixedUpdate开始重新执行。退出阶段有OnDestroy()
当物体被销毁时调用,一般用于清理内存。OnApplicationQuit()
(应用退出)当应用退出时调用,但有时会失效,此方法为不稳定的方法。