C#笔记 协程coroutines yield return

时间:2021-05-21 23:35:14

coroutines在unity里用处多多,用的好可以大量简化代码。
例如:
让loading场景绘制进度条不阻塞,这里异步加载关卡的函数还是必须的。
简化编写不同阶段不同行为的GameObject。
等等。

从IEnumerator和IEnumerable开始
看起来是枚举,其实在c#里的意思是迭代器(IEnumerator)和可迭代的(IEnumerable),这个不要阐述错误理解。
迭代模式(指设计模式):允许你访问一个数据项序列中的所有元素,而无须关心序列是什么类型(数组、列表、链表等)。
看一个yield return来实现的迭代器代码:

public IEnumerator GetEnumerator()
{
for ( int index = 0; index < values.Length; index ++ )
{
yield return values[(index + startingPoint)%value.Length];
}
}

yield return 语句只表示“暂时地”退出方法——事实上,你可把它看成暂停。
这些搞清楚可以更好的理解unity里的协程(coroutines)。

Unity coroutines
例子功能:
不同阶段不同行为的GameObject。
阶段1,它移动到特定坐标后打印“我到了”
阶段2,过3秒后再打印“完成!”

public class CoroutinesExample : MonoBehaviour
{
public float smoothing = 1f;
public Transform target;

void Start ()
{
StartCoroutine(MyCoroutine(target));
}

// 注意这个函数返回值是IEnumerator,必须
IEnumerator MyCoroutine (Transform target)
{
// 处理第一阶段
// 和目标大于0.05就按照smoothing * Time.deltaTime移动一段距离
while(Vector3.Distance(transform.position, target.position) > 0.05f)
{
transform.position = Vector3.Lerp(transform.position, target.position, smoothing * Time.deltaTime);
yield return null;// 这里暂停后返回,等待下帧执行机会
}
print("我到了");

// 处理第二阶段
yield return new WaitForSeconds(3f);
print("完成!");
}
}

代码相对于要切换状态的传统写法简炼的多。

Monobehaviour的函数执行顺序图
下面是Monobehaviour的函数执行顺序图,可以看到yield的恢复位置。
C#笔记 协程coroutines yield return

unity中的yield
yield 后面可以跟的表达式:
a) return null - 等下个Update之后恢复
b) return new WaitForEndOfFrame() - 等下个OnGUI之后恢复
c) return new WaitForFixedUpdate() - 等下个FixedUpdate之后恢复,有可能一帧内多次执行
d) return new WaitForSeconds(2) - 2秒后,等下个Update之后恢复
e) return new WWW(url) - Web请求完成了,Update之后恢复
f) return StartCorourtine() - 新的协成完成了,Update之后恢复
g) break -退出协程
h) return Application.LoadLevelAsync(levelName) - load level 完成,异步加载场景
i) return Resources.UnloadUnusedAssets(); // unload 完成

注意其中 WaitForSeconds()受Time.timeScale影响,当Time.timeScale = 0f 时,yield return new WaitForSecond(x) 将不会恢复。

一些规则
1)返回值必须是IEnumerator
2)参数不能加ref或out
3)函数Update和FixedUpdate中不能使用yield语句,但可以启动协程
4)yield return语句不能位于try-catch语句块中
5)yield return不能放在匿名方法中
6)yield return不能放在unsafe语句块中