自己实现unity的协程调度器--利用c#迭代器和unity生命周期函数

通过unity协程调度器来开启协程,yield return后面逻辑的执行时机如下
yield return不同内容的含义
1.下一帧执行
yield return 数字;
yield return null;
//在update和LateUpdate之间执行

2.等待指定秒过后执行
Yield return new WaitforSecends(秒):
//在update和LateUpdate之间执行

3.等待下一个固定物理帧更新时执行
Yeild return WaitForFixedUpdate();
//在FixedUpdate和碰撞相关函数之后执行

4.等待摄像机和GUI渲染过后执行
Yield return new WaitForEndOfFrame();
//在LateUpdate之后的渲染相关处理完毕后
主要用于做截图功能时

5.一些特殊类型的对象,比如异步加载相关函数返回的对象
//一般在Update和LateUpdate之间执行

6.跳出协程
Yield return;

此时我利用c#迭代器和unity生命周期函数来实现自己的协程调度器,让yield return 数字,表示等待多少秒执行后面的逻辑。也就是实现 new WaitforSecends(秒)类的功能。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class YiedlReturnTime
{
    //记录 下次还要执行的 迭代器接口
    public IEnumerator e;
    //记录 下次执行的时间
    public float time;
}

public class CoroutineMrg : MonoBehaviour
{
    //单例模式
    private static CoroutineMrg instance;
    public static CoroutineMrg Instance => instance;
    //存储迭代器函数对象的容器  
    //因为可以在不同的地方通过该协程调度器来开启协程(同时可以开启多个协程)
    private List<YiedlReturnTime> list = new List<YiedlReturnTime>();

    void Start()
    {
        instance = this;
    }

    void Update()
    {
        //为了避免在循环的过程移除元素而导致元素位置改变,需要从列表中移除内容 我们可以倒着遍历
        for(int i=list.Count-1;i>=0;i--)
        {
            //判断 当前迭代器函数 是否到了下一次执行的时间
            if(list[i].time<=Time.time)
            {
                if(list[i].e.MoveNext())
                {
                    //如果是true 那还需要对该迭代器 进行处理
                    if(list[i].e.Current is int)
                    {
                        list[i].time = Time.time + (int)list[i].e.Current;
                    }
                    else
                    {
                        //该list 只是存储 处理时间相关 等待逻辑的  迭代器函数
                        //如果是别的类型就因该存储到别的容器中
                    }    
                }
                else
                {
                    //后面已经没有可以等待和执行的了 证明已经执行完毕了逻辑。
                    list.RemoveAt(i);
                }
            }
        }
    }
    public void MystartCoroutine(IEnumerator e)
    {
        //传入一个 迭代器函数返回的结构,那么一来就因该执行它
        //一来就执行第一步
        if(e.MoveNext())
        {
            //如果yield return 返回的是数字
            if(e.Current is int)
            {
                //按思路 应该把 这个迭代器函数 和它下一次执行的时间点 记录下来
                //然后不停检测 时间 是否达到了 下一次执行的时间点
                YiedlReturnTime y = new YiedlReturnTime();
                y.e = e;
                y.time = Time.time + (int)e.Current;
                //把记录的信息记录到数据容器中
                list.Add(y);
            }
            else
            {
                //其它类型代表的意思 需要自行实现
            }
            
        }
    }
}


测试类

using System.Collections;
using UnityEngine;

public class Lession15 : MonoBehaviour
{
    IEnumerator MyCoroutine()
    {
        print("第1次");
        yield return 1;//等待1秒
        print("第2次");
        yield return 10;//等待10秒
        print("第3次");
    }
   
    void Start()
    {
        //利用自己实现的协程调度器 来 开启协程
        CoroutineMrg.Instance.MystartCoroutine(MyCoroutine()); 
    }

}

注意:两个类都要挂载到游戏物体上,查看截图时间,分别等待了1秒和10秒。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity协程的底层实现是基于C#迭代器实现的。在C#中,使用yield关键字可以将方法转换为迭代器,通过迭代器可以实现协程的效果。Unity中的协程也是基于这个原理实现的。 如果要自己实现一个协程,可以按照以下步骤进行: 1. 定义一个委托,用于表示协程的执行体。 ```csharp public delegate IEnumerator CoroutineDelegate(); ``` 2. 定义一个协程类,保存协程的执行体和当前执行状态。 ```csharp public class Coroutine { private CoroutineDelegate m_CoroutineDelegate; private IEnumerator m_Enumerator; private bool m_IsDone; public bool IsDone { get { return m_IsDone; } } public Coroutine(CoroutineDelegate coroutineDelegate) { m_CoroutineDelegate = coroutineDelegate; m_Enumerator = m_CoroutineDelegate(); m_IsDone = false; } public void Update() { if (m_Enumerator != null && !m_IsDone) { if (!m_Enumerator.MoveNext()) { m_IsDone = true; } } } } ``` 3. 在需要使用协程的地方,创建一个协程对象并添加到一个协程管理中。 ```csharp public class CoroutineManager : MonoBehaviour { private static CoroutineManager m_Instance; private List<Coroutine> m_Coroutines = new List<Coroutine>(); public static CoroutineManager Instance { get { if (m_Instance == null) { m_Instance = new GameObject("CoroutineManager").AddComponent<CoroutineManager>(); } return m_Instance; } } private void Update() { for (int i = m_Coroutines.Count - 1; i >= 0; i--) { Coroutine coroutine = m_Coroutines[i]; coroutine.Update(); if (coroutine.IsDone) { m_Coroutines.RemoveAt(i); } } } public Coroutine StartCoroutine(CoroutineDelegate coroutineDelegate) { Coroutine coroutine = new Coroutine(coroutineDelegate); m_Coroutines.Add(coroutine); return coroutine; } } ``` 4. 在协程中使用yield关键字来实现挂起和恢复。 ```csharp private IEnumerator MyCoroutine() { Debug.Log("Start Coroutine"); yield return null; Debug.Log("Wait One Frame"); yield return new WaitForSeconds(1.0f); Debug.Log("Wait One Second"); yield return new WaitForEndOfFrame(); Debug.Log("Wait End Of Frame"); } ``` 以上就是一个简单的协程实现。注意,实际应用中还需要考虑协程的取消、异常处理等问题,需要根据具体需求进行扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值