前言
个人总结的一些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 - 简书