河北省住房和城乡建设部网站首页,什么网站做一件代发,wordpress自动评论王,东莞常平核酸检测点一. 什么是协程
协程概述 在 Unity 中#xff0c;协程#xff08;Coroutine#xff09;是一种非常常用的机制#xff0c;用于非阻塞地处理需要跨越多个帧、等待某些条件或延迟一段时间才能完成的任务。Unity 的协程通过 C# 的 IEnumerator 和 yield return 实现#xff0…一. 什么是协程
协程概述 在 Unity 中协程Coroutine是一种非常常用的机制用于非阻塞地处理需要跨越多个帧、等待某些条件或延迟一段时间才能完成的任务。Unity 的协程通过 C# 的 IEnumerator 和 yield return 实现使得你可以在游戏主线程中以一种简洁的方式执行异步操作而不需要使用复杂的多线程或回调。 协程允许你暂停代码的执行并在稍后的某个时间点恢复执行。适合处理游一些常见需求比如 1延迟一定时间后执行代码。 2等待某个条件如动画完成、玩家输入后继续执行。 3 逐帧执行任务而不是一次性完成避免阻塞主线程。
下面一些示例
void Start()
{// 启动协程StartCoroutine(MyCoroutine());Debug.Log(因为是协程,所以不会阻塞打印);
}IEnumerator MyCoroutine()
{Debug.Log(Before waiting);// 暂停协程 2 秒yield return new WaitForSeconds(2);Debug.Log(After waiting 2 seconds);
}
执行流程 当 StartCoroutine(MyCoroutine()) 被调用时MyCoroutine 方法开始执行,先输出 Before waiting. 在遇到yield return时会立即返回控制权给上级调用者,这里上级调用者是主线程,打印因为是协程,所以不会阻塞打印,并且执行到这里的状态会被保存下来,以便条件(这里的条件是等待两秒)完成后继续向下执行. 2 秒后协程恢复继续执行输出 After waiting 2 seconds。 yield return 的使用
协程中的 yield return 是关键它决定了协程什么时候暂停以及在什么条件下恢复。
yield return null//暂停协程直到下一帧。它相当于让协程在下一帧继续执行。
yield return new WaitForSeconds(x)//暂停协程 x 秒后继续执行。 yield return new WaitUntil(predicate)//等待直到某个条件为 true 时继续执行。
如yield return new WaitUntil(() someCondition true); // 等待某个条件成立
yield return new WaitWhile(predicate)//等待直到某个条件为 false 时继续执行。 yield return StartCoroutine(otherCoroutine)//等待另一个协程执行完毕后再继续执行当前协程。
yield return WaitForEndOfFrame()
//暂停协程执行直到当前帧的渲染完成。可以在本帧所有操作完成后再进行一些后处理。
yield return WaitForFixedUpdate()
//暂停协程执行直到下一次物理更新即 FixedUpdate 调用发生时继续。常用于与物理更新频率保持同步的操作。
常见用例
1. 延迟执行任务
协程经常被用来在一段时间后执行某个任务。以下是一个延迟 3 秒后执行的例子
IEnumerator DelayedAction()
{yield return new WaitForSeconds(3); // 等待 3 秒Debug.Log(This happens after 3 seconds);
}2. 等待玩家输入
协程可以暂停代码执行直到玩家做出某些输入比如按下某个键
IEnumerator WaitForPlayerInput()
{Debug.Log(Waiting for player to press Space);yield return new WaitUntil(() Input.GetKeyDown(KeyCode.Space)); // 等待玩家按下空格键Debug.Log(Player pressed Space);
}3. 多协程依赖执行
你可以让一个协程等待另一个协程执行完成后再继续。例如下面的例子展示了如何等待两个协程执行完成后再执行后续逻辑
IEnumerator Sequence()
{yield return StartCoroutine(Task1());yield return StartCoroutine(Task2());Debug.Log(Both tasks completed);
}IEnumerator Task1()
{Debug.Log(Task 1 started);yield return new WaitForSeconds(2);Debug.Log(Task 1 completed);
}IEnumerator Task2()
{Debug.Log(Task 2 started);yield return new WaitForSeconds(1);Debug.Log(Task 2 completed);
}4. 逐帧处理任务
有时我们希望任务能在多个帧内逐渐完成而不是一次性完成以避免卡顿。协程可以轻松实现这一点
IEnumerator DoTaskOverTime()
{for (int i 0; i 10; i){Debug.Log(Processing step: i);yield return null; // 等待到下一帧再继续执行}
}协程的注意事项 协程不是真正的多线程 虽然协程可以暂停执行并在之后恢复但它们始终在主线程中运行。也就是说协程不会并发执行只是通过暂停和恢复的方式避免了阻塞主线程的情况。 Unity 的协程是始终在主线程上运行的并不涉及多线程处理。但因为协程使用了 yield return 进行暂停所以它可以让其他代码继续执行而不会阻塞主线程的运行。这种非阻塞的行为有时会让人误以为协程运行在另一个线程上但实际上它仍然依赖主线程的调度。 假设你启动了一个协程在协程中等待 2 秒后打印一段话而协程下面是一个耗时的方法。
void Start()
{// 启动协程StartCoroutine(MyCoroutine());// 耗时操作HeavyOperation();
}IEnumerator MyCoroutine()
{Debug.Log(Before waiting);// 暂停协程 2 秒yield return new WaitForSeconds(2);Debug.Log(After waiting 2 seconds);
}void HeavyOperation()
{// 模拟一个非常耗时的操作比如计算密集型任务Debug.Log(Heavy operation started);for (int i 0; i 1000000000; i) { } // 假设这是一个耗时的操作Debug.Log(Heavy operation finished);
}这里就会出现一个问题: 因为 HeavyOperation() 占用了大量的 CPU 时间主线程处理其他任务包括更新协程的进度可能会延迟。这意味着即使 2 秒时间已经过去了但如果 HeavyOperation() 还没执行完协程中的 After waiting 2 seconds 也会推迟打印。 当协程遇到 yield return 时执行会挂起并交出控制权让上级调用者通常是 Unity 的主线程继续执行其他任务。然后当条件满足后协程会恢复执行继续从挂起的地方往下执行。 注意这个条件满足之后,说的不清晰,让我们进一步说清楚协程恢复的时机:当协程的条件满足时例如等待时间结束Unity 不会立即打断主线程的操作。协程的恢复是在主线程的当前帧所有任务结束之后才会被调度执行。 上面的例子是等待两秒,但是下面正在执行一个需要大量cpu的操作,两秒已经过去了还没有执行完,Unity并不会打断这个操作,所以这个等待秒数的行为并不是绝对靠谱的. 这是协程与多线程的关键区别之一。协程不会在任何时间打断主线程的工作而是等到主线程“空闲”时例如一帧的任务全部完成才会恢复执行。 在大多数情况下协程的等待时间比如 WaitForSeconds(2) 等待 2 秒是靠谱的但确实存在一些特殊情况可能导致时间的精确度不如帧数那样可靠。具体来说帧是更为精确和可控的而秒的等待可能会受到主线程的负载、性能瓶颈等因素的影响。 在 Unity 中协程的恢复是基于帧更新的。Unity 的主线程在每一帧都会处理脚本、物理、渲染等任务如果主线程负载过重或者有一些耗时操作帧率可能会降低例如从 60FPS 降到 30FPS进而影响时间的精度。 协程的生命周期 如果启动协程的 MonoBehaviour 被销毁或禁用协程会自动停止。因此协程的生命周期依赖于启动它的对象。
如果你希望手动停止协程可以使用 StopCoroutine() 方法。
Coroutine myCoroutine;void Start()
{myCoroutine StartCoroutine(MyCoroutine());
}void StopMyCoroutine()
{if (myCoroutine ! null){StopCoroutine(myCoroutine);}
}YieldInstruction YieldInstruction 类是一个抽象基类用于控制协程的执行。通过使用 yield 关键字与 YieldInstruction 子类如 WaitForSeconds, WaitForEndOfFrame, WaitForFixedUpdate 等配合可以让协程暂停一段时间或者直到满足某些条件才继续执行。
CustomYieldInstruction
继承自 YieldInstruction 的类 CustomYieldInstruction 是一个可继承的类允许开发者自定义协程的等待条件。通过重写 keepWaiting 属性可以定义更复杂的逻辑来控制协程何时继续执行
通过继承 CustomYieldInstruction你可以实现复杂的等待条件。
public class WaitForCondition : CustomYieldInstruction
{private Funcbool condition;public WaitForCondition(Funcbool condition){this.condition condition;}public override bool keepWaiting{get { return !condition(); }}
}// 使用自定义的协程等待条件
IEnumerator CustomWaitExample()
{Debug.Log(Waiting for custom condition...);yield return new WaitForCondition(() Time.time 5);Debug.Log(Condition met, continuing...);
}