Unity Term Book
Scripting & Lifecycle

Coroutine

A Coroutine is a special method that can pause (yield return) and resume later without blocking the main thread — letting gameplay logic span many frames.

Imagine...

A Coroutine is like a bookmark in a book. You are reading (running code), hit a wait → drop a bookmark (yield return), close the book to do other work. Next frame, Unity opens the book at the bookmark and continues. Unlike async/await, coroutines stay on the main thread — not more thread-safe, but fully wired into the Unity lifecycle.

The concept in detail

A Coroutine is an IEnumerator — C# iterator protocol. Each yield return yields a yield instruction: null (wait 1 frame), WaitForSeconds(t), WaitForFixedUpdate(), WaitUntil(predicate)

StartCoroutine vs async: Coroutines run on Unity’s main thread and can touch every Unity API. async/await can resume on another thread (unless you keep the SynchronizationContext). Coroutines stop when the MonoBehaviour is disabled/destroyed; async does not stop by itself.

Cache WaitForSeconds: new WaitForSeconds(t) allocates each call. Store and reuse: var wait = new WaitForSeconds(2f); inside a loop.

Stopping: StopCoroutine() needs the Coroutine handle from StartCoroutine. StopAllCoroutines() stops every coroutine on that MonoBehaviour.

Diagram: Coroutine vs Update

❌ Update approach (polling)

void Update() {

if (Time.time > startTime + 2f)

DoAction(); // check every frame!

}

⚡ Checks the condition every frame even when idle

✅ Coroutine approach

IEnumerator Delayed() {

yield return new WaitForSeconds(2f);

DoAction(); // runs once

}

✅ No CPU cost while waiting

Coroutine timeline across frames:

Start()
→ StartCoroutine
F1: run until yield
⏸ pause
F2,F3: waiting...
F4: resume → done

Hands-on steps

1

Define an IEnumerator

A method returning IEnumerator with at least one yield return.

2

Start with StartCoroutine

Coroutine co = StartCoroutine(MyCoroutine()) — keep the handle to stop later.

3

Cache WaitForSeconds

Create once at the top of the class and reuse in loops: _waitSec = new WaitForSeconds(0.5f).

4

Stop when needed

StopCoroutine(co) or, in OnDisable, StopAllCoroutines().

Interactive simulator

Pick a Coroutine type and press Start to watch it advance across frames.

Code Execution

Frame Timeline

Press StartCoroutine...

Frame: 0|State: Idle|Pick a Coroutine type and press Start

Code example

Basic

Basic FadeOut effect and delayed action.

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

Code example

Advanced

Coroutine chaining (yield return another coroutine) and 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;
  }
}

📌 Quick recap

  • yield return null = wait one frame
  • Cache new WaitForSeconds(t) — avoid GC every frame
  • Coroutines stop when the GO is disabled/destroyed
  • yield return StartCoroutine() to chain coroutines

⚠️ Common mistakes

  • ❌ Forgetting the Coroutine reference

    Cannot StopCoroutine precisely without a handle

    ✅ _co = StartCoroutine(MyRoutine())

  • ❌ new WaitForSeconds() inside a while loop

    Allocates every iteration → GC spam

    ✅ Cache: var w = new WaitForSeconds(0.1f);