Unity实现自己的协程系统

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

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

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

系统结构概述

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

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

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

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

协程节点设计

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

维护协程的主体:即IEnumerator。

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

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

接口结构

/// <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();
}

具体实现

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):允许多个协程并行执行,每帧都会更新所有未完成的协程。

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

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

接口结构

/// <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);
}

具体实现

并行调度器
/// <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);elsethrow new System.Exception("yield return type error");}else{coroutine.IsFinished = true; // 标记协程已完成finishedCoroutines.Add(coroutine);}}
}
顺序调度器
/// <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);elsethrow new System.Exception("yield return type error");}else{coroutine.IsFinished = true; // 标记协程已完成coroutineQueue.Dequeue(); // 将完成的协程移出队列}}
}

等待条件设计

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

接口结构

// 定义等待条件的结构,实现该接口自定义可 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();
}

具体实现


#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

预留上下文成员

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

示例代码

并行调度器示例

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

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CoroutineSchedulerTest : MonoBehaviour
{CoroutineScheduler c=new CoroutineScheduler();// Start is called before the first frame updatevoid Start(){c.AddCoroutine(TestTime());var t = c.AddCoroutine(TestTime1());c.AddCoroutine(TestCoroutine(t));}// Update is called once per framevoid 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("等待一个协程完成,这里我等待的协程是等待两秒的协程");}
}

顺序调度器示例

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("等待一个协程完成,这里我等待的协程是等待两秒的协程");}
}

全部代码

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);elsethrow 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);elsethrow 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/424564.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

效率神器来了:AI工具手把手教你快速提升工作效能

随着科技的进步&#xff0c;AI工具已经成为提升工作效率的关键手段。本文将介绍一些实用的AI工具和方法&#xff0c;帮助你自动化繁琐的重复性任务、优化数据管理、促进团队协作与沟通&#xff0c;并提升决策质量。 背景&#xff1a;OOP AI-免费问答学习交流-GPT 自动化重复性任…

IP纯净度对跨境电商有哪些影响

在全球化贸易的浪潮中&#xff0c;跨境电商凭借其打破地理界限的能力&#xff0c;成为推动国际贸易的重要力量。然而&#xff0c;跨境电商的运营并非没有挑战&#xff0c;其中IP纯净度是影响其成功的关键因素之一。本文将探讨IP纯净度对跨境电商运营的多方面影响&#xff0c;并…

从单体到微服务:FastAPI ‘挂载’子应用程序的转变

在现代 Web 应用开发中&#xff0c;模块化架构是一种常见的设计模式&#xff0c;它有助于将大型应用程序分解为更小、更易于管理的部分。FastAPI&#xff0c;作为一个高性能的 Python Web 框架&#xff0c;提供了强大的支持来实现这种模块化设计。通过“挂载”子应用程序&#…

WebGL系列教程八(GLSL着色器基础语法)

目录 1 前言2 基本原则3 基本数据类型4 顶点着色器和片元着色器4.1 声明4.2 初始化项目4.3 赋值 5 结构体5.1 声明5.2 赋值 6 函数6.1 基本结构6.2 自定义函数6.3 常用内置函数 7 精度8 其他9 总结 1 前言 通过前七讲&#xff0c;我们已经见过了WebGL中的部分基础语法&#xff…

webpack5-手撸RemoveConsolePlugin插件

写在前面 其实呢&#xff0c;这个东西也就那样&#xff0c;主要是我们得清楚webpack构建过程中的生命周期钩子&#xff0c; 就拿这个插件来说&#xff0c;我们想要把输出的js文件里面的内容中的console语句去掉&#xff0c;那么我们就需要找到webpack处理完文件时的钩子&#…

海外VS国内:网安上市公司人均创收对比

二级市场分析章节中分析了中国网络网络安全上市公司人均创收63.2万、人均毛利37.6万&#xff0c;人均创利-1.6万。 有网友问了&#xff1a;海外网络安全公司的人均情况如何&#xff1f;那么让我们一起看看吧。 我们统计了在海外上市的28家主要网络安全公司的2023年的人均情况&…

2020ICPC上海 D - Walker M - Gitignore

D: 首先显然要二分,判断当前二分的mid时间下是否能满足走满0~n 枚举所有情况,这里按照左,右起点p1,p2分别讨论 p1向左 p2向左(以下向左和向右都代表向左或者向右到墙,而不代表初速度方向)&#xff0c;只需要计算p1或者p2反弹之后还能走距离n就是合法 p1向左 p2向右&#xff…

PHP智慧家政同城服务家政系统小程序源码

智慧家政&#xff0c;同城服务新篇章 —— 探索家政系统的无限可能 开篇&#xff1a;走进智慧家政时代 在这个快节奏的生活中&#xff0c;每一分每一秒都显得尤为珍贵。当忙碌成为常态&#xff0c;如何让家成为真正的避风港&#xff1f;答案或许就藏在“智慧家政同城服务家政…

155K Star,Python 入门到进阶最佳学习资源

Hi&#xff0c;骚年&#xff0c;我是大 G&#xff0c;公众号「GitHub 指北」会推荐 GitHub 上有趣有用的项目&#xff0c;一分钟 get 一个优秀的开源项目&#xff0c;挖掘开源的价值&#xff0c;欢迎关注。 导语 如果你正在寻找一个全面、系统、深入的 Python 学习项目&#…

合资油车断崖式崩盘,买车的千万慎重了

文 | AUTO芯球 作者 | 雷慢 合资车&#xff0c;燃油车全体大逃亡的时候来了&#xff0c; 你敢信吗&#xff0c;8月份&#xff0c;国内新能源汽车零售渗透率达到54%&#xff0c; 我给大家讲个冷笑话&#xff0c; 几个月前还有车企老总说什么&#xff0c; “只要传统车企一发…

pyflink 安装和测试

FPY Warning! 安装 apache-Flink # pip install apache-Flink -i https://pypi.tuna.tsinghua.edu.cn/simple/ Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/ Collecting apache-FlinkDownloading https://pypi.tuna.tsinghua.edu.cn/packages/7f/a3/ad502…

软件测试学习笔记丨Docker 安装、管理、搭建服务

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/32192 容器&#xff08;Docker&#xff09;技术的价值 保证环境一致性&#xff0c;只要使用相同镜像部署就可以保证一致性。轻量级虚拟化访问&#xff0c;运行更快&#xff0c;资源更小。同时…

微信小程序使用 ==== 粘性布局

目录 Chrome杀了个回马枪 position:sticky简介 你可能不知道的position:sticky 深入理解粘性定位的计算规则 粘性定位其他特征 代码实现 微信小程序在scroll-view中使用sticky Chrome杀了个回马枪 position:sticky早有耳闻也有所了解&#xff0c;后来&#xff0c;Chro…

长业务事务的离线并发问题

事务指代一组操作同时成功或同时失败&#xff0c;事务可分为两类&#xff1a; 系统事务&#xff1a;即关系数据库事务&#xff0c;一次数据库连接中由start transaction或begin开启&#xff0c;commit表示提交&#xff0c;rollback表示回滚&#xff1b;业务事务&#xff1a;完…

力扣题解2848

大家好&#xff0c;欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述&#xff08;简单&#xff09;&#xff1a; 与车相交的点 给你一个下标从 0 开始的二维整数数组 nums 表示汽车停放在数轴上的坐标。对于任意下标 i&#xff0c;nums[i] [starti, endi] &…

html+css+js网页设计 旅游 厦门旅游网10个页面

htmlcssjs网页设计 旅游 厦门旅游网10个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&am…

SpringBoot权限认证-Sa-Token的使用与详解

本文详细介绍了Sa-Token在Java项目中的使用方法&#xff0c;包括Sa-Token的基本概念、与其他权限框架的比较、基本语法和高级用法&#xff0c;并通过实例讲解了如何在项目中集成和使用Sa-Token。作为一款轻量级Java权限认证框架&#xff0c;Sa-Token在简化权限管理、提高开发效…

『功能项目』切换职业技能面板【49】

我们打开上一篇48切换职业面板的项目&#xff0c; 本章要做的事情是制作第二职业法师技能面板、第三职业面板并且完成切换 双击打开Canvas进入预制体空间 复制三个技能栏面板 重命名 设置第一技能栏 设置第二职业技能栏 设置第三职业技能栏 修改脚本&#xff1a;ChangeProfess…

FreeRTOS—任务通知

一&#xff0c;概念介绍 队列、信号量、事件组等IPC技术都需要创建一个中间对象进程之间通过这些中间对象进行通讯或同步。创建对象就需要分配内存&#xff0c;占用一定内存。 二&#xff0c;任务通知的特点&#xff1a; 一个任务或ISR向另外一个指定的任务发送通知&#xff0c…

PhpStudy下载安装使用学习

一、官网下载 官网地址&#xff1a;Windows版phpstudy下载 - 小皮面板(phpstudy)https://old.xp.cn/download.html 【首页】选择Windows版&#xff0c;进行下载 下载完成是一个压缩包的形式&#xff0c;解压得到一个.exe的执行文件&#xff0c;点击执行安装程序&#xff08;注…