Unity中的MonoBehaviour及生命周期

本文详细解析了Unity引擎中MonoBehaviour类的作用及生命周期函数,包括Reset、Awake、Start、Update等,阐述了它们的执行时机及应用场景,并对比了继承与非继承MonoBehaviour类的区别。

一:前言

从第一次在unity创建C#脚本开始就会发现每个脚本都继承了一个叫MonoBehviour的类,那么MonoBehaviour是做什么的呢?

一层层进入MonoBehaviour父类,可以看出MonoBehaviour间接继承了Component,所以继承自MonoBehaviour脚本的作用其中之一就是充当组件的角色,注意当我们需要将一个自定义脚本以组件的形式添加到对应的GameObject时(Unity中Inspector面板中的每一项都称为组件,包括创建的脚本),该脚本是必须要继承MonoBehaviour,否则无法添加为组件


二:生命周期函数

生命周期函数可以分为两种状态:
——编辑器下的状态:只有Reset函数一个
——运行状态(真正的生命周期):除Reset函数外,其余函数都在运行状态执行(加特殊字段,也可以在编辑器运行)

——Reset
Reset:只能在编辑器模式下运行,点击Reset按钮和第一次将脚本挂载到游戏物体上时执行


——Awake,OnEnable,Start

Awake:脚本第一次加载或所挂载的游戏物体第一次激活时(只依赖于所挂载的游戏物体是否激活),一个生命周期内只执行一次
OnEnable:在脚本组件或脚本组件所挂载的游戏物体每次被激活时都会被调用,一个生命周期内执行多次
Start:当游戏物体处于激活状态并且脚本也处于激活状态才可以执行,一个生命周期内只执行一次

在Inspector面板中的组件上有一个复选框,这是在脚本中有生命周期函数时才会显示的,但是测试后发现只有Awake和Reset并不会出现复选框,因为这两个函数与脚本是否激活无关

 ——FixedUpdate,Update,LateUpdate
FixedUpdate:每帧调用而且每帧的时间间隔是固定的,不受帧率影响。每一帧的时间是在Edit->Project Settings->Time面板中的Fixed Timestep设置,默认为0.02s,每秒执行50次,对于更新频率比较稳定的物理系统来说就合适放在FixedUpdate中处理
Update:每帧调用但是每帧的时间间隔不固定,受到帧率影响,与电脑性能有关。例如FPS是30,则每秒执行30帧,如果FPS是10,则每秒执行10帧。是不稳定的
LateUpdate:与Update一样每帧之间的时间是不固定的,受到帧率影响,它是三种Update中最后一个执行的。相机的跟随一般在LateUpdate中实现,这样可以确保在人物位置改变后再进行相机位置的改变


OnApplicationQuit,OnDisable,OnDestroy

OnApplicationQuit:当你的应用程序退出时,会先调用OnApplicationQuit
OnDisable:在脚本组件或脚本组件所挂载的游戏物体每次被禁用时都会被调用,一个生命周期内执行多次
OnDestory:销毁脚本组件或脚本所挂载的游戏物体时调用


三:普通类与继承MonoBehaviour类的区别

——继承MonoBehaviour的类不需要创建它的实例,类内部也不要有构造函数或析构函数,因为MonoBehaviour类中有自己的构造函数做一些引擎内的事情,所以不提倡使用new关键字调用其构造函数
如果用new构造会出现以下警告:

警告说的很明显,就是如果继承了MonoBehaviour就不允许你用new创建,你可以用添加组件的方式替代

——不继承MonoBehaviour不能使用Invoke,Coroutine,print以及生命周期函数等
——不继承MonoBehaviour不能挂在到Inspector上,也就是说不能当组件使用


四:一些成员方法

——GetComponentInChildren:只要脚本所挂载的游戏物体是激活的就可以查找到,无论脚本本身是否激活


五:生命周期函数总结

——Editor中:Reset
——初始化阶段:Awake—OnEnable—Start
——物理计算阶段:FixedUpdate—动画机更新—内部物理更新(刚体等)—OnTriggerXXX、OnColliisionXXX—yield WaitForFixedUpdate
——输入检测阶段:OnMouseXXX
——游戏逻辑阶段:Update—yield xxx—LateUpdate
——场景渲染阶段:OnWillRenderObject—OnPreCull—OnBecameVisible—OnBecameInvisible—OnPreRender—OnPostRender—OnRenderImage
——自创工具阶段:OnGrawGizmos
——GUI阶段:OnGUI
——帧结束:yield WaitForEndOfFrame
——结束阶段:OnDisable—OnDestroy


六:Unity中是如何调用MonoBehaviour生命周期方法的

打开MonoBehaviour类,你会发现并没有Awake、Start等方法,而继承MonoBehaviour的类也没有重写这些方法,就算是私有的也可以被调用
Unity内部运用了反射去调用这些生命周期函数,运行时通过反射将所有继承MonoBehaviour的类中的生命周期函数找出来,维护一套列表

<think>嗯,用户想要了解在Unity中设置MonoBehaviour生命周期事件的最佳实践,并需要代码示例。首先,我需要回顾一下Unity生命周期函数,比如Awake、Start、Update这些的顺序和用途。根据引用[1]里的信息,调用顺序是Awake -> OnEnable -> Start,然后是Update、FixedUpdate、LateUpdate等。这些函数的正确使用对于代码的高效运行很重要。 用户需要最佳实践的代码示例,所以我应该分不同阶段来讲解。比如,在初始化阶段,Awake适合用于组件引用和变量初始化,而Start则适合处理依赖其他组件的逻辑。比如在Awake里获取组件,然后在Start里进行需要这些组件的操作。 然后,更新阶段有Update、FixedUpdate和LateUpdate。FixedUpdate用于物理相关的更新,而LateUpdate可能在摄像机跟随中使用,确保在所有Update之后执行。需要举例说明,比如在LateUpdate中更新摄像机位置。 事件响应阶段,比如OnEnable和OnDisable,可以用于注册和注销事件,避免内存泄漏。例如,在OnEnable里订阅事件,OnDisable里取消订阅。 销毁阶段,OnDestroy用于资源释放,比如停止协程或断开网络连接。这里要提醒用户注意资源管理,避免泄露。 另外,用户可能还会关心如何选择正确的生命周期函数,如引用[1]提到的第4点。所以需要给出一些实践建议,比如避免在Update中进行昂贵操作,区分Awake和Start的使用场景等。 代码示例方面,要确保每个函数都有对应的例子,比如在Awake里获取组件,在Start里初始化依赖,在OnEnable里注册事件。同时,代码格式要正确,使用$$...$$来包裹独立公式,但这里可能更多是代码块,所以用三个反引号包裹。 最后,生成相关问题,比如如何优化生命周期函数的使用,处理脚本执行顺序等,这样用户可以有进一步了解的途径。需要确保引用标识正确,比如引用[1]、[2]、[3]对应的内容。 检查是否覆盖了所有生命周期阶段,并提供了实际的代码示例,确保最佳实践的建议明确,比如变量初始化放在Awake,依赖处理在Start,事件处理在OnEnable/OnDisable。同时提醒避免常见错误,如在Update中频繁实例化对象。 总结起来,结构应该是分阶段讲解,每个阶段给出最佳实践和代码示例,然后给出注意事项和相关问题。这样用户能清晰地理解如何正确使用各个生命周期函数。</think>### UnityMonoBehaviour生命周期事件最佳实践代码示例 #### 1. 初始化阶段 ```csharp using UnityEngine; public class LifecycleDemo : MonoBehaviour { private Rigidbody _rb; private bool _isInitialized; // Awake:用于初始化独立变量和组件引用 private void Awake() { _rb = GetComponent<Rigidbody>(); // 获取组件引用 Debug.Log("Awake: 组件初始化完成"); } // OnEnable:激活时执行(可重复触发) private void OnEnable() { EventManager.OnGameStart += HandleGameStart; // 事件订阅 Debug.Log("OnEnable: 已注册游戏开始事件"); } // Start:初始化依赖其他组件的逻辑 private void Start() { if (_rb != null) _rb.mass = 5f; // 依赖物理组件的设置 _isInitialized = true; Debug.Log("Start: 初始化完成"); } } ``` **最佳实践**: - 在$Awake()$中获取组件引用和初始化独立变量[^3] - 在$Start()$中执行需要其他组件完成初始化的操作[^1] - 通过$OnEnable()$进行事件订阅,避免重复注册[^2] --- #### 2. 更新阶段 ```csharp // FixedUpdate:物理相关更新(固定时间步长) private void FixedUpdate() { if (_isInitialized) { _rb.AddForce(Vector3.up * 0.5f); // 物理引擎相关操作 } } // Update:常规游戏逻辑更新 private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { FireProjectile(); // 输入响应 } } // LateUpdate:摄像机跟随等后期处理 private void LateUpdate() { UpdateCameraPosition(); // 保证在角色移动后更新 } ``` **最佳实践**: - 物理操作严格放在$FixedUpdate()$[^1] - 输入检测放在$Update()$[^2] - 需要延迟执行的逻辑使用$LateUpdate()$[^3] --- #### 3. 事件响应阶段 ```csharp // OnDisable:对象禁用时执行 private void OnDisable() { EventManager.OnGameStart -= HandleGameStart; // 取消事件订阅 Debug.Log("OnDisable: 已注销游戏开始事件"); } // OnDestroy:对象销毁时执行 private void OnDestroy() { StopAllCoroutines(); // 停止协程 Debug.Log("OnDestroy: 资源已释放"); } ``` **最佳实践**: - 必须成对实现$OnEnable()$/$OnDisable()$的事件管理[^1] - 在$OnDestroy()$中释放非托管资源[^3] --- #### 4. 高级实践(协程管理) ```csharp private Coroutine _damageCoroutine; private void TakeDamage() { if (_damageCoroutine != null) StopCoroutine(_damageCoroutine); _damageCoroutine = StartCoroutine(DamageEffect()); } private IEnumerator DamageEffect() { // 实现伤害效果逻辑 yield return new WaitForSeconds(1f); } ``` **最佳实践**: - 使用协程引用控制长时间运行的任务 - 在对象禁用时自动停止协程 --- ### 注意事项 1. 避免在$Update()$中执行昂贵操作(如$GameObject.Find$) 2. 脚本执行顺序可通过`Edit > Project Settings > Script Execution Order`调整 3. 使用$[RequireComponent(typeof(Rigidbody))]$属性确保组件依赖
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello Bug.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值