使用过Unity的同学一定知道,Unity提供了一套协程机制,简直不要太好用。但是这个协程依赖于Unity引擎,离开Unity就无法使用。那有没有办法实现不依赖Unity的协程呢?答案是当然阔以。 所谓实现一个协程,就是实现一个迭代器的容器!

Unity的协程

使用过Unity的同学应该都清楚Unity提供的协程,它可以使用的场景非常广泛。比如我们需要在UI打开的时候,延迟一秒钟播放一个动画。代码可以如下写:

public void OnUIOpen()
{StartCoroutine(DelyToPlayAni());
}private IEnumerator DelyToPlayAni()
{yield return new WaitForSeconds(0.5f);//PlayAniMethod
}

我们可以看到StartCoroutine方法的参数是一个返回值很奇怪的函数,这个IEnumerator 是一个什么鬼东西呢?

这要从迭代器模式说起。 迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,他是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式。简单来说,迭代器模式使得你能够获取到序列中的所有元素而不用关心是其类型是array,list,linked list或者是其他什么序列结构。这一点使得能够非常高效的构建数据处理通道(data pipeline),即数据能够进入处理通道,进行一系列的变换,或者过滤,然后得到结果。事实上,这正是LINQ的核心模式。

我们来看一下IEnumerator的接口是什么样子的。

using System.Runtime.InteropServices;namespace System.Collections
{[ComVisible(true)]public interface IEnumerator{object Current { get; }bool MoveNext();void Reset();}
}

我们发现IEnumerator属于System.Collections程序集,这说明,这个小东西根本不是Unity创造的,而是C#本身就拥有的东西。也说明,我们完全可以自己实现一个类似Unity的协程嘛!ok,摩拳擦掌,准备大展身手。

什么是IEnumerator

尴尬的问题来了,我们并不是很清楚迭代器到底是什么东西以及它的工作模式。看来开始之前,还有一些知识需要了解。

在.NET中,迭代器模式被IEnumerator和IEnumerable及其对应的泛型接口所封装。如果一个类实现了IEnumerable接口,那么就能够被迭代;调用GetEnumerator方法将返回IEnumerator接口的实现,它就是迭代器本身。迭代器类似数据库中的游标,他是数据序列中的一个位置记录。迭代器只能向前移动,同一数据序列中可以有多个迭代器同时对数据进行操作。 这个只能向前移动的特性就是我们协程需要用到的特性。所谓实现一个协程,就是实现一个迭代器的容器!

早在C#1的时候,C#就提供了一个迭代器,那就是foreach。 使得能够进行比for循环语句更直接和简单的对集合的迭代,编译器会将foreach编译来调用GetEnumerator和MoveNext方法以及Current属性,如果对象实现了IDisposable接口,在迭代完成之后会释放迭代器。 我们可以再回顾下IEnumerator的接口内容,各自含义如下:

object Current{ get; } 当前正在访问的对象,只读
bool MoveNext();移动到下一个元素,我们的协程通过此函数来控制迭代器的到底要不要移动下一个代码段.
如果迭代器返回false,则证明迭代器已经执行完毕,此时协程也应该执行完毕
void Reset();重置函数

开始设计协程

根据以上分析,来我们可以设计我们的协程:需要一个迭代器对象,并且处理其接口相关的函数。

///协程类定义
public sealed class Coroutine
{private IEnumerator _routine;public Coroutine(IEnumerator routine){_routine = routine;}public bool MoveNext(){if (_routine == null)return false;return _routine.MoveNext();}public void Stop(){_routine = null;}
}

如何驱动这个协程呢?我们可以定义一个协程管理器类。

using System;
using System.Collections;
using System.Collections.Generic;namespace Coroutine
{///使用单例模式的协程管理器,用于驱动所有协程MoveNextpublic class CoroutineManager{private static CoroutineManager _instance = null;public static CoroutineManager Instance{get{if (_instance == null)_instance = new CoroutineManager();return _instance;}}///链表存储所有协程对象private LinkedList<Coroutine> coroutineList = new LinkedList<Coroutine>();private LinkedList<Coroutine> coroutinesToStop = new LinkedList<Coroutine>();///开启一个协程public Coroutine Start(IEnumerator ie){var c = new Coroutine(ie);coroutineList.AddLast(c);return c;}///关闭一个协程public void Stop(IEnumerator ie){}public void Stop(Coroutine coroutine){coroutinesToStop.AddLast(coroutine);}///主线程驱动所有协程对象public void UpdateCoroutine(){var node = coroutineList.First;while (node != null){var cor = node.Value;bool ret = false;if (cor != null){bool toStop = coroutinesToStop.Contains(cor);if (!toStop){//一旦协程对象返回false,即意味着该协程要退出ret = cor.MoveNext();}}if (!ret){coroutineList.Remove(node);Console.WriteLine("[CoroutineManager] remove cor");}node = node.Next;}}}
}

开始测试协程1.0

我们的协程1.0版本写好了,如何测试呢?简单,这里以C#控制台程序驱动我们的协程为例。不过在正式测试前,还有一些工作要做。

我们需要定义一些辅助的接口和类来模拟Unity的协程控制,如WaitForSeconds等。相关实现代码如下:

using System;
namespace Coroutine
{public class Time{public const float deltaTime = 0.02f;public const int deltaMilTime = 20;}public interface IWait{bool Tick();}public class WaitForSeconds : IWait{public float waitTime = 0;public WaitForSeconds(float time){waitTime = time;}bool IWait.Tick(){waitTime -= Time.deltaTime;Console.WriteLine("[WaitForSeconds] now left:" + waitTime);return waitTime <= 0;}}public class WaitForFrame : IWait{public int waitFrame = 0;public WaitForFrame(int frame){waitFrame = frame;}bool IWait.Tick(){waitFrame--;Console.WriteLine("[WaitForFrame] now left:" + waitFrame);return waitFrame <= 0;}}
}

为了使得WaitForSeconds等可以参与我们协程的流程控制,还需要对Coroutine进行改造,主要改造的是MoveNext函数,改造完后的Coroutine完整代码如下:

using System;
using System.Collections;namespace Coroutine
{public class Coroutine{private IEnumerator _routine;public Coroutine(IEnumerator routine){_routine = routine;}public bool MoveNext(){if (_routine == null)return false;//看迭代器当前的流程控制(即yield return 后边的对象)//是否是我们当前IWait对象,如果是,看是否满足moveNext的条件IWait wait = _routine.Current as IWait;bool moveNext = true;if (wait != null)moveNext = wait.Tick();if (!moveNext){//此处有些不好理解。当时间没有到时,我们应该返回true//告诉管理器我们后边还有对象需要下一次继续迭代Console.WriteLine("[Coroutine] not movenext");return true;}else{//此时所有等待时间或者帧都已经迭代完毕,看IEnumerator对象后续是否还有yield return对象//将此结果通知给管理器,管理器会在下一次迭代时决定是否继续迭代该Coroutine对象Console.WriteLine("[Coroutine] movenext");return _routine.MoveNext();}}public void Stop(){_routine = null;}}
}

一切就绪,code我们控制台代码,在main韩素中模拟unity的update函数,不断update我们的协程管理器即可。测试代码如下:

using System;
using System.Collections;
using System.Threading;namespace Coroutine
{class MainClass{public static void Main(string[] args){Console.WriteLine("[Main] coroutine test begin");CoroutineManager.Instance.Start(CoroutineTest());int framecount = 0;//模拟当前帧数,当前time下,1秒50帧while (true){framecount++;Console.WriteLine("[Main] cur framecount:<" + framecount + "> CoroutineManager.Instance.UpdateCoroutine");CoroutineManager.Instance.UpdateCoroutine();//模拟unity的updateThread.Sleep(Time.deltaMilTime);if (framecount >= 35)break;}}private static IEnumerator CoroutineTest(){Console.WriteLine("[CoroutineTest] enter coroutine. begin return waitforseconds:0.5f");yield return new WaitForSeconds(0.5f);Console.WriteLine("[CoroutineTest] wait for seconds <0.5f> over. begin wait for frame:3");yield return new WaitForFrame(3);Console.WriteLine("[CoroutineTest] wait for frame <3> over. now exit corutine");}}
}

由我们的协程内容可知:第一个yield return延迟0.5秒,即25帧,应该在第26帧执行完毕。第2个yield return延迟3帧,应该在第29帧执行完毕。因此35帧的时候while循环退出。我们看下控制台打印结果,如下:

Last login: Wed Feb 12 17:29:04 on ttys000[Main] coroutine test begin
[Main] cur framecount:<1> CoroutineManager.Instance.UpdateCoroutine
[Coroutine] movenext
[CoroutineTest] enter coroutine. begin return waitforseconds:0.5f
[Main] cur framecount:<2> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.48
[Coroutine] not movenext
[Main] cur framecount:<3> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.46
[Coroutine] not movenext
[Main] cur framecount:<4> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.44
[Coroutine] not movenext
[Main] cur framecount:<5> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.42
[Coroutine] not movenext
[Main] cur framecount:<6> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.3999999
[Coroutine] not movenext
[Main] cur framecount:<7> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.3799999
[Coroutine] not movenext
[Main] cur framecount:<8> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.3599999
[Coroutine] not movenext
[Main] cur framecount:<9> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.3399999
[Coroutine] not movenext
[Main] cur framecount:<10> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.3199999
[Coroutine] not movenext
[Main] cur framecount:<11> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.2999999
[Coroutine] not movenext
[Main] cur framecount:<12> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.2799999
[Coroutine] not movenext
[Main] cur framecount:<13> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.2599999
[Coroutine] not movenext
[Main] cur framecount:<14> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.2399999
[Coroutine] not movenext
[Main] cur framecount:<15> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.2199999
[Coroutine] not movenext
[Main] cur framecount:<16> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.1999999
[Coroutine] not movenext
[Main] cur framecount:<17> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.1799999
[Coroutine] not movenext
[Main] cur framecount:<18> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.1599999
[Coroutine] not movenext
[Main] cur framecount:<19> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.1399999
[Coroutine] not movenext
[Main] cur framecount:<20> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.1199999
[Coroutine] not movenext
[Main] cur framecount:<21> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.0999999
[Coroutine] not movenext
[Main] cur framecount:<22> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.07999991
[Coroutine] not movenext
[Main] cur framecount:<23> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.05999991
[Coroutine] not movenext
[Main] cur framecount:<24> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.03999991
[Coroutine] not movenext
[Main] cur framecount:<25> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:0.01999991
[Coroutine] not movenext
[Main] cur framecount:<26> CoroutineManager.Instance.UpdateCoroutine
[WaitForSeconds] now left:-8.940697E-08
[Coroutine] movenext
[CoroutineTest] wait for seconds <0.5f> over. begin wait for frame:3
[Main] cur framecount:<27> CoroutineManager.Instance.UpdateCoroutine
[WaitForFrame] now left:2
[Coroutine] not movenext
[Main] cur framecount:<28> CoroutineManager.Instance.UpdateCoroutine
[WaitForFrame] now left:1
[Coroutine] not movenext
[Main] cur framecount:<29> CoroutineManager.Instance.UpdateCoroutine
[WaitForFrame] now left:0
[Coroutine] movenext
[CoroutineTest] wait for frame <3> over. now exit corutine
[CoroutineManager] remove cor
[Main] cur framecount:<30> CoroutineManager.Instance.UpdateCoroutine
[Main] cur framecount:<31> CoroutineManager.Instance.UpdateCoroutine
[Main] cur framecount:<32> CoroutineManager.Instance.UpdateCoroutine
[Main] cur framecount:<33> CoroutineManager.Instance.UpdateCoroutine
[Main] cur framecount:<34> CoroutineManager.Instance.UpdateCoroutine
[Main] cur framecount:<35> CoroutineManager.Instance.UpdateCoroutinePress any key to continue...

控制台打印的结果可以佐证我们的协程执行符合预期,WaitForSeconds和WaitForFrame可以正常的控制我们的协程的流程!nice!我们的第一课内容虽然多,但是出师大捷,为我们的协程的扩展打下了坚实的基础!

unity协程_[C#进阶]C#实现类似Unity的协程相关推荐

  1. unity 构建迷宫_教程:使用GameDraw在Unity中构建迷宫游戏关卡

    unity 构建迷宫 GameDraw is a 3D modeling extension for Unity developed by Mixed Dimensions that reduces ...

  2. python 协程 多线程_python进阶之多线程(简单介绍协程)

    多线程 线程:实现多任务的另一种方式 一个进程中,也经常需要同时做多件事,就需要同时运行多个'子任务',这些子任务,就是线程 线程又被称为轻量级进程(lightweight process),是更小的 ...

  3. android unity 关闭应用_在后台运行的Android Unity应用程序

    我有一个在Android上运行的Unity应用程序.它也会在应用程序放入后台时继续运行.它不使用服务在后台运行.相反,这个应用程序会创建一个新线程,并且该线程在应用程序处于后台时继续运行.在后台运行的 ...

  4. unity 360视频_如何将360视频与Unity集成

    unity 360视频 Since the introduction of the new Video Player component in Unity 5.6, we've had a lot o ...

  5. 怎么更进一步学python_【百尺竿头,更进一步学Python】Python进阶课程——进程,线程和协程的区别...

    本文带来各类奇怪的IT百科知识. [百尺竿头,更进一步学Python]Python进阶课程--进程:线程和协程的区别 现在多进程多线程已经是老生常谈了:协程也在最近几年流行起来.今天我们本文主要介绍进 ...

  6. java 协程_你真的了解kotlin的协程么?

    协程我的理解是协作的线程,就是说与主线程协作的线程,也就是工作线程. 而协程的本质确实是这样.Kotlin协程的底层就是Java的多线程,协程就是一个比较方便的线程框架.协程这种线程框架有什么好处呢? ...

  7. c++ 协程_理解Python协程(Coroutine)

    由于GIL的存在,导致Python多线程性能甚至比单线程更糟. GIL: 全局解释器锁(英语:Global Interpreter Lock,缩写GIL),是计算机程序设计语言解释器用于同步线程的一种 ...

  8. c++ 协程_用yield实现协程

    上一篇 理解python中的yield关键字 介绍了使用yeld实现生成器函数,这一篇我们来继续深入的了解一下yield,用yield实现协程. 先来解答一下上一篇留下的问题:下面的代码为什么第二次调 ...

  9. python3 携程_多任务(3):协程

    代码环境:python3.6 上一篇文章我们讲了 python 中多线程的使用:点击阅读,现在我们讲讲 python 中的协程. 异步IO 我们知道,CPU 速度远远快于磁盘.网络等 IO.在 IO ...

最新文章

  1. Sharepoint 2007 定制Feature和卸载Feature
  2. Trees Made to Order ZOJ - 1062
  3. 斯坦福大学2014机器学习教程中文笔记目录
  4. vue生命周期图示中英文版Vue实例生命周期钩子
  5. 【深入理解JVM】JVM字节码指令集
  6. 辨析ADKJVMJREJDKADT
  7. Bootstrap 禁用某个菜单项
  8. SQL Server 2019中的行模式内存授予反馈
  9. 专为专业音乐行业从业人员打造的AI智能编曲工具:Orb Producer Suite Mac
  10. 计算机为动态分区无法安装系统,采用gpt分区无法安装系统怎么办
  11. delphi pi怎么得到?
  12. H3C防火墙基础配置1-登录配置、安全域配置
  13. 2018再见,2019你好
  14. vue_drf之视频接口
  15. 大数据基础知识思维导图
  16. Ubuntu系统下打开chm文件
  17. 2022年芜湖市科技型中小企业类科技项目申报奖励补贴条件及申报时间程序
  18. trim()函数的含义及使用方法
  19. 时空之轮Android手柄,经典角色扮演类游戏 Android时空之轮
  20. BZOJ1984: 月下“毛景树”

热门文章

  1. 基于JAVA+SpringMVC+Mybatis+MYSQL的保险业务管理系统
  2. 基于JAVA+SpringMVC+Mybatis+MYSQL的学生请假管理系统
  3. 如何利用计算机实现非线性转换,基于cass数控绕线机非线性算法的设计与实现-计算机应用技术专业论文.docx...
  4. call和apply;this;闭包
  5. 【算法】Huffman编码(数据结构+算法)
  6. Android安全问题 钓鱼程序
  7. XML是什么,它能够做什么?——写给XML入门者
  8. 【Hadoop代码笔记】Hadoop作业提交之客户端作业提交
  9. ie6/ie7/firefox/dispaly:inline-block:兼容性
  10. WriteFile API简介