用C# 模拟实现unity里的协程

注:需要了解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 }
View Code

先定义一个等待接口,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 }
View Code
 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 }
View Code

定义 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 }
View Code

这一段代码是这里最重要的部分。这里用一个链表记录了添加的所有“协程”,并在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 }
View Code

模拟一帧的时间。

 

运用测试:

 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 }
View Code

测试结果:

用秒表掐了 一下,好像没什么毛病。

 

posted @ 2018-08-31 14:37  吃斤欢乐豆  阅读(5036)  评论(1编辑  收藏  举报