Coroutine
Coroutine 是 Unity 中可暂停(yield return)并稍后恢复的特殊方法,不会阻塞主线程,从而让玩法逻辑跨越许多帧执行。适合延时、淡入淡出、分帧加载与回合流程;理解与 Update、async/await 的取舍,能帮程序与玩法设计师写出清晰、可停止且不易造成内存泄漏的异步逻辑。
想象一下...
Coroutine 就像书里的书签。你在阅读(运行代码),遇到等待 → 放下书签(yield return),合上书去做别的事。下一帧,Unity 从书签处打开书继续。与 async/await 不同,协程始终在主线程——并非更线程安全,但完全接入 Unity 生命周期。
概念详解
Coroutine 是 IEnumerator——C# 迭代器协议。每次
yield return 交出一条 yield instruction:
null(等 1 帧)、WaitForSeconds(t)、
WaitForFixedUpdate()、WaitUntil(predicate)…
StartCoroutine 与 async:Coroutine 在 Unity 主线程上运行,
可以调用所有 Unity API。async/await 可能在其他线程恢复(除非你保留 SynchronizationContext)。
MonoBehaviour 被禁用/销毁时 Coroutine 会停止;async 不会自行停止。
缓存 WaitForSeconds:
new WaitForSeconds(t) 每次调用都会分配。请存储并复用:
var wait = new WaitForSeconds(2f); 放在循环内复用。
停止:StopCoroutine() 需要
StartCoroutine 返回的 Coroutine 句柄。
StopAllCoroutines() 停止该 MonoBehaviour 上的所有协程。
示意图:Coroutine 与 Update
❌ Update approach (polling)
void Update() {
if (Time.time > startTime + 2f)
DoAction(); // 每帧检查!
}
⚡ 即使空闲也每帧检查条件
✅ Coroutine approach
IEnumerator Delayed() {
yield return new WaitForSeconds(2f);
DoAction(); // 只运行一次
}
✅ 等待时不占用 CPU
Coroutine 跨多帧时间线:
动手步骤
定义 IEnumerator
返回 IEnumerator 的方法,且至少有一个 yield return。
用 StartCoroutine 启动
Coroutine co = StartCoroutine(MyCoroutine()) —— 保留句柄以便稍后停止。
缓存 WaitForSeconds
在类顶部创建一次,在循环中复用:_waitSec = new WaitForSeconds(0.5f)。
需要时停止
StopCoroutine(co),或在 OnDisable 中用 StopAllCoroutines()。
交互模拟器
选择一种 Coroutine 类型并按 Start,观察它跨帧推进。
Code Execution
Frame Timeline
点击 StartCoroutine...
代码示例
基础基础 FadeOut 效果与延迟动作。
using System.Collections;
using UnityEngine;
public class UIAnimator : MonoBehaviour
{
private CanvasGroup _group;
void Awake() => _group = GetComponent<CanvasGroup>();
public void FadeOut(float duration)
{
StartCoroutine(FadeOutRoutine(duration));
}
private IEnumerator FadeOutRoutine(float duration)
{
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
_group.alpha = 1f - (elapsed / duration);
yield return null; // wait one frame
}
_group.alpha = 0f;
gameObject.SetActive(false);
}
public Coroutine DoAfterDelay(float delay, System.Action action)
{
return StartCoroutine(DelayedAction(delay, action));
}
private IEnumerator DelayedAction(float delay, System.Action action)
{
yield return new WaitForSeconds(delay);
action?.Invoke();
}
}代码示例
进阶Coroutine 链式调用(yield return 另一个协程)与 WaitUntil。
using System.Collections;
using UnityEngine;
public class LevelTransition : MonoBehaviour
{
private bool _loadingDone = false;
private readonly WaitForSeconds _framePause = new(Time.fixedDeltaTime);
public void TransitionToNextLevel()
{
StartCoroutine(TransitionSequence());
}
private IEnumerator TransitionSequence()
{
// 1. Run another Coroutine and wait for it
yield return StartCoroutine(FadeToBlack(1.0f));
// 2. Load scene async
_loadingDone = false;
StartCoroutine(LoadSceneAsync());
// 3. Wait until load finishes (WaitUntil)
yield return new WaitUntil(() => _loadingDone);
// 4. Fade in the new scene
yield return StartCoroutine(FadeFromBlack(0.5f));
}
private IEnumerator LoadSceneAsync()
{
var op = UnityEngine.SceneManagement.SceneManager
.LoadSceneAsync("Level_02");
op.allowSceneActivation = false;
while (op.progress < 0.9f)
yield return null;
op.allowSceneActivation = true;
_loadingDone = true;
}
}📌 快速记忆
- ▸
yield return null= 等待一帧 - ▸缓存
new WaitForSeconds(t)—— 避免每帧 GC - ▸GO 被禁用/销毁时 Coroutine 会停止
- ▸
yield return StartCoroutine()可串联协程
⚠️ 常见错误
❌ 忘记保存 Coroutine 引用
没有句柄就无法精确 StopCoroutine
✅ _co = StartCoroutine(MyRoutine())
❌ 在 while 循环里 new WaitForSeconds()
每次迭代都分配 → GC 刷屏
✅ 缓存:var w = new WaitForSeconds(0.1f);