自己实现unity的协程功能_c#实现类似unity的协程-CSDN博客
前天和同事聊天,聊起来协程,聊起原理,什么迭代器,什么MoveNext
几句话带过之后就算完了,事后再次想起,发现自己已经忘了具体细节,于是也打算写成博客,供自己以后回应
一句话概括
(yield外部的)(会运行的)代码行,会被放到MoveNext()中
(写在yield return后面的类或者参数)会变成Current,Update每帧去调Current(判断是否能MoveNext),倘若返回了false,就不做任何事,否则就MoveNext()+新的初始化
具体代码+注释
查看代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;internal class Program
{static void Main(string[] args){MyMonoBehaviour objMyMonoBehaviour = new MyMonoBehaviour();Console.WriteLine("Create MyMonoBehaviour" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));objMyMonoBehaviour.StartCoroutine(CoroutineDetail());while (true){objMyMonoBehaviour.Update();Thread.Sleep(100);}}static IEnumerator CoroutineDetail(){Console.WriteLine("yield return null start:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));yield return null;Console.WriteLine("yield return null end:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));Console.WriteLine("wait 1.0 seconds start:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));yield return new MyWaitForSeconds(1.0f);Console.WriteLine("wait 1.0 seconds end:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));Console.WriteLine("wait 2.0 seconds start:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));yield return new MyWaitForSeconds(2.0f);Console.WriteLine("wait 2.0 seconds end:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));}
}public class MyMonoBehaviour
{public class RoutineInfo{//我的迭代器public IEnumerator routine;//迭代器当前需要比较的对象public MyYieldInstruction current;public bool IsCanMoveNext(){return current == null || current.IsCanMoveNext();}}//迭代器数据类ListList<RoutineInfo> lstRoutine = new List<RoutineInfo>();public void StartCoroutine(IEnumerator routine){//如果是空迭代器或者只能迭代一次的,直接返回if (routine == null || !routine.MoveNext()) return;//新建迭代数据类,管理该迭代器RoutineInfo objRoutineInfo = new RoutineInfo();lstRoutine.Add(objRoutineInfo);//初始化迭代数据类objRoutineInfo.routine = routine;SetRoutineInfo(ref objRoutineInfo);}//设置目标迭代器当前的迭代参数public void SetRoutineInfo(ref RoutineInfo objRoutineInfo){//yield后面不是new了一个类嘛,存到迭代器的Current里了,要拿到类,就在这设置一下objRoutineInfo.current = objRoutineInfo.routine.Current as MyYieldInstruction;}public void Update(){//从后往前遍历,便于lstRoutine.RemoveAt(i)for (int i = lstRoutine.Count - 1; i >= 0; i--){RoutineInfo item = lstRoutine[i];if (item == null) continue;if (!item.IsCanMoveNext()) continue;if (item.routine.MoveNext()) SetRoutineInfo(ref item);else lstRoutine.RemoveAt(i);//清除迭代完的迭代器}}
}//抽象类+抽象方法,有其他类型的迭代器就继承这个,判断条件由自己去实现
public abstract class MyYieldInstruction
{public abstract bool IsCanMoveNext();
}public class MyWaitForSeconds : MyYieldInstruction
{//在yield return时,记录等待时间,并用于后续的每次比较public float seconds;private DateTime beginTime;public MyWaitForSeconds(float seconds){this.seconds = seconds;beginTime = DateTime.Now;}//MyWaitForSeconds的比较就是//1.在初始化时记录开始时间和等待时间//2.使用当前时间减去开始时间,得到时间差//3.使用时间差去和等待时间比较,如果时间差>等待时间,就表示可以MoveNextpublic override bool IsCanMoveNext(){TimeSpan deltaSeconds = DateTime.Now - beginTime;bool res = deltaSeconds.TotalSeconds > seconds;Console.WriteLine("MyWaitForSeconds类内部比较一次,结果为" + res);return res;}
}
输出: