写点什么

Unity 常用生命周期函数解析 - 超级详细,不服来辩

作者:陈言必行
  • 2023-04-21
    辽宁
  • 本文字数:4565 字

    阅读完需:约 15 分钟

Unity常用生命周期函数解析 - 超级详细,不服来辩

一,初始化

1.1 函数描述

运行状态初始化:(也是执行顺序)


  1. Awake:初始化时调用,在 Start 函数之前调用

  2. OnEnable:在对象启用时调用

  3. Start:仅当启用脚本实例时,才会在第一帧调用


编辑器状态初始化


  1. Reset:编辑器下调用,当脚本第一次附加到物体上或者点击 Reset 时执行,来初始化脚本属性。


实际应用


  1. Awake:通常使用为需要提前初始化的逻辑。比如单例赋值private void Awake(){Instance = this;}

  2. OnEnable:处理每次显示时都需要进行初始化的逻辑,通常和 OnDisable 配合使用。比如: 游戏逻辑使用并修改了变量Number,而下次显示时使用是又需要Number = 1,此时就可以写private void Awake(){Number = 1;}

  3. Start:通常使用为一些变量初始化逻辑。比如:获取指定物体:private void Start(){child1 = transform.GetChild(0);}

  4. Reset:通常在游戏测试调试时,使用编辑器下的初始化。通过点击 Reset 执行一个逻辑。



1.2 示例解析

运行状态初始化示例:  搭建场景新建两个 UI -> Image,一个作为背景,一个作为弹窗。新建脚本并挂载到弹窗 Image 上。


脚本内容如下:


using UnityEngine;
public class LifeCycleFunction : MonoBehaviour{ #region Initialization 初始化 public int Number = 1; private void Awake() { Debug.Log("Awake 初始化时调用,在Start函数之前调用,..."); } private void OnEnable() { Debug.Log("OnEnable 每次对象启用都会被调用..."); } private void Reset() { Number = 0; Debug.Log("Reset 编辑器模式下才可被调用...Number: " + Number); } private void Start() { Debug.Log("Start 仅当启用脚本实例时,才会在第一帧调用..."); } #endregion}
复制代码


此次运行结果展示了Awake,OnEnable,Start 的执行顺序,运行结果:



编辑器下的初始化:1.脚本拖拽附加时自动执行



2.点击 Reset 调用: 可以看到我手动 Number 设置为 2,Reset 后被重置为代码中写的 0;




二,更新

2.1 函数描述

三个函数


  1. FixedUpdate:固定时间调用,FixedUpdate 通常比 Update 更频繁地调用

  2. Update:每帧调用一次

  3. LateUpdate:在 Update 完成后,每帧调用一次


实际应用


  1. FixedUpdate:所有物理计算和更新都在 FixedUpdate 中处理。它是固定时间调用,不会受到帧率影响。比如:一些物理属性的更新操作 Force,Collider,Rigidbody 等。

  2. Update:每帧调用一次,根据帧率的快慢影响执行速度。通常的游戏逻辑都写在这里,比如:和玩家交换,当用户按下空格时进行执行什么操作。

  3. LateUpdate:每次 Update 完成后调用移除。常见用处是相机跟随主角,比如:主角在 Update 中移动,则可以在 LateUpdate 执行相机的移动,这将可以保证摄像机跟着的时候之前的逻辑一起完全执行完成。



2.2 示例解析

将如下脚本添加到上面新建的脚本中:(为了方便查看,将上面初始化的函数先注释掉)


#region 更新
// 固定时间调用,FixedUpdate通常比Update更频繁地调用private void FixedUpdate(){ Debug.Log("FixedUpdate 固定时间调用...");}// 每帧调用一次private void Update(){ Debug.Log("Update 每帧调用一次...");}// 在Update后,每帧调用一次 private void LateUpdate(){ Debug.Log("LateUpdate 在Update后,每帧调用一次...");}#endregion
复制代码


运行后,可以看到结果如下:




三,鼠标交互

3.1 函数描述

几个函数


  1. OnMouseEnter: 鼠标进入时调用一次

  2. OnMouseOver: 鼠标停留(经过)时一直调用

  3. OnMouseExit: 鼠标退出时调用一次

  4. OnMouseDown: 鼠标按下时调用一次

  5. OnMouseDrag: 鼠标拖拽(按住)时一直调用

  6. OnMouseUp: 鼠标抬起时调用一次


<font size=3>实际使用:使用时一般都是成对使用


  1. OnMouseEnter,OnMouseOver,OnMouseExit 一组。比如模拟选中状态:鼠标进入时物体变色,鼠标退出时再变回来。

  2. OnMouseDown,OnMouseDrag,OnMouseUp 一组。比如射击游戏:鼠标按下拖拽时调整方向,抬起时发射子弹。

  3. 当鼠标按下并停留在当前游戏对象上时,OnMouseOver,OnMouseDrag 会同时触发。


<font size=3>检测原理


  1. 只能检测当前脚本挂载的游戏对象。

  2. 当前游戏对象需要有碰撞体。

  3. 不能有其他物体(UI)遮挡到此游戏对象。


  • 总结为一局话就是:OnMouseXXX 的原理是通过鼠标的射线检测来判断鼠标当前位置是否碰到了挂载脚本游戏对象的碰撞体。


勾选 IsTrigger:


  若需要不检测勾选 IsTrigger 的碰撞体,Edit => Project Settings => Physics 中的 Queries Hit Triggers,将这个✅ 取消,即可不触发勾选 IsTrigger 的。【注意:默认是✅ 勾选状态,不需要触发则取消勾选】




3.2 示例解析

  场景中创建一个 Cube,将其位置调整在摄像机先显示即可【示例中调整位置(0,0,-1),缩放为(3,3,3)】。


测试功能:


  • 鼠标进入\退出,触发 Cube 颜色变化

  • 鼠标移动到 Cube 上,触发旋转

  • 鼠标在 Cube 按下并拖拽,触发 Cube 跟随移动

  • 鼠标抬起 Cube 回归到原来位置


创建并挂载到 Cube 代码如下:


using UnityEngine;
public class OnMouseXXXTest : MonoBehaviour{ #region 鼠标相关
// 鼠标和Collider(碰撞体)之间的触发 // 鼠标进入 private void OnMouseEnter() { Debug.Log("OnMouseEnter 鼠标进入..."); GetComponent<MeshRenderer>().material.color = Color.blue; } // 鼠标停留 private void OnMouseOver() { transform.Rotate(Vector3.up * 50 * Time.deltaTime); }
// 鼠标退出 private void OnMouseExit() { Debug.Log("OnMouseExit 鼠标退出..."); GetComponent<MeshRenderer>().material.color = Color.green; }
// 鼠标按下 private void OnMouseDown() { Debug.Log("OnMouseDown 鼠标按下..."); }
// 鼠标拖动 private void OnMouseDrag() { Vector3 target = Input.mousePosition; target.z = Mathf.Abs(Camera.main.transform.position.z); transform.localPosition = Camera.main.ScreenToWorldPoint(target); }
// 鼠标抬起 private void OnMouseUp() { Debug.Log("OnMouseUp 鼠标抬起..."); transform.localPosition = new Vector3(0, 0, -1); }
#endregion}
复制代码


测试效果:




四,碰撞检测

4.1 函数描述

碰撞函数


  1. OnCollisionEnter: 进入碰撞时触发一次。

  2. OnCollisionStay: 在碰撞体中停留时每帧触发一次。

  3. OnCollisionExit: 离开碰撞体时触发一次。


触发函数


  1. OnTriggerEnter: 进入碰撞体时触发一次。

  2. OnTriggerStay: 在碰撞体中停留时每帧触发一次。

  3. OnTriggerExit: 离开碰撞体是触发一次。


PS:上面这六个方法,还有对应 2D 碰撞体的六个方法(如:OnCollisionEnter2D) 函数后面添加 2D 接口,触发条件和使用方式和 3D 一致。 使用时注意碰撞体和检测函数同步接口,即用 2D 碰撞体必须用 2D 函数。


函数执行条件


  1. 两个物体需要都有碰撞体(Collider)组件。

  2. 检测方(挂载脚本物体)需要有刚体(Rigidbody)组件。

  3. Collider 上都不勾选 IsTrigger(有一方勾选则执行触发函数)。



4.2 示例解析

在三例上继续操作,在 Cube 上添加 Rigidbody 组件,并取消勾选 Use Gravity 属性(避免其受到重力影响)。



然后再创建两个 Cube,位置大小随意能在 Game 视图看到就行。将其中一个 BoxCollider 的 IsTrigger 属性勾选 ✅ 上。这样就可以一个用来测试碰撞,一个用来测试触发了。


将代码添加到OnMouseXXXTest类中,添加内容如下:


#region 碰撞,触发检测
// 在方法名后加上2D(如:OnCollisionEnter2D), 即可触发2D碰撞体对应函数// 进入碰撞时触发private void OnCollisionEnter(Collision other){ Debug.Log("开始碰撞..." + other.collider.gameObject.name); other.collider.GetComponent<MeshRenderer>().material.color = Color.black;}
// 在碰撞体中停留时每帧触发一次private void OnCollisionStay(Collision other){ Debug.Log("持续碰撞中..." + other.collider.gameObject.name); other.collider.GetComponent<MeshRenderer>().material.color = Color.red;}
// 离开碰撞体时触发private void OnCollisionExit(Collision other){ Debug.Log("碰撞结束..." + other.collider.gameObject.name); other.collider.GetComponent<MeshRenderer>().material.color = Color.white;}
// 碰撞体勾选is Trigger 选项: 取消碰撞器,开启触发器// 进入碰撞体时触发private void OnTriggerEnter(Collider other){ Debug.Log("触发开始..."); other.transform.GetComponent<MeshRenderer>().material.color = Color.black;}
// 在碰撞体中触发private void OnTriggerStay(Collider other){ Debug.Log("持续触发中..."); other.transform.GetComponent<MeshRenderer>().material.color = Color.yellow;}
// 离开碰撞体是触发private void OnTriggerExit(Collider other){ Debug.Log("触发结束..."); other.transform.GetComponent<MeshRenderer>().material.color = Color.white;}#endregion
复制代码


运行后得到如下效果:




五,应用程序

5.1 函数描述

三个函数


  1. OnApplicationPause: 检测到暂停的帧结束 --> 切换到后台和回来时调用。

  2. OnApplicationFocus: 当屏幕 获得/失去 焦点时调用

  3. OnApplicationQuit: 当程序退出时调用。


实际应用


  1. OnApplicationPause: 游戏停止保存数据/游戏继续数据初始化。

  2. OnApplicationFocus: 失去焦点关闭背景音乐/获得焦点继续播放音乐。

  3. OnApplicationQuit: 在移动端大退时也会对调用,但不会触发上面两个方法。



5.2 示例解析

使用时将下面代码复制到需要处理检测逻辑的部分即可:


#region Application 应用程序
// 检测到暂停的帧结束时调用,有效地在正常帧更新之间private void OnApplicationPause(bool pauseStatus){ Debug.Log("OnApplicationPause ... " + pauseStatus); if (pauseStatus) // 切换到后台时执行 { } else // 切换到前台执行一次 { }}
// 当屏幕 获得/失去 焦点时调用private void OnApplicationFocus(bool hasFocus){ Debug.Log("OnApplicationFocus ... " + hasFocus); if (hasFocus) // 获得焦点 -- 切换到前台 { } else // 失去焦点 { }}
// 当程序退出时调用 -- Application.Quit();触发,不会触发上面两个方法private void OnApplicationQuit(){ Debug.Log("OnApplicationQuit ... ");}
复制代码


由下图可以看到执行逻辑:当我点击 Hierarchy 面板时触发失去焦点,再次点击 Game 视图则触发了获得焦点




六,禁用销毁

6.1 函数描述

三个函数


  1. OnDisable: 当对象被禁用时调用此函数(其父物体被禁用也会触发)。

  2. OnDestroy: 在对象存在的最后一帧的所有帧更新之后调用此函数。


实际应用


  1. OnDisable: 通常和 OnEnable 配合使用。比如:在 OnEnable 添加监听,在 OnDisable 移除监听

  2. OnDestroy: 当物体销毁或者场景关闭时触发。比如:子弹打到墙壁时,需要销毁子弹并触发一个打击音效。



6.2 示例解析

1.游戏物体禁用触发OnDisable



2.游戏物体销毁时触发OnDisableOnDestroy





Unity 官方图解:



怎么样?是不是收获颇丰。本文对你有帮助的话,欢迎小伙伴们三连支持一下。有问题的话我们评论见吧~

发布于: 刚刚阅读数: 2
用户头像

陈言必行

关注

公号:开发同学留步 2019-03-12 加入

我是一个从事Unity游戏开发攻城狮,InfoQ&阿里云签约博主,CSDN博客专家,U3D论坛版主,6年开发经验,助你日常不加班。⽂章皆为从零到⼀的⼊⻔级教程,也有很多⼯作中遇到的问题解析。

评论

发布
暂无评论
Unity常用生命周期函数解析 - 超级详细,不服来辩_Unity_陈言必行_InfoQ写作社区