前言
个人总结的一些Unity协程学习心得,如有不对请在评论区指出一起学习!感谢。
在Unity编程中谈到异步逻辑,可以考虑使用协程来实现。协程(Coroutine)在Unity中的主要作用就是把一个任务暂停(挂起),并在下一帧(或之后的某一时间点)继续。常见案例有网络请求、临时计时器、资源加载、打字机特效等。需要注意的是,协程不是线程,协程是在主线程中执行的。使用协程可以不用考虑同步和锁的问题。
1、C#迭代器
官方文档:遍历集合 - C# | Microsoft Learn
在了解并使用协程之前,我们可以先扩展一下关于C#迭代器的知识,方便我们理解什么是yield。
迭代器方法或
get
访问器可对集合执行自定义迭代。 迭代器方法使用 yield return 语句返回元素,每次返回一个。 到达yield return
语句时,会记住当前在代码中的位置。 下次调用迭代器函数时,将从该位置重新开始执行。迭代器方法或get
访问器的返回类型可以是 IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>。
在C#中,yield可以粗浅的理解为“产出”。用于实现迭代器,逐步产出序列中的元素
案例:写一个返回数字的迭代器方法
void Start()
{var numbers = GetNumbers();while (numbers.MoveNext()){Debug.Log($"t_Coroutine {numbers.Current}");}
}IEnumerator GetNumbers()
{yield return 0;yield return 1;yield return 200;
}
输出:
但是在Unity中,我们只用到了IEnumerator来创建协程方法。因此,我着重介绍一下IEnumerator接口。
1.1 IEnumerator
public interface IEnumerator
{object Current{get;}bool MoveNext();void Reset();
}
可以看到其实迭代器内部很简单。
Current是当前迭代器指向的对象;MoveNext()是移动指针指向下一个对象,如果是最后一个对象则返回false;Reset()是重置为最初状态。
2、协程基本语法
2.1 写一个简单的协程
协程开始时,会顺序执行yield return null;之前的代码,遇到yield return 会被挂起(暂停),等待指定时间后,从被挂起的地方继续往下执行。
using System.Collections;
using UnityEngine;public class CoroutineExample : MonoBehaviour
{void Start(){StartCoroutine(MyCoroutine());}IEnumerator MyCoroutine(){// 开启协程就会执行的逻辑Debug.Log("t_Coroutine Start Coroutine");yield return null; //在这里被暂停,暂停1帧//挂起并恢复之后的逻辑Debug.Log("t_Coroutine Wait One Frame After");yield return new WaitForSeconds(2); //在这里被暂停,暂停2秒Debug.Log("t_Coroutine Wait Two Seconds After");//协程结束}
}
输出:
2.2 yield return的不同用法
2.2.1 yield return null 与 yield return 1
在Unity协程中,yield return null; 和 yield return 1; 实际上是等效的,二者都会暂停协程的执行并在下一帧继续执行。即等待一帧
之所以等效,是因为yield return会返回一个值给Unity的协程调度器,这个值用于决定协程何时继续。null和任意整数(例如1)都会导致协程在下一帧恢复执行。
2.2.2 yield break
立即停止协程
案例:可以看到Break After并没有被打印出来,在此之前协程就停止了
using System.Collections;
using UnityEngine;public class CoroutineExample : MonoBehaviour
{void Start(){StartCoroutine(MyCoroutineBreak());}IEnumerator MyCoroutineBreak(){Debug.Log("t_Coroutine Start MyCoroutineBreak");yield return null; //在这里被暂停,暂停1帧Debug.Log("t_Coroutine Wait Two Seconds After");yield break;//协程结束Debug.Log("t_Coroutine Break After");}
}
输出:
2.2.3 yield return StartCoroutine(OtherCoroutine())
协程是可以嵌套的,可以使用yield return StartCoroutine(OtherCoroutine()),等待OtherCoroutine协程方法执行完毕后,再继续往下执行
案例:
void Start()
{Debug.Log("t_Coroutine Start1");StartCoroutine(TestCoroutine());Debug.Log("t_Coroutine Start2");
}IEnumerator TestCoroutine()
{Debug.Log("t_Coroutine test1");yield return StartCoroutine(LoadCoroutine());Debug.Log("t_Coroutine test2");
}IEnumerator LoadCoroutine()
{Debug.Log("t_Coroutine load 1");yield return null;Debug.Log("t_Coroutine load 2");
}
输出:
2.2.4 其他用法
yield return new WaitForEndOfFrame();//等待帧结束yield return new WaitForSeconds(1f);//等待1秒;yield return WebRequest();//等待到网络请求结束返回结果的时候;yield return new WaitUntil()//等待到结果返回为true的时候yield return new WaitWhile()//等待到结果返回为false的时候
2.3 开启 / 停止协程
2.3.1 直接开启一个协程
无法控制暂停,直接调用协程方法开启
StartCoroutine(MyCoroutine());
2.3.2 用变量控制协程
private Coroutine myCoroutine;
myCoroutine = StartCoroutine(MyCoroutine());//开启协程
StopCoroutine(myCoroutine);//停止协程
2.3.3 用协程方法名控制协程
StartCoroutine("MyCoroutine");//开启协程
StopCoroutine("MyCoroutine");//停止协程
2.3.4 停止所有正在运行的协程
会停止该对象上所有正在运行的协程,慎用!
当一个游戏对象(GameObject)把Active设置为false,即SetActive(false),也会停止该对象上附加的协程。
调用Destroy(GameObject)时,Unity会调用OnDisable(),处理并停止协程。在这一帧结束时,会调用OnDestroy()。
StopAllCoroutines();
3、学习参考博客
官方文档:Unity - Manual: Coroutines
通俗易懂博客(建议先看这个,很好理解):Unity C#中协程/携程Coroutine简单易懂详解-CSDN博客
更进一步理解:Unity StartCoroutine - 简书