注:需要了解C#的迭代器,不然很難理解。
之前面試有被問到unity協程的原理,以及撇開unity用純C#去實現協程的方法。后來了解一下,確實可以的。趁這會有空,稍微總結一下。
還是結合代碼說事吧:

1 /// <summary>
2 /// 等待接口 3 /// </summary>
4 public interface IWait 5 { 6 /// <summary>
7 /// 每幀檢測是否等待結束 8 /// </summary>
9 /// <returns></returns>
10 bool Tick(); 11 }
先定義一個等待接口,WaitForSeconds 和 WaitForFrames 實現接口的Tick()方法,每一幀調用Tick()方法檢測是否等待結束

1 /// <summary> 2 /// 按秒等待 3 /// </summary> 4 public class WaitForSeconds:IWait 5 { 6 float _seconds = 0f; 7 8 public WaitForSeconds(float seconds) 9 { 10 _seconds = seconds; 11 } 12 13 public bool Tick() 14 { 15 _seconds -= Time.deltaTime; 16 return _seconds <= 0; 17 } 18 }

1 /// <summary> 2 /// 按幀等待 3 /// </summary> 4 public class WaitForFrames:IWait 5 { 6 private int _frames = 0; 7 public WaitForFrames(int frames) 8 { 9 _frames = frames; 10 } 11 12 public bool Tick() 13 { 14 _frames -= 1; 15 return _frames <= 0; 16 } 17 }
定義 WaitForSeconds 和 WaitForFrames ,在構造函數初始化需要等待的時間/幀數

1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading; 7 using System.Threading.Tasks; 8
9 public class CoroutineManager 10 { 11 private static CoroutineManager _instance = null; 12 public static CoroutineManager Instance 13 { 14 get
15 { 16 if (_instance == null) 17 { 18 _instance = new CoroutineManager(); 19 } 20 return _instance; 21 } 22 } 23
24 private LinkedList<IEnumerator> coroutineList = new LinkedList<IEnumerator>(); 25
26 public void StartCoroutine(IEnumerator ie) 27 { 28 coroutineList.AddLast(ie); 29 } 30
31 public void StopCoroutine(IEnumerator ie) 32 { 33 try
34 { 35 coroutineList.Remove(ie); 36 } 37 catch (Exception e) { Console.WriteLine(e.ToString()); } 38 } 39
40 public void UpdateCoroutine() 41 { 42 var node = coroutineList.First; 43 while (node != null) 44 { 45 IEnumerator ie = node.Value; 46 bool ret = true; 47 if (ie.Current is IWait) 48 { 49 IWait wait = (IWait)ie.Current; 50 //檢測等待條件,條件滿足,跳到迭代器的下一元素 (IEnumerator方法里的下一個yield)
51 if (wait.Tick()) 52 { 53 ret = ie.MoveNext(); 54 } 55 } 56 else
57 { 58 ret = ie.MoveNext(); 59 } 60 //迭代器沒有下一個元素了,刪除迭代器(IEnumerator方法執行結束)
61 if (!ret) 62 { 63 coroutineList.Remove(node); 64 } 65 //下一個迭代器
66 node = node.Next; 67 } 68 } 69 }
這一段代碼是這里最重要的部分。這里用一個鏈表記錄了添加的所有“協程”,並在UpdateCoroutine()方法的每一次執行都去遍歷這些“協程”以及檢測等待是否結束。

1 public class Time 2 { 3 //每幀時間(秒)
4 public static float deltaTime 5 { get { return (float)deltaMilliseconds / 1000; } } 6 //每幀時間(毫秒)
7 public static int deltaMilliseconds 8 { get { return 20; }} 9 }
模擬一幀的時間。
運用測試:

1 public class Program 2 { 3 static void Main(string[] args) 4 { 5 var t1 = Test01(); 6 var t2 = Test02(); 7 CoroutineManager.Instance.StartCoroutine(t1); 8 CoroutineManager.Instance.StartCoroutine(t2); 9
10 while (true)// 模擬update
11 { 12 Thread.Sleep(Time.deltaMilliseconds); 13 CoroutineManager.Instance.UpdateCoroutine(); 14 } 15 } 16
17
18 static IEnumerator Test01() 19 { 20 Console.WriteLine("start test 01"); 21 yield return new WaitForSeconds(5); 22 Console.WriteLine("after 5 seconds"); 23 yield return new WaitForSeconds(5); 24 Console.WriteLine("after 10 seconds"); 25 } 26
27 static IEnumerator Test02() 28 { 29 Console.WriteLine("start test 02"); 30 yield return new WaitForFrames(500); 31 Console.WriteLine("after 500 frames"); 32 } 33 }
測試結果:
用秒表掐了 一下,好像沒什么毛病。