自己實現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();
}
}
//迭代器資料類List
List<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.使用時間差去和等待時間比較,如果時間差>等待時間,就表示可以MoveNext
public override bool IsCanMoveNext()
{
TimeSpan deltaSeconds = DateTime.Now - beginTime;
bool res = deltaSeconds.TotalSeconds > seconds;
Console.WriteLine("MyWaitForSeconds類內部比較一次,結果為" + res);
return res;
}
}
輸出: