通过简单的C#测试代码,更好的理解Unity中的功能。

协程,协程其实就是以IEnumerator为根本,在上面套了一层Coroutine的皮,然后在合适的时机调用迭代器的MoveNext方法。通过迭代器的Current的值,进行合适时机的选择。

在C#中,通过Console.ReadKey()模拟Unity中的每一帧。

复制代码
 1 static IEnumerator Coroutine()
 2 {
 3     Console.WriteLine("[1]开始处理数据....");
 4     Console.WriteLine("[1]结束处理数据!");
 5     yield return null;
 6     Console.WriteLine("[2]开始处理数据....");
 7     Console.WriteLine("[2]结束处理数据!");
 8     yield return null;
 9     Console.WriteLine("[3]开始处理数据....");
10     Console.WriteLine("[3]结束处理数据!");
11     yield return null;
12     Console.WriteLine("[4]结束协程...");
13 }
14 static void Main(string[] args)
15 {
16     var e = Coroutine();
17     bool isEnd = false;
18     while (!isEnd)
19     {
20         Console.ReadKey();
21         Console.WriteLine("更新帧");
22         isEnd = !e.MoveNext();
23     }
24 }
复制代码

每一次按下按键时,会执行一个yield return前的操作。

虽然一共只有3个yield return,但是一共需要按下4次按键,正如IEnumerator的延迟执行一样。每一次foreach,或者说是调用迭代器的MoveNext方法的时候才能确定是否更新Current,也就是是否有yield return语句,所以一共需要4次更新。

运行结果如下图

实际上除了yeild return null这种等待一帧的方式,我们更常用的还有返回一个 WaitForSecends用来等待一定时间,下面来实现这种效果。

为了方便接下来的测试,简单的对Console.Writeline与ReadKey进行一些包装,让我们更加方便的使用,过程比较简单就不做展示。

 

定义一个float类型数据,记录当前“游戏”的运行时间,一个int类型数据,记录当前“游戏”已渲染的帧数数量。

我们这里假设我们模拟的Unity,每一帧间隔的时间是稳定的,它会以0.1秒的间隔渲染每一帧游戏画面。

模拟一个Update函数,他会模拟Unity“渲染”每一帧,并记录更新游戏帧数与时间。同时输出当前游戏运行时间与已渲染的帧数数量方便我们观察。

当然,这个Update需要我们通过Readkey手动触发,毕竟只是模拟Unity。

代码如下

复制代码
// 游戏运行时间
static float GameRunTime = 0f;
// 游戏已渲染帧数数量
static int FrameCount = 0;
static void Update()
{
    ReadKey();
    FrameCount++;
    GameRunTime = FrameCount * 0.1f;
    Print($"[{FrameCount}]游戏更新下一帧, 游戏运行时间 " + String.Format("{0:N1}", GameRunTime) + "");
}
复制代码

好了,这时候我们已经可以通过再Main函数通过在While(true)中调用Update函数让我们的“游戏”跑起来了。

接下来我们要实现Unity中的Coroutine与WaitForSecends。

WaitForSecends,它非常简单,只有一个float类型字段,waitfor:等待到某个时间

1 public class WaitForSecends
2 {
3     public float waitfor;
4     public WaitForSecends(float wait)
5     {
6         this.waitfor = GameRunTime + wait;
7     }
8 }

Coroutine的实现也并不复杂,我们规定协程在调用前会通过 IsCanMoveNext判断是否到达可以执行协程的条件。若不符合则继续等待,若符合则执行协程中的下一步

然后对迭代器的MoveNext方法套一层皮,如果迭代器的Current的空,则等待一帧,否则会尝试进行转Current为WaitForSecend类型(其他类型的实现也类似)。然后更新数据用以在IsCanMoveNext中计算。

直接上代码吧。

复制代码
class Coroutine
{
    public float waitFor = 0;
    IEnumerator core;
    public bool IsCanMoveNext()
    {
        return waitFor == 0 || GameRunTime >= this.waitFor;
    }
    public bool MoveNext()
    {
        bool res = core.MoveNext();
        if(core.Current != null)
        {
            var w = core.Current as WaitForSecends;
            if(w != null)
            {
                waitFor = w.waitfor;
            }
        }
        else
        {
            waitFor = 0;
        }
        return res;
    }
    public Coroutine(IEnumerator e)
    {
        this.core = e;
    }
}
复制代码

然后我们声明测试函数

复制代码
 1 static IEnumerator CoroutineFunction()
 2 {
 3     Print("某个函数在协程开始时立即执行");
 4     yield return null;
 5     Print("某个函数延迟一帧后执行");
 6     yield return new WaitForSecends(0.5f);
 7     Print("某个函数在0.5秒后被执行");
 8     yield return new WaitForSecends(0.2f);
 9     Print("某个函数在0.2秒后被执行");
10     yield return null;
11     Print("某个函数延迟一帧后执行");
12 }
复制代码

在Main函数中调用以上这些东西

复制代码
 1 static void Main(string[] args)
 2 {
 3     Coroutine cor = new Coroutine(CoroutineFunction());
 4     while (true)
 5     {
 6         Update();
 7         if (cor.IsCanMoveNext())
 8         {
 9             cor.MoveNext();
10         }
11     }
12 }
复制代码

可以看到“协程”已经按照我们所期望的执行时机跑起来了。其他类型的WaitFor也可以用类似的办法实现,这里就不做赘述了。

本文中功能的实现方法仅是本人的模拟手段,Unity本身实现这些方法的细节并不一定采用相同的方案,本文仅起到帮助进一步了解Unity功能本质与抛砖引玉的作用。

posted @   LaNashTT  阅读(106)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示