zoukankan      html  css  js  c++  java
  • 用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

    测试结果:

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

  • 相关阅读:
    leetcode 763 划分字母区间
    leetcode 392 判断子序列
    Leetcode 665 修改一个数成为非递减数组 (Easy)
    leetcode 605 种花问题 贪心算法
    leetcode 452 用最少数量的箭引爆气球 贪心算法
    leetcode 455 分发饼干 贪心算法
    delphi中的 CLX Application
    delphi 之DCOM应用服务器定义函数
    SqlServer 之 sp_executesql系统存储过程的介绍和使用
    delphi 之调用WinSock的API获取本机的机器名称和IP地址
  • 原文地址:https://www.cnblogs.com/yougoo/p/9565704.html
Copyright © 2011-2022 走看看