协程的基本原理
协程本质就是一个状态机,内部通过MoveNext()方法进行执行,并通过一个state状态标识来区分当前应该执行哪个部分,同时Current代表当前执行的部分的返回值。
Ienumerator PrintTime()
{
int time = 10;
yield return null;
for(int i = 0;i < time;i++)
{
print(i);
yield return null;
}
int totalTime = 0;
for(j = 1;j < = time;j++)
{
totalTime += j;
}
yield return new WaitForSeconds(0.5f);
}
要点
- 每个yield return部分,在底层实现都会转换为一个函数,然后按顺序执行
- Current属性代表了当前部分函数的返回值,yield return 返回值 这部分的返回值是什么,那Current就是什么。
以上过程在底层大概模拟为以下过程
public class 类
{
private int state;
private object current;
public object Current => current;
public bool MoveNext()
{
switch(state)
{
case 0:
int time = 10;
current = null;
state = 1;
return true;
case 1:
int i = 0;
print(i);
current = null;
state = 2;
return true;
//循环内部yield return ,则就是将循环拆成了多个小的部分
//这里拆成了十个小的部分,state = 12
case 12:
int totalTime = 0;
for(j = 1;j < = time;j++)
{
totalTime += j;
}
state = -1; //最后一个部分,-1为结束标志
current = new WaitForSeconds(0.5f);
return false; //由于这是最后一部分,返回false
}
}
}
进阶使用
- 理论上可以通过协程的MoveNext()来控制协程的执行,但是由于协程的特殊性,一般不推荐
- 要想实现较为精细的控制协程,可以通过自定义类来实现
//使用:
//在类中创建对象,并将对应的协程传入,在Update或Lateupdate等帧更新方法中进行调用自定义类的Update,通过类的标识位对协程进行控制
public class ManualCoroutineRunner
{
private IEnumerator coroutine;
private bool isPaused = false;
public void Start(IEnumerator routine)
{
coroutine = routine;
}
public void Update()
{
if (!isPaused && coroutine != null)
{
if (!coroutine.MoveNext())
{
coroutine = null; // 协程结束
}
}
}
public void Pause() => isPaused = true;
public void Resume() => isPaused = false;
}