C# 基于 IEnumerator 实现一个类似Unity的协程

Unity Coroutine


Unity Coroutine(协程)和 IEnumerator 基本一样,类似一个状态机的概念,稍微有过深入了解C# IEnumerator 可以大概知道这里面是一个状态机的概念 。

IEnumerable 是一个状态标识,标识这个对象是可以进行迭代尝试的(没有太大深究意义,只需要知道当你需要自己实现一个迭代器的时候需要继承它就可以了)。

IEnumerator.Current 则为当前状态,IEnumerator.MoveNext() 尝试进行下个状态的切换,失败则会返回false。

我们先看个非常简单代码:

// An highlighted block
static IEnumerable TestIEnumerator01()
{
    for (int i = 0; i < 10; i++)
    {
        yield return i;
    }
    yield return null;
}

在C#中,这是一个非常简单的迭代器。(作者不喜欢理论,喜欢依赖代码来解释。)
接着,我们在Main函数里面实现一点东西,整体的代码看起来是这样的:

using System;
using System.Collections;

namespace IteratorLearn
{
    class IteratorLearnClass
    {
        static IEnumerable TestIEnumerator01()
        {
            for (int i = 0; i < 10; i++)
            {
                yield return i;
            }
            yield return null;
        }

        static void Main(string[] args)
        {
            IEnumerator iterator = TestIEnumerator01().GetEnumerator();
            while (iterator.MoveNext())
            {
                Console.WriteLine(iterator.Current);
            }
            Console.WriteLine("iterator over!");
        }
    }
}

输出的结果:

在这里插入图片描述

我们可以看到,输出了10个阿拉伯数字以及一个Null。
那么,冲这个简单的代码中,我们便可以发现其中的奥利奥(奥秘)了。我们每调用一次MoveNext() 那么状态便继续往下走。而 yield return xxxx 便是我们的状态返回结果。既然我们已经大概了解 IEnumerable 与 IEnumerator,那么我们进入今天的主题!

基于 IEnumerator 实现一个类似Unity的协程


上面我们已经了解了 IEnumerable 与 IEnumerator,接下来我们开始写代码!并且尽可能模拟 Unity MonoBehaviour(不代表Unity就是这么写的!)

using System;
using System.Collections;
using System.Collections.Generic;

namespace IteratorLearn
{
    class IteratorLearnClass
    {
        static IEnumerator TestIEnumerator01()
        {
            for (int i = 0; i < 10; i++)
            {
                yield return i;
                Console.WriteLine(i);
            }
            yield return null;
        }
        static MonoBehaviour behaviour;
        static void Main(string[] args)
        {
            behaviour = new MonoBehaviour();
            behaviour.StartCoroutine(TestIEnumerator01());
            Console.WriteLine("iterator over!");
        }
    }

    public class Time
    {
        public static int frameCount;
    }

    public class MonoBehaviour
    {
        private bool m_MonoBehaviourRuning = false;
        private Stack<IEnumerator> m_CurrentTorStacks = new Stack<IEnumerator>();

        public MonoBehaviour()
        {

        }

        public void StartCoroutine(IEnumerator routine)
        {
            this.m_CurrentTorStacks.Push(routine);
            this.m_MonoBehaviourRuning = this.m_CurrentTorStacks.Count > 0;
            if (this.m_MonoBehaviourRuning)
            {
                IEnumerator iterator = TestIEnumerator01().GetEnumerator();
                while (iterator.MoveNext())
                {
                    this.IteratorUpdate();
                    this.m_MonoBehaviourRuning = this.m_CurrentTorStacks.Count > 0;
                }
            }
        }

        private void IteratorUpdate()
        {
            if (this.m_CurrentTorStacks.Count > 0)
            {
                IEnumerator iterator = this.m_CurrentTorStacks.Pop();
                while (iterator.MoveNext())
                {
                }
            }
        }

        IEnumerable TestIEnumerator01()
        {
            while (this.m_MonoBehaviourRuning)
            {
                Time.frameCount++;
                yield return Time.frameCount;
            }
        }
    }
}

代码不难,只是为了模拟Unity的MonoBehaviour这样更清晰一些。但是这里的代码执行结果和之前是一样的。

在这里插入图片描述
既然是一样的,那作者写那么多废话干嘛?不急,我们再看看:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace IteratorLearn
{
    class IteratorLearnClass
    {
        static IEnumerator TestIEnumerator01()
        {
            for (int i = 0; i < 10; i++)
            {
                yield return new WaitForFrame(50);
                Console.WriteLine($"i => {i}, frame => {Time.frameCount}");
            }
            yield return null;
        }
        static MonoBehaviour behaviour;
        static void Main(string[] args)
        {
            behaviour = new MonoBehaviour();
            behaviour.StartCoroutine(TestIEnumerator01());
            Console.WriteLine("iterator over!");
        }
    }


    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct WaitForFrame
    {

        public int Frame;

        public WaitForFrame(int frame)
        {
            Frame = frame;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct Time
    {
        public static int frameCount;
    }


    public class MonoBehaviour
    {
        private bool m_Condition = false;
        private bool m_MonoBehaviourRuning = false;
        private Stack<IEnumerator> m_CurrentTorStacks = new Stack<IEnumerator>();
        private int m_lastFrame = 0;


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void StartCoroutine(IEnumerator routine)
        {
            this.m_CurrentTorStacks.Push(routine);
            this.m_MonoBehaviourRuning = this.m_CurrentTorStacks.Count > 0;
            if (this.m_MonoBehaviourRuning)
            {
                IEnumerator iterator = TestIEnumerator01().GetEnumerator();
                while (iterator.MoveNext())
                {
                    this.IteratorUpdate();
                    this.m_MonoBehaviourRuning = this.m_CurrentTorStacks.Count > 0;
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void IteratorUpdate()
        {
            if (this.m_CurrentTorStacks.Count > 0)
            {
                WaitForFrame waitForFrame = default(WaitForFrame);

                IEnumerator iterator = this.m_CurrentTorStacks.Pop();
                if(iterator.Current != null)
                {
                    if (iterator.Current is WaitForFrame)
                    {
                        waitForFrame = (WaitForFrame)iterator.Current;
                        if (Time.frameCount < this.m_lastFrame + waitForFrame.Frame)
                        {
                            this.m_CurrentTorStacks.Push(iterator);
                            this.m_Condition = false;
                        }
                        else
                        {
                            this.m_lastFrame = Time.frameCount;
                            this.m_Condition = true;
                            iterator.MoveNext();
                        }
                    }
                }
                else
                {
                    this.m_Condition = iterator.MoveNext();
                }


                while (this.m_Condition)
                {
                    if (iterator.Current != null)
                    {
                        if (iterator.Current is WaitForFrame)
                        {
                            waitForFrame = (WaitForFrame)iterator.Current;
                            if (Time.frameCount < this.m_lastFrame + waitForFrame.Frame)
                            {
                                this.m_lastFrame = Time.frameCount;
                                this.m_CurrentTorStacks.Push(iterator);
                                break;
                            }
                        }
                    }
                    else
                    {
                        this.m_Condition = iterator.MoveNext();
                    }
                }
            }
        }

        IEnumerable TestIEnumerator01()
        {
            while (this.m_MonoBehaviourRuning)
            {
                Time.frameCount++;
                yield return Time.frameCount;
            }
        }
    }
}

yield return new WaitForFrame(50); 这段代码很熟悉对吧?没错!等待指定的帧数!这样我们就大概的实现以下类似Unity的协程,当然这是不考虑代码优化的学习代码。各种条件判断和while。按照如此的思路,那么实现一个秒等待也是可行的。

执行后的结果返回:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值