Unity Term Book
スクリプティングとライフサイクル

Coroutine

Coroutine は yield return で一時停止し、後で再開できる特殊なメソッドです。メインスレッドをブロックせずに、複数フレームにまたがるゲームロジックを書けます。WaitForSeconds のキャッシュ、コルーチン連鎖、StopCoroutine による停止の扱い方まで基礎を学べます。

想像してみてください...

Coroutine は本のしおりのようです。読んでいて(コード実行)、待ちに当たるとしおりを挟み(yield return)、他の作業へ。次フレームで Unity はしおりの位置から再開します。async/await と違い、コルーチンはメインスレッドに留まり——スレッドセーフが増えるわけではないが、Unity ライフサイクルに完全に組み込まれます。

概念の詳細

Coroutine は IEnumerator——C# のイテレータプロトコルです。各 yield returnyield instruction を返します: null(1 フレーム待つ)、WaitForSeconds(t)WaitForFixedUpdate()WaitUntil(predicate)

StartCoroutine vs async: Coroutine は Unity の メインスレッドで走り、すべての Unity API に触れられます。async/await は (SynchronizationContext を保たない限り)別スレッドで再開し得ます。Coroutine は MonoBehaviour が 無効/破棄されると止まりますが、async は自動では止まりません。

WaitForSeconds をキャッシュ: new WaitForSeconds(t) は呼び出しごとに割り当てます。保存して再利用: var wait = new WaitForSeconds(2f); をループ内で使います。

停止: StopCoroutine() には StartCoroutine が返す Coroutine ハンドルが必要です。 StopAllCoroutines() はその MonoBehaviour 上の全コルーチンを止めます。

ダイアグラム:Coroutine vs Update

❌ Update approach (polling)

void Update() {

if (Time.time > startTime + 2f)

DoAction(); // 毎フレームチェック!

}

⚡ 不要でも毎フレーム条件を確認

✅ Coroutine approach

IEnumerator Delayed() {

yield return new WaitForSeconds(2f);

DoAction(); // 1 回だけ実行

}

✅ 待機中は CPU を使わない

Coroutine の複数フレームタイムライン:

Start()
→ StartCoroutine
F1: yield まで実行
⏸ pause
F2,F3: 待機...
F4: 再開 → 完了

ハンズオン手順

1

IEnumerator を定義

IEnumerator を返し、少なくとも 1 つの yield return を持つメソッド。

2

StartCoroutine で開始

Coroutine co = StartCoroutine(MyCoroutine()) —— 後で止めるためにハンドルを保持。

3

WaitForSeconds をキャッシュ

クラス先頭で一度作りループで再利用:_waitSec = new WaitForSeconds(0.5f)

4

必要時に停止

StopCoroutine(co)、または OnDisable で StopAllCoroutines()

インタラクティブシミュレーター

Coroutine タイプを選び Start を押すと、フレームをまたいで進む様子を確認できます。

Code Execution

Frame Timeline

StartCoroutine を押す...

Frame: 0|State: Idle|Coroutine の種類を選んで Start

コード例

基本

基本的な 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();
  }
}

コード例

上級

コルーチン連鎖(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 = 1 フレーム待つ
  • new WaitForSeconds(t) をキャッシュ——毎フレーム GC を避ける
  • GO が無効/破棄されるとコルーチンは止まる
  • 連鎖には yield return StartCoroutine()

⚠️ よくあるミス

  • ❌ Coroutine 参照を残さない

    ハンドルなしでは StopCoroutine を正確に呼べない

    ✅ _co = StartCoroutine(MyRoutine())

  • ❌ while ループ内で new WaitForSeconds()

    反復ごとに割り当て → GC スパム

    ✅ キャッシュ:var w = new WaitForSeconds(0.1f);