StopAllCoroutines二三事

刚学习Unity的时候,总会不时看到文章说合理使用协程(Coroutine)能更好实现一些功能。今天不说怎么使用协程,相信网上已经有非常多的资料了,重点分析的目标是当初从未上心的一个函数StopAllCoroutines。

1.函数意义

第一眼看到函数名的时候,很有迷惑性。Stop All?!乖乖,这个函数有点霸道啊,直接就停掉所有的协程,这岂不是会停掉一些我不想停掉的协程呢?算了,这种接口还是少用为妙。不过后来仔细想想,Unity这么成熟一个商业引擎,做设计的工程师不至于这么无聊弄一些意义不大的接口吧。仔细看了看官方接口描述:

这些能够解释清楚了,只是停掉当前behaviour上的所有协程,不会影响到其他物体。做个测试,创建一个C#文件,名字叫Coroutine1,代码如下:

using UnityEngine;
using System.Collections;

public class Coroutine1 : MonoBehaviour {

	// Use this for initialization
	void Start () {
		StartCoroutine (Cor1 ());
		StartCoroutine (Cor2 ());
	}
	
	// Update is called once per frame
	void Update () {
		if(Input.GetKeyDown(KeyCode.Alpha1))
		   StopAllCoroutines();
	}

	IEnumerator Cor1(){
		Debug.LogError ("Start Cor1");
		int i = 0;
		while (true) {
			Debug.LogError ("Cor1 frame " + i);
			i++;
			yield return new WaitForSeconds(0.5f);
		}
		Debug.LogError ("end Cor1");
	}

	IEnumerator Cor2(){
		Debug.LogError ("Start Cor2");
			int i = 0;
			while (true) {
				Debug.LogError ("Cor2 frame " + i);
				i++;
				yield return new WaitForSeconds(0.5f);
			}
		Debug.LogError ("end Cor2");
	}
}

讲脚本随意挂到一个GameObject上,运行程序,可以看到Cor1和Cor2函数中while里面的日志交替输出。按下键盘上数字1后,停止输出日志,说明StopAllCoroutines确实停止了当前脚本上的所有协程。至于它真的会不会停掉所有物体上的所有协程,我不想贴代码,大家自己写个脚本去验证吧。

2.What is this behaviour?

搞清楚了函数的大致应用情况,但是仍有一丝疑惑。

回到接口本身,按照接口描述,是停止this behaviour上所有的协程,这个behaviour指的到底是挂载了我们所写脚本的GameObject上的这一个Coroutine1的实例,还是指场景里面所有的Coroutine1 实例呢?再做一个测试,新建一个脚本Coroutine2,代码如下:

using UnityEngine;
using System.Collections;

public class Coroutine2 : MonoBehaviour {

	// Use this for initialization
	void Start () {
		StartCoroutine (Cor ());
	}
	
	// Update is called once per frame
	void Update () {
		if (Input.GetKeyDown (KeyCode.Alpha1) && name == "Cube1") {
			StopAllCoroutines();
		}
	}

	IEnumerator Cor(){
		Debug.LogError ("Start Cor " + name);
		while (true) {
			Debug.LogError("Update Cor " + name);
			yield return new WaitForSeconds(0.5f);
		}
	}
}

在场景中创建2个GameObject “Cube1” 和 “Cube2”,再为两个物体分别挂载Coroutine2脚本。为了方便,代码中直接使用了物体名字,而没有通过其他方式来加以区分。

运行游戏可以看到仍然是两条日志交替输出。当按下数字键1后,日志不再输出“Start cor 1”,说明Cube1上脚本里面启动的协程已经被停止了,而Cube2上的协程还在继续运行。

很显然,输出结果已经告诉了我们答案。StopAllCoroutines只会停止调用这个接口的脚本实例对象上的所有协程,而非调用这个接口脚本的所有实例对象。

结语

可能有人会说,这个接口描述一看就知道结果是这样啊,还需要这样写脚本来验证?!请原谅一个强迫症患者偶尔咬文嚼字想要对于疑惑进行验证的心情。与其懵懵懂懂前进,不如怀着喜悦心情去寻究真相,这个过程本来就是满怀乐趣呢。

### 协程无响应的原因分析 在开发过程中,如果遇到协程无法正常工作的情况,通常可能是由于以下几个原因引起的: #### 1. **对象生命周期管理不当** 当挂载有协程的对象被频繁启用或禁用时,可能会导致协程中断。例如,在 Unity 中,一旦游戏对象被禁用 (`gameObject.SetActive(false)`),其上的所有协程都会停止运行[^4]。 #### 2. **启动逻辑错误** 如果协程的启动条件不满足或者存在逻辑错误,则可能导致协程未能成功触发。比如 `StartCoroutine` 方法可能未正确调用,或者传入的方法签名不符合要求[^3]。 #### 3. **资源依赖缺失** 某些情况下,协程依赖于特定的游戏对象组件或其他外部资源。如果这些资源在协程执行期间变得不可用(如被销毁),则会引发异常并终止协程。 --- ### 解决方案 以下是针对上述问题的具体解决办法: #### (一) 确保对象状态稳定 对于经常启停的对象,可以考虑将其父级容器设置为始终活动的状态,而通过其他手段控制子对象的行为。这样即使子对象反复开关也不会影响全局协程的稳定性。 ```csharp public class CoroutineManager : MonoBehaviour { private bool isRunning = false; public void ToggleObject(bool isActive) { if (!isActive && isRunning) StopAllCoroutines(); gameObject.SetActive(isActive); } } ``` #### (二) 正确初始化与传递参数 验证是否按照标准流程创建了协程实例,并确认所使用的函数形式匹配预期定义。特别是异步操作中的 suspend 和 async 关键字配合使用时需格外注意语法准确性。 ```csharp IEnumerator ExampleRoutine() { Debug.Log("Starting routine..."); yield return new WaitForSeconds(2f); Debug.Log("Finished after delay."); } void Start() { StartCoroutine(ExampleRoutine()); } ``` #### (三) 增强健壮性设计 引入额外的安全机制防止意外崩溃发生。可以通过捕获潜在风险点提前规避隐患;另外也可以利用 try-catch 结构处理可能出现的问题场景。 ```csharp IEnumerator SafeOperation(RectMask2D mask, Vector4 direction) { try { while (true) { if(mask != null){ mask.padding -= direction; yield return new WaitForEndOfFrame(); }else{ throw new System.Exception("Mask reference lost!"); } } } catch(Exception e){ Debug.LogError($"Error occurred:{e.Message}"); } } ``` --- ### 总结 综上所述,造成协程失效的主要因素集中在目标实体的有效性和程序内部衔接环节之上。采取合理措施优化这两方面即可显著改善此类状况的发生概率^。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值