Unity实现自己的协程系统

为什么自己实现一套协程系统

协程(Coroutine)是一个强大且灵活的工具,它可以帮助开发者处理异步任务,例如等待某些事件、处理逐帧更新等。在Unity中,协程通常通过IEnumerator来实现,这种机制允许代码在执行过程中暂停和恢复。

然而,Unity原生的协程系统虽然简单易用,但在复杂场景下,例如管理多个协程、处理等待条件、暂停和恢复协程等,会显得力不从心。因此,我设计并实现了一个自定义的协程调度系统,旨在提供更强的控制、调度和扩展性。

系统结构概述

我的系统包括以下几个核心组件:

协程节点(CoroutineNode):用于表示一个协程的执行单元,负责管理协程的状态(暂停、完成)以及等待条件。

协程调度器(CoroutineScheduler):负责调度多个协程的执行顺序、暂停和恢复协程、移除完成的协程。

等待条件(WaitCondition):允许协程在特定条件下暂停执行,直到条件满足。

协程节点设计

协程节点是协程的封装体,主要负责:

维护协程的主体:即IEnumerator。

管理协程的状态:包括是否完成、是否暂停。

等待条件:协程可以通过等待特定条件来决定是否继续执行。

接口结构

cs 复制代码
/// <summary>
/// 一个协程节点的基础结构
/// </summary>
public interface ICoroutineNode
{
    /// <summary>
    /// 是否完成
    /// </summary>
    bool IsFinished { get; set; }

    /// <summary>
    /// 是否暂停
    /// </summary>
    bool IsPaused { get; set; }

    /// <summary>
    /// 枚举器,代表协程的主体
    /// </summary>
    IEnumerator Fiber { get; }

    /// <summary>
    /// 协程等待条件
    /// </summary>
    ICoroutineWaitCondition WaitCondition { get; }

    /// <summary>
    /// 判断协程是否可以继续执行
    /// </summary>
    /// <param name="context">自定义上下文</param>
    /// <returns></returns>
    bool CanContinue(ICoroutineContext context);

    /// <summary>
    /// 添加一个等待条件
    /// </summary>
    /// <param name="condition"></param>
    void AddWaitCondition(ICoroutineWaitCondition condition);

    // 暂停协程
    void Pause();

    // 恢复协程
    void Resume();
}

具体实现

cs 复制代码
public class CoroutineNode : ICoroutineNode
{
    // 协程主体(Fiber)
    public IEnumerator Fiber { get; private set; }

    // 是否完成
    public bool IsFinished { get; set; }

    // 是否暂停
    public bool IsPaused { get; set; }

    // 当前节点的等待条件
    private ICoroutineWaitCondition waitCondition = null;

    public ICoroutineWaitCondition WaitCondition => waitCondition;

    // 构造函数,传入一个协程(Fiber)
    public CoroutineNode(IEnumerator fiber)
    {
        Fiber = fiber;
        IsFinished = false;
        IsPaused = false;
    }

    // 添加等待条件
    public void AddWaitCondition(ICoroutineWaitCondition condition) => waitCondition = condition;

    // 检查等待条件是否满足,决定协程是否可以继续执行
    public bool CanContinue(ICoroutineContext context) => waitCondition.IsConditionMet(context);

    // 暂停等待条件
    public void Pause() => waitCondition.Pause();

    // 恢复等待条件   
    public void Resume() => waitCondition.Resume();
}

协程节点可以灵活管理一个协程的生命周期,并与等待条件解耦,便于扩展和控制。

协程调度器设计

顺序调度器与并行调度器

我的设计中包含两种不同的协程调度器:

顺序调度器(CoroutineSchedulerOrder):按照协程的加入顺序,依次执行每个协程,前一个协程完成后,才开始下一个协程的执行。

并行调度器(CoroutineScheduler):允许多个协程并行执行,每帧都会更新所有未完成的协程。

在并行调度器中的协程,执行顺序不保证,所以不要修改同一份数据.

后续如果继续实现不同类型的调度器,可以轻松地改为工厂模式生产调度器

接口结构

cs 复制代码
/// <summary>
/// 一个协程调度器的基础结构
/// </summary>
public interface ICoroutineScheduler
{
    /// <summary>
    /// 剩余协程数量
    /// </summary>
    int CoroutineCount { get; }
    /// <summary>
    /// 向调度器中添加协程
    /// </summary>
    /// <param name="fiber">枚举器</param>
    /// <returns></returns>
    ICoroutineNode AddCoroutine(IEnumerator fiber);

    /// <summary>
    /// 暂停一个协程
    /// </summary>
    /// <param name="coroutine"></param>
    void PauseCoroutine(ICoroutineNode coroutine);
    /// <summary>
    /// 恢复一个协程
    /// </summary>
    /// <param name="coroutine"></param>
    void ResumeCoroutine(ICoroutineNode coroutine);
    /// <summary>
    /// 移除一个协程
    /// </summary>
    /// <param name="coroutine"></param>
    /// <returns></returns>
    ICoroutineNode RemoveCoroutine(ICoroutineNode coroutine);
    /// <summary>
    /// 移除全部协程
    /// </summary>
    void RemoveAllCoroutines();

    // 更新协程状态,在每帧调用
    void UpdateCoroutines(ICoroutineContext context = null);
}

具体实现

并行调度器
cs 复制代码
/// <summary>
/// 每帧会将列表的所有协程执行一遍,不保证顺序
/// </summary>
public class CoroutineScheduler : ICoroutineScheduler
{
    // 用于存储所有协程的队列
    private List<ICoroutineNode> coroutineList = new List<ICoroutineNode>();
    //被暂停的协程
    private HashSet<ICoroutineNode> frozenCoroutineHashSet = new HashSet<ICoroutineNode>();

    private List<ICoroutineNode> finishedCoroutines = new List<ICoroutineNode>();
    //所剩协程数量
    public int CoroutineCount { get => coroutineList.Count; }
    // 向调度器中添加协程
    public ICoroutineNode AddCoroutine(IEnumerator fiber)
    {
        if (fiber == null)
        {
            return null;
        }

        ICoroutineNode coroutine = new CoroutineNode(fiber); // 创建协程节点
        coroutineList.Add(coroutine); // 将节点加入队列
        return coroutine;
    }

    // 停止一个特定的协程,不影响其他协程
    public void PauseCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsPaused = true;
    }
    //恢复一个协程
    public void ResumeCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsPaused = false;
    }

    /// <summary>
    /// 移除一个协程,视为该协程完成了
    /// </summary>
    /// <param name="coroutine"></param>
    /// <returns></returns>
    public ICoroutineNode RemoveCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsFinished = true;
        coroutineList.Remove(coroutine);
        return coroutine;
    }
    // 移除所有协程,视为已完成
    public void RemoveAllCoroutines()
    {
        foreach (var c in coroutineList) c.IsFinished = true;

        coroutineList.Clear();
    }

    // 更新协程状态,在每帧调用
    public void UpdateCoroutines(ICoroutineContext context = null)
    {
        int queueSize = coroutineList.Count;
        if (queueSize == 0) return;

        foreach (var coroutine in coroutineList)
        {
            //已完成协程移除列表
            if (coroutine.IsFinished)
            {
                finishedCoroutines.Add(coroutine);
                continue;
            }
            //被暂停协程
            if (coroutine.IsPaused)
            {
                if (frozenCoroutineHashSet.Contains(coroutine)) continue;

                if (coroutine.WaitCondition != null)
                {
                    coroutine.Pause();
                    frozenCoroutineHashSet.Add(coroutine);
                }
                continue;
            }
            else if (frozenCoroutineHashSet.Contains(coroutine))//是否是被暂停过的协程要恢复
            {
                coroutine.Resume();
                frozenCoroutineHashSet.Remove(coroutine);
            }

            if (coroutine.WaitCondition == null)
            {

            }
            else if (!coroutine.CanContinue(context)) continue;

            MoveNextCoroutine(coroutine);
        }

        // 移除已经完成的协程
        foreach (var finished in finishedCoroutines)
        {
            coroutineList.Remove(finished);
        }

    }
    private void MoveNextCoroutine(ICoroutineNode coroutine)
    {
        // 如果协程可以继续执行,调用 MoveNext() 继续执行协程
        if (coroutine.Fiber.MoveNext())
        {
            System.Object yieldCommand = coroutine.Fiber.Current; // 获取当前协程的返回值
            var coroutineWaitCondition = yieldCommand as ICoroutineWaitCondition;

            // 如果返回的是等待条件,添加等待条件到协程节点
            if (coroutineWaitCondition != null)
                coroutine.AddWaitCondition(coroutineWaitCondition);
            else
                throw new System.Exception("yield return type error");
        }
        else
        {
            coroutine.IsFinished = true; // 标记协程已完成
            finishedCoroutines.Add(coroutine);
        }
    }
}
顺序调度器
cs 复制代码
/// <summary>
/// 协程调度器,管理协程的生命周期和调度
/// 该调度器的协程有执行顺序,前一个协程彻底执行完,下一个协程才开始执行
/// </summary>
public class CoroutineSchedulerOrder : ICoroutineScheduler
{
    // 用于存储所有协程的队列
    private Queue<ICoroutineNode> coroutineQueue = new Queue<ICoroutineNode>();
    //被暂停协程
    private ICoroutineNode frozenCoroutineNodeOrder = null;

    //所剩协程数量
    public int CoroutineCount { get => coroutineQueue.Count; }
    // 向调度器中追加协程
    public ICoroutineNode AddCoroutine(IEnumerator fiber)
    {
        if (fiber == null)
        {
            return null;
        }

        ICoroutineNode coroutine = new CoroutineNode(fiber); // 创建协程节点
        coroutineQueue.Enqueue(coroutine); // 将节点加入队列
        return coroutine;
    }

    // 停止一个特定的协程,这将阻塞后续的协程
    public void PauseCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsPaused = true;
    }
    //恢复一个被暂停的协程
    public void ResumeCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsPaused = false;
    }


    // 移除一个协程,视为该协程完成了
    public ICoroutineNode RemoveCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsFinished = true;
        var coroutineList = coroutineQueue.ToList();
        coroutineList.Remove(coroutine);
        coroutineQueue = new Queue<ICoroutineNode>(coroutineList);
        return coroutine;
    }
    // 移除所有协程,视为已完成
    public void RemoveAllCoroutines()
    {
        foreach (var c in coroutineQueue) c.IsFinished = true;

        coroutineQueue.Clear();
    }

    // 更新协程状态,在每帧调用
    public void UpdateCoroutines(ICoroutineContext context = null)
    {
        int queueSize = coroutineQueue.Count;
        if (queueSize == 0) return;

        ICoroutineNode coroutine = coroutineQueue.Peek(); // 获取队首协程

        // 如果协程已完成,从队列中移除
        if (coroutine.IsFinished)
        {
            coroutineQueue.Dequeue();
            return;
        }

        // 如果协程暂停,执行暂停操作,并跳过本帧处理
        if (coroutine.IsPaused)
        {
            if (frozenCoroutineNodeOrder != null && frozenCoroutineNodeOrder == coroutine) return;
            if (coroutine.WaitCondition != null)
            {
                coroutine.Pause();
                frozenCoroutineNodeOrder = coroutine; // 记录冻结的协程                
            }
            return;
        }
        else if (frozenCoroutineNodeOrder != null && frozenCoroutineNodeOrder == coroutine)
        {
            coroutine.Resume(); // 如果之前被冻结,现在恢复协程
            frozenCoroutineNodeOrder = null;
        }

        if (coroutine.WaitCondition == null)
        {
            //什么也不用做,走到MoveNextCoroutine进行初始化
        }
        else if (!coroutine.CanContinue(context)) return; // 检查协程是否满足继续执行的条件

        MoveNextCoroutine(coroutine);


    }
    private void MoveNextCoroutine(ICoroutineNode coroutine)
    {
        // 如果协程可以继续执行,调用 MoveNext() 继续执行协程
        if (coroutine.Fiber.MoveNext())
        {
            System.Object yieldCommand = coroutine.Fiber.Current; // 获取当前协程的返回值
            var coroutineWaitCondition = yieldCommand as ICoroutineWaitCondition;

            // 如果返回的是等待条件,添加等待条件到协程节点
            if (coroutineWaitCondition != null)
                coroutine.AddWaitCondition(coroutineWaitCondition);
            else
                throw new System.Exception("yield return type error");
        }
        else
        {
            coroutine.IsFinished = true; // 标记协程已完成
            coroutineQueue.Dequeue(); // 将完成的协程移出队列
        }
    }
}

等待条件设计

为了增强协程的灵活性,我设计了可扩展等待条件,可以等待特定帧数、时间、或其他协程的完成。

接口结构

cs 复制代码
// 定义等待条件的结构,实现该接口自定义可 yield return的对象
public interface ICoroutineWaitCondition
{
    /// <summary>
    /// 判断等待条件是否满足
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    bool IsConditionMet(ICoroutineContext context);

    /// <summary>
    /// 被暂停时会调用一次
    /// </summary>
    void Pause();

    /// <summary>
    /// 被恢复时会调用一次
    /// </summary>
    void Resume();
}

具体实现

cs 复制代码
#region 等待条件
// 等待帧的条件类
public class WaitForFrameCondition : ICoroutineWaitCondition
{
    private int waitFrame; // 等待帧数

    public WaitForFrameCondition(int frame)
    {
        if (frame <= 0)
        {
            throw new ArgumentException("Frame must be greater than 0.", nameof(frame));
        }
        waitFrame = frame;
    }


    bool ICoroutineWaitCondition.IsConditionMet(ICoroutineContext context)
    {
        waitFrame--;
        return waitFrame < 0;
    }

    // 无需实现
    void ICoroutineWaitCondition.Pause() { }

    // 无需实现
    void ICoroutineWaitCondition.Resume() { }
}

// 等待时间的条件类
public class WaitForTimeCondition : ICoroutineWaitCondition
{
    private float waitTime; // 等待时间

    public WaitForTimeCondition(float time)
    {
        waitTime = time;
    }

    bool ICoroutineWaitCondition.IsConditionMet(ICoroutineContext context)
    {
        waitTime -= Time.deltaTime;
        return waitTime < 0;
    }

    // 无需实现
    void ICoroutineWaitCondition.Pause() { }

    // 无需实现
    void ICoroutineWaitCondition.Resume() { }

}

// 等待其他协程完成的条件类
public class WaitForCoroutineCondition : ICoroutineWaitCondition
{
    private ICoroutineNode coroutine; // 被依赖的协程节点

    public WaitForCoroutineCondition(ICoroutineNode coroutine)
    {
        this.coroutine = coroutine;
    }

    // 检查依赖的协程是否已经完成
    bool ICoroutineWaitCondition.IsConditionMet(ICoroutineContext context) => coroutine.IsFinished;

    // 暂停依赖的协程
    void ICoroutineWaitCondition.Pause() => this.coroutine.Pause();

    // 恢复依赖的协程
    void ICoroutineWaitCondition.Resume() => this.coroutine.Resume();
}


#endregion

预留上下文成员

cs 复制代码
/// <summary>
/// 这个接口预留,作为拓展使用
/// </summary>
public interface ICoroutineContext
{
    //添加内容例如
    //当前Unity运行的帧数,运行时间
}
public struct CoroutineContext : ICoroutineContext
{
    //添加字段作为拓展
}

示例代码

并行调度器示例

通常我们会有多个调度器实例存在于多个非Mono实例中(比如数据层),多个调度器实例的方法在它们的表现层(继承Mono)或者一个全局的调度器的Update中执行UpdateCoroutines方法,将协程调度出去.

下面的实例代码为方便没有这么做.

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CoroutineSchedulerTest : MonoBehaviour
{
    CoroutineScheduler c=new CoroutineScheduler();
    // Start is called before the first frame update
    void Start()
    {
        c.AddCoroutine(TestTime());
        var t = c.AddCoroutine(TestTime1());
        c.AddCoroutine(TestCoroutine(t));
    }

    // Update is called once per frame
    void Update()
    {
       c.UpdateCoroutines();
    }
    IEnumerator TestTime()
    {
        yield return new WaitForTimeCondition(3);
        Debug.Log("等待三秒");
    }
    IEnumerator TestTime1()
    {

        yield return new WaitForTimeCondition(2);
        Debug.Log("等待两秒");
    }
    IEnumerator TestCoroutine(ICoroutineNode c)
    {
        yield return new WaitForCoroutineCondition(c);
        Debug.Log("等待一个协程完成,这里我等待的协程是等待两秒的协程");
    }
}

顺序调度器示例

cs 复制代码
using UnityEngine;
using System.Collections;

public class CoroutineSchedulerOrderTest : MonoBehaviour
{
    CoroutineSchedulerOrder coroutineSchedulerOrder = new CoroutineSchedulerOrder();
    private void Start()
    {
        coroutineSchedulerOrder.AddCoroutine(TestFrame());
        var t = coroutineSchedulerOrder.AddCoroutine(TestTime());
        coroutineSchedulerOrder.AddCoroutine(TestCoroutine(t));

    }
    private void Update()
    {
        coroutineSchedulerOrder.UpdateCoroutines();
    }

    IEnumerator TestFrame()
    {
        yield return new WaitForFrameCondition(1);
        Debug.Log("等待一帧");
    }
    IEnumerator TestTime()
    {

        yield return new WaitForTimeCondition(2);
        Debug.Log("等待两秒");
    }
    IEnumerator TestCoroutine(ICoroutineNode c)
    {
        yield return new WaitForCoroutineCondition(c);
        Debug.Log("等待一个协程完成,这里我等待的协程是等待两秒的协程");
    }
}

全部代码

cs 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

/// <summary>
/// 一个协程节点的基础结构
/// </summary>
public interface ICoroutineNode
{
    /// <summary>
    /// 是否完成
    /// </summary>
    bool IsFinished { get; set; }

    /// <summary>
    /// 是否暂停
    /// </summary>
    bool IsPaused { get; set; }

    /// <summary>
    /// 枚举器,代表协程的主体
    /// </summary>
    IEnumerator Fiber { get; }

    /// <summary>
    /// 协程等待条件
    /// </summary>
    ICoroutineWaitCondition WaitCondition { get; }

    /// <summary>
    /// 判断协程是否可以继续执行
    /// </summary>
    /// <param name="context">自定义上下文</param>
    /// <returns></returns>
    bool CanContinue(ICoroutineContext context);

    /// <summary>
    /// 添加一个等待条件
    /// </summary>
    /// <param name="condition"></param>
    void AddWaitCondition(ICoroutineWaitCondition condition);

    // 暂停协程
    void Pause();

    // 恢复协程
    void Resume();
}
/// <summary>
/// 一个协程调度器的基础结构
/// </summary>
public interface ICoroutineScheduler
{
    /// <summary>
    /// 剩余协程数量
    /// </summary>
    int CoroutineCount { get; }
    /// <summary>
    /// 向调度器中添加协程
    /// </summary>
    /// <param name="fiber">枚举器</param>
    /// <returns></returns>
    ICoroutineNode AddCoroutine(IEnumerator fiber);

    /// <summary>
    /// 暂停一个协程
    /// </summary>
    /// <param name="coroutine"></param>
    void PauseCoroutine(ICoroutineNode coroutine);
    /// <summary>
    /// 恢复一个协程
    /// </summary>
    /// <param name="coroutine"></param>
    void ResumeCoroutine(ICoroutineNode coroutine);
    /// <summary>
    /// 移除一个协程
    /// </summary>
    /// <param name="coroutine"></param>
    /// <returns></returns>
    ICoroutineNode RemoveCoroutine(ICoroutineNode coroutine);
    /// <summary>
    /// 移除全部协程
    /// </summary>
    void RemoveAllCoroutines();

    // 更新协程状态,在每帧调用
    void UpdateCoroutines(ICoroutineContext context = null);
}

// 定义等待条件的结构,实现该接口自定义可 yield return的对象
public interface ICoroutineWaitCondition
{
    /// <summary>
    /// 判断等待条件是否满足
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    bool IsConditionMet(ICoroutineContext context);

    /// <summary>
    /// 被暂停时会调用一次
    /// </summary>
    void Pause();

    /// <summary>
    /// 被恢复时会调用一次
    /// </summary>
    void Resume();
}
/// <summary>
/// 这个接口预留,作为拓展使用
/// </summary>
public interface ICoroutineContext
{
    //添加内容例如
    //当前Unity运行的帧数,运行时间
}
public struct CoroutineContext : ICoroutineContext
{
    //添加字段作为拓展
}
/// <summary>
/// 具体的协程节点实现
/// </summary>
public class CoroutineNode : ICoroutineNode
{
    // 协程主体(Fiber)
    public IEnumerator Fiber { get; private set; }

    // 是否完成
    public bool IsFinished { get; set; }

    // 是否暂停
    public bool IsPaused { get; set; }

    // 当前节点的等待条件
    private ICoroutineWaitCondition waitCondition = null;

    public ICoroutineWaitCondition WaitCondition => waitCondition;

    // 构造函数,传入一个协程(Fiber)
    public CoroutineNode(IEnumerator fiber)
    {
        Fiber = fiber;
        IsFinished = false;
        IsPaused = false;
    }

    // 添加等待条件
    public void AddWaitCondition(ICoroutineWaitCondition condition) => waitCondition = condition;

    // 检查等待条件是否满足,决定协程是否可以继续执行
    public bool CanContinue(ICoroutineContext context) => waitCondition.IsConditionMet(context);

    // 暂停等待条件
    public void Pause() => waitCondition.Pause();

    // 恢复等待条件   
    public void Resume() => waitCondition.Resume();
}

/// <summary>
/// 协程调度器,管理协程的生命周期和调度
/// 该调度器的协程有执行顺序,前一个协程彻底执行完,下一个协程才开始执行
/// </summary>
public class CoroutineSchedulerOrder : ICoroutineScheduler
{
    // 用于存储所有协程的队列
    private Queue<ICoroutineNode> coroutineQueue = new Queue<ICoroutineNode>();
    //被暂停协程
    private ICoroutineNode frozenCoroutineNodeOrder = null;

    //所剩协程数量
    public int CoroutineCount { get => coroutineQueue.Count; }
    // 向调度器中追加协程
    public ICoroutineNode AddCoroutine(IEnumerator fiber)
    {
        if (fiber == null)
        {
            return null;
        }

        ICoroutineNode coroutine = new CoroutineNode(fiber); // 创建协程节点
        coroutineQueue.Enqueue(coroutine); // 将节点加入队列
        return coroutine;
    }

    // 停止一个特定的协程,这将阻塞后续的协程
    public void PauseCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsPaused = true;
    }
    //恢复一个被暂停的协程
    public void ResumeCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsPaused = false;
    }


    // 移除一个协程,视为该协程完成了
    public ICoroutineNode RemoveCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsFinished = true;
        var coroutineList = coroutineQueue.ToList();
        coroutineList.Remove(coroutine);
        coroutineQueue = new Queue<ICoroutineNode>(coroutineList);
        return coroutine;
    }
    // 移除所有协程,视为已完成
    public void RemoveAllCoroutines()
    {
        foreach (var c in coroutineQueue) c.IsFinished = true;

        coroutineQueue.Clear();
    }

    // 更新协程状态,在每帧调用
    public void UpdateCoroutines(ICoroutineContext context = null)
    {
        int queueSize = coroutineQueue.Count;
        if (queueSize == 0) return;

        ICoroutineNode coroutine = coroutineQueue.Peek(); // 获取队首协程

        // 如果协程已完成,从队列中移除
        if (coroutine.IsFinished)
        {
            coroutineQueue.Dequeue();
            return;
        }

        // 如果协程暂停,执行暂停操作,并跳过本帧处理
        if (coroutine.IsPaused)
        {
            if (frozenCoroutineNodeOrder != null && frozenCoroutineNodeOrder == coroutine) return;
            if (coroutine.WaitCondition != null)
            {
                coroutine.Pause();
                frozenCoroutineNodeOrder = coroutine; // 记录冻结的协程                
            }
            return;
        }
        else if (frozenCoroutineNodeOrder != null && frozenCoroutineNodeOrder == coroutine)
        {
            coroutine.Resume(); // 如果之前被冻结,现在恢复协程
            frozenCoroutineNodeOrder = null;
        }

        if (coroutine.WaitCondition == null)
        {
            //什么也不用做,走到MoveNextCoroutine进行初始化
        }
        else if (!coroutine.CanContinue(context)) return; // 检查协程是否满足继续执行的条件

        MoveNextCoroutine(coroutine);


    }
    private void MoveNextCoroutine(ICoroutineNode coroutine)
    {
        // 如果协程可以继续执行,调用 MoveNext() 继续执行协程
        if (coroutine.Fiber.MoveNext())
        {
            System.Object yieldCommand = coroutine.Fiber.Current; // 获取当前协程的返回值
            var coroutineWaitCondition = yieldCommand as ICoroutineWaitCondition;

            // 如果返回的是等待条件,添加等待条件到协程节点
            if (coroutineWaitCondition != null)
                coroutine.AddWaitCondition(coroutineWaitCondition);
            else
                throw new System.Exception("yield return type error");
        }
        else
        {
            coroutine.IsFinished = true; // 标记协程已完成
            coroutineQueue.Dequeue(); // 将完成的协程移出队列
        }
    }
}

/// <summary>
/// 每帧会将列表的所有协程执行一遍,不保证顺序
/// </summary>
public class CoroutineScheduler : ICoroutineScheduler
{
    // 用于存储所有协程的队列
    private List<ICoroutineNode> coroutineList = new List<ICoroutineNode>();
    //被暂停的协程
    private HashSet<ICoroutineNode> frozenCoroutineHashSet = new HashSet<ICoroutineNode>();

    private List<ICoroutineNode> finishedCoroutines = new List<ICoroutineNode>();
    //所剩协程数量
    public int CoroutineCount { get => coroutineList.Count; }
    // 向调度器中添加协程
    public ICoroutineNode AddCoroutine(IEnumerator fiber)
    {
        if (fiber == null)
        {
            return null;
        }

        ICoroutineNode coroutine = new CoroutineNode(fiber); // 创建协程节点
        coroutineList.Add(coroutine); // 将节点加入队列
        return coroutine;
    }

    // 停止一个特定的协程,不影响其他协程
    public void PauseCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsPaused = true;
    }
    //恢复一个协程
    public void ResumeCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsPaused = false;
    }

    /// <summary>
    /// 移除一个协程,视为该协程完成了
    /// </summary>
    /// <param name="coroutine"></param>
    /// <returns></returns>
    public ICoroutineNode RemoveCoroutine(ICoroutineNode coroutine)
    {
        coroutine.IsFinished = true;
        coroutineList.Remove(coroutine);
        return coroutine;
    }
    // 移除所有协程,视为已完成
    public void RemoveAllCoroutines()
    {
        foreach (var c in coroutineList) c.IsFinished = true;

        coroutineList.Clear();
    }

    // 更新协程状态,在每帧调用
    public void UpdateCoroutines(ICoroutineContext context = null)
    {
        int queueSize = coroutineList.Count;
        if (queueSize == 0) return;

        foreach (var coroutine in coroutineList)
        {
            //已完成协程移除列表
            if (coroutine.IsFinished)
            {
                finishedCoroutines.Add(coroutine);
                continue;
            }
            //被暂停协程
            if (coroutine.IsPaused)
            {
                if (frozenCoroutineHashSet.Contains(coroutine)) continue;

                if (coroutine.WaitCondition != null)
                {
                    coroutine.Pause();
                    frozenCoroutineHashSet.Add(coroutine);
                }
                continue;
            }
            else if (frozenCoroutineHashSet.Contains(coroutine))//是否是被暂停过的协程要恢复
            {
                coroutine.Resume();
                frozenCoroutineHashSet.Remove(coroutine);
            }

            if (coroutine.WaitCondition == null)
            {

            }
            else if (!coroutine.CanContinue(context)) continue;

            MoveNextCoroutine(coroutine);
        }

        // 移除已经完成的协程
        foreach (var finished in finishedCoroutines)
        {
            coroutineList.Remove(finished);
        }

    }
    private void MoveNextCoroutine(ICoroutineNode coroutine)
    {
        // 如果协程可以继续执行,调用 MoveNext() 继续执行协程
        if (coroutine.Fiber.MoveNext())
        {
            System.Object yieldCommand = coroutine.Fiber.Current; // 获取当前协程的返回值
            var coroutineWaitCondition = yieldCommand as ICoroutineWaitCondition;

            // 如果返回的是等待条件,添加等待条件到协程节点
            if (coroutineWaitCondition != null)
                coroutine.AddWaitCondition(coroutineWaitCondition);
            else
                throw new System.Exception("yield return type error");
        }
        else
        {
            coroutine.IsFinished = true; // 标记协程已完成
            finishedCoroutines.Add(coroutine);
        }
    }
}


#region 等待条件
// 等待帧的条件类
public class WaitForFrameCondition : ICoroutineWaitCondition
{
    private int waitFrame; // 等待帧数

    public WaitForFrameCondition(int frame)
    {
        if (frame <= 0)
        {
            throw new ArgumentException("Frame must be greater than 0.", nameof(frame));
        }
        waitFrame = frame;
    }


    bool ICoroutineWaitCondition.IsConditionMet(ICoroutineContext context)
    {
        waitFrame--;
        return waitFrame < 0;
    }

    // 无需实现
    void ICoroutineWaitCondition.Pause() { }

    // 无需实现
    void ICoroutineWaitCondition.Resume() { }
}

// 等待时间的条件类
public class WaitForTimeCondition : ICoroutineWaitCondition
{
    private float waitTime; // 等待时间

    public WaitForTimeCondition(float time)
    {
        waitTime = time;
    }

    bool ICoroutineWaitCondition.IsConditionMet(ICoroutineContext context)
    {
        waitTime -= Time.deltaTime;
        return waitTime < 0;
    }

    // 无需实现
    void ICoroutineWaitCondition.Pause() { }

    // 无需实现
    void ICoroutineWaitCondition.Resume() { }

}

// 等待其他协程完成的条件类
public class WaitForCoroutineCondition : ICoroutineWaitCondition
{
    private ICoroutineNode coroutine; // 被依赖的协程节点

    public WaitForCoroutineCondition(ICoroutineNode coroutine)
    {
        this.coroutine = coroutine;
    }

    // 检查依赖的协程是否已经完成
    bool ICoroutineWaitCondition.IsConditionMet(ICoroutineContext context) => coroutine.IsFinished;

    // 暂停依赖的协程
    void ICoroutineWaitCondition.Pause() => this.coroutine.Pause();

    // 恢复依赖的协程
    void ICoroutineWaitCondition.Resume() => this.coroutine.Resume();
}


#endregion
相关推荐
Red Red3 分钟前
GEO数据库提取疾病样本和正常样本|GEO数据库区分疾病和正常样本|直接用|生物信息|生信
开发语言·数据库·笔记·学习·r语言·c#·生物信息
Zhen (Evan) Wang2 小时前
What is the new in C#11?
开发语言·c#
IT规划师6 小时前
C#|.net core 基础 - 值传递 vs 引用传递
c#·.netcore·值传递·引用传递
__water8 小时前
『功能项目』窗口可拖拽脚本【59】
c#·unity引擎·窗口可拖拽
Aa134517650259 小时前
c#中使用sql防注入方式写入数据
数据库·sql·c#
ling1s11 小时前
C#基础(12)递归函数
开发语言·算法·c#
熊思宇11 小时前
C# 离线激活码的实现方式
c#
来一杯龙舌兰11 小时前
【JAVA】自动生成常量类、自动生成所需代码(附源码)
java·开发语言·c#·自动生成代码
看山还是山,看水还是。13 小时前
c#进度条实现方法
c语言·开发语言·笔记·c#
孑么13 小时前
C# 委托与事件 观察者模式
开发语言·unity·c#·游戏引擎·游戏程序