游戏脚本和MonoBehaviour生命周期

本文详细介绍了Unity中脚本的生命周期,包括Awake、Start、Update等关键函数的作用及调用时机,并通过示例展示了如何利用这些函数进行游戏逻辑开发。此外还介绍了Unity中的调试方法及GUI系统的使用。
一.游戏脚本

1.双击Project视图中, Script目录下的GameManager脚本


2.使用MonoDevelop编辑器打开


3.所有创建的用于添加到游戏对象上的脚本都必须继承自MonoBehaviour。
二.调式
1.我们可以在脚本里输出调试信息, 并在控制台中显示。
2.接口
(1).Debug.Log:向控制台输出普通信息(白色)。
(2).Debug.LogWarning:向控制台输出警告信息(黄色)。
(3).Debug.LogError:向控制台输出错误信息(红色)。
三.MonoBehaviour生命周期
1.Awake(), 脚本唤醒函数。当游戏对象被创建的时候, 游戏对象绑定的脚本会在该帧内执行Awake()函数, 无论脚本是否处于激活状态。
2.Start(), 该函数在脚本被激活的时候执行, 位于Awake()函数之后。该函数的执行同样也是在游戏对象被创建的帧里。不同的是, 如果脚本处于不激活状态, Start()函数是不会被执行的。
3.Update(), 只有处于激活状态下的脚本, 都会在每一帧里调用Update()函数, 该函数也是最为常用的一个函数, 用来更新逻辑。
4.LateUpdate(), 该函数是延迟更新函数, 处于激活状态下的脚本在每一帧里都会在Update()函数执行后调用该函数, 通常用来调整代码执行的顺序。比如玩家的角色需要一个摄像机来跟随, 那么通常角色的移动逻辑会写在Update()里, 而摄像机的跟随在LateUpdate()里, 这样可以确保在角色的位置计算完毕后, 再根据角色位置确定摄像机的位置和视角。

5.FixedUpdate(), 该函数用于固定更新。在游戏运行的过程中, 每一帧的处理时间是不固定的, 当我们需要固定间隔时间执行某些代码时, 就会用到FixedUpdate()函数。在菜单栏中, 点击”Edit” -> “Project Settings” -> “Time”菜单项, 之后在Inspector视图里出现时间管理器, 其中”Fixed Timestep”选项用于设置FixeUpdate()的更新帧率, 更新帧率默认是0.02秒。固定更新常用于移动物体等操作, 因为固定更新每一帧调用的时间间隔是一样的, 所以移动速度是匀速的。


6.OnGUI(), 绘制界面函数。Unity使用最新的UGUI界面系统来创建页面, 所以OnGUI()一般作为测试功能使用, 如创建测试按钮。
7.OnDestroy(), 在当前脚本销毁时调用该函数, 可以在该函数里填写删除时需要处理的逻辑。
8. OnEnable(), 激活函数, 当脚本处于激活时调用。
9. OnDisable(), 当脚本被禁用时调用。

10.所有继承自MonoBehaviour的脚本都有一个名为enabled的bool属性值开关。enabled对应脚本名称左侧的勾, 如我们创建的GameManager脚本:


(1).enabled决定生命周期的函数是否会被调用。当enabled为true时, 生命周期各个阶段对应的函数将会被调用, 否则不调用, Awake和OnDisable函数除外。当enabled变为true时脚本执行OnEnable(), 当enabled变为false时脚本执行OnDisable()。
(2).enabled只是与生命周期的函数有关, 与其他函数和所有变量都没有关系。如果脚本中不保护任何生命周期函数, enabled将没有任何意义。

11.生命周期代码

using UnityEngine;

public class GameManager : MonoBehaviour
{
	private bool hasPrintUpdate = false;
	private bool hasPrintLateUpdate = false;
	private bool hasPrintFixedUpdate = false;
	private bool hasPrintGUI = false;

	void Awake()
	{
		Debug.Log ("---Awake---");
	}

	void OnEnable()
	{
		Debug.Log ("---OnEnable---");
	}

	void Start () 
	{
		Debug.Log ("---Start---");
	}

	void FixedUpdate()
	{
		if (!hasPrintFixedUpdate)
		{
			hasPrintFixedUpdate = true;
			Debug.Log ("---FixedUpdate---");
		}
	}

	void Update ()
	{
		if(!hasPrintUpdate)
		{
			hasPrintUpdate = true;
			Debug.Log ("---Update---");
		}
	}

	void LateUpdate()
	{
		if (!hasPrintLateUpdate) 
		{
			hasPrintLateUpdate = true;
			Debug.Log ("---LateUpdate---");
		}
	}

	void OnGUI()
	{
		if (!hasPrintGUI) 
		{
			hasPrintGUI = true;
			Debug.Log ("---OnGUI---");
		}
	}

	void OnDisable()
	{
		Debug.Log ("---OnDisable---");
	}

	void OnDestroy()
	{
		Debug.Log ("---OnDestroy---");
	}
		
}
12.绑定GameManager脚本到Cube游戏对象上
(1).在Hierarchy视图中选中Cube游戏对象

(2).在Project视图中,鼠标左键拖拽点GameManager脚本到Inspector视图中


(3).运行脚本,控制台打印


四.UnityGUI的使用
1.UnityGUI使用一个特殊的OnGUI()函数, 在该函数中加入实现UI的脚本。
2.UnityGUI的接口
(1).GUI.xxx(), 需要自己手动填写处于屏幕上的位置。
(2).GUILayout.xxx(),unity会自己排版。
3.UnityGUI测试函数
(1).GUILayout.Label (sting str), 标签用于显示文本。输入的参数为需要显示的文本。
(2).GUILayout.TextField (sting str), 按钮用于出发事件。输入的参数为在按钮上显示的文字。它返回一个bool值, 表示是否按下了按钮。
(3).GUILayout.Button(sting str), 文本区域用于输入文本。输入的参数为显示的文本, 返回经过用户输入改动后的文本。一般用参数变量接收返回值即可。

4.UseGUI实例

using UnityEngine;

public class UseGUI : MonoBehaviour
{
	private string text = "";
	private string myName = "";

	void OnGUI()
	{
		GUILayout.Label ("please input your name:");
		text = GUILayout.TextField (text);

		if(GUILayout.Button("submit"))
		{
			myName = text;
			text = string.Empty;
		}

		if(!string.IsNullOrEmpty(myName))
		{
			GUILayout.Label ("submit name success, name: " + myName);
		}
	}
}
5.绑定GameManager脚本到Cube游戏对象上
(1).在Hierarchy视图中选中Cube游戏对象

(2).在Inspector视图中, 点击Add Component按钮, 搜索UseGUI脚本进行绑定


(3).运行测试


<think>嗯,用户想要了解在Unity中设置MonoBehaviour生命周期事件的最佳实践,并需要代码示例。首先,我需要回顾一下Unity的生命周期函数,比如Awake、Start、Update这些的顺序用途。根据引用[1]里的信息,调用顺序是Awake -> OnEnable -> Start,然后是Update、FixedUpdate、LateUpdate等。这些函数的正确使用对于代码的高效运行很重要。 用户需要最佳实践的代码示例,所以我应该分不同阶段来讲解。比如,在初始化阶段,Awake适合用于组件引用变量初始化,而Start则适合处理依赖其他组件的逻辑。比如在Awake里获取组件,然后在Start里进行需要这些组件的操作。 然后,更新阶段有Update、FixedUpdateLateUpdate。FixedUpdate用于物理相关的更新,而LateUpdate可能在摄像机跟随中使用,确保在所有Update之后执行。需要举例说明,比如在LateUpdate中更新摄像机位置。 事件响应阶段,比如OnEnableOnDisable,可以用于注册注销事件,避免内存泄漏。例如,在OnEnable里订阅事件,OnDisable里取消订阅。 销毁阶段,OnDestroy用于资源释放,比如停止协程或断开网络连接。这里要提醒用户注意资源管理,避免泄露。 另外,用户可能还会关心如何选择正确的生命周期函数,如引用[1]提到的第4点。所以需要给出一些实践建议,比如避免在Update中进行昂贵操作,区分AwakeStart的使用场景等。 代码示例方面,要确保每个函数都有对应的例子,比如在Awake里获取组件,在Start里初始化依赖,在OnEnable里注册事件。同时,代码格式要正确,使用$$...$$来包裹独立公式,但这里可能更多是代码块,所以用三个反引号包裹。 最后,生成相关问题,比如如何优化生命周期函数的使用,处理脚本执行顺序等,这样用户可以有进一步了解的途径。需要确保引用标识正确,比如引用[1]、[2]、[3]对应的内容。 检查是否覆盖了所有生命周期阶段,并提供了实际的代码示例,确保最佳实践的建议明确,比如变量初始化放在Awake,依赖处理在Start,事件处理在OnEnable/OnDisable。同时提醒避免常见错误,如在Update中频繁实例化对象。 总结起来,结构应该是分阶段讲解,每个阶段给出最佳实践代码示例,然后给出注意事项相关问题。这样用户能清晰地理解如何正确使用各个生命周期函数。</think>### Unity中MonoBehaviour生命周期事件最佳实践代码示例 #### 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))]$属性确保组件依赖
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值