注:需要了解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 }
测试结果:
用秒表掐了 一下,好像没什么毛病。