一文读懂Unity常用生命周期函数! 超级详细不服来辩~

Posted 陈言必行

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文读懂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: 鼠标抬起时调用一次

实际使用:使用时一般都是成对使用

  1. OnMouseEnter,OnMouseOver,OnMouseExit 一组。比如模拟选中状态:鼠标进入时物体变色,鼠标退出时再变回来。
  2. OnMouseDown,OnMouseDrag,OnMouseUp 一组。比如射击游戏:鼠标按下拖拽时调整方向,抬起时发射子弹。
  3. 当鼠标按下并停留在当前游戏对象上时,OnMouseOver,OnMouseDrag会同时触发。

检测原理

  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 官方图解:
图解

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

以上是关于一文读懂Unity常用生命周期函数! 超级详细不服来辩~的主要内容,如果未能解决你的问题,请参考以下文章

一文读懂 Spring Bean 的生命周期

一文读懂 Spring Bean 的生命周期

一文读懂Java类加载机制

深度学习一文读懂机器学习常用损失函数(Loss Function)

超级干货 :一文读懂数据可视化

一文读懂简单查询代价估算