【Unity】Unityの非同期処理!コルーチンの使い方をマスターしよう

作成: 2025-12-07最終更新: 2026-07-13

数秒待ってから処理を再開したい、重い処理を少しずつ分割して実行したい。そんな時に役立つUnityの強力な機能「コルーチン」。IEnumeratorとyield returnの仕組みを初心者にも分かりやすく解説します。

「3秒後に敵を出現させたい」「フェードアウトを少しずつ進めたい」——こうした 時間をまたぐ処理Updateとタイマー変数で書こうとすると、コードはあっという間に複雑になります。

Unityにはこのための専用機能、 コルーチン (Coroutine) があります。「処理の途中で休憩して、指定したタイミングで続きから再開できる関数」で、時間経過を伴う処理や重い処理の分割が驚くほどシンプルに書けます。この記事ではIEnumeratoryield returnを中心に、仕組みと使い方を解説します。

コルーチンのイメージ。処理の途中で休憩を挟み、続きから再開できる

この記事でわかること

  • コルーチンの仕組み(中断と再開)
  • IEnumeratoryield returnStartCoroutineの3点セット
  • 時間待ち・フレーム分割などの中断条件の使い分け
  • コルーチンの停止方法(StopCoroutine

Sponsored

コルーチンとは?

コルーチンは、 処理を一時的に中断し、後で特定のタイミングから再開できる 特殊な関数です。通常の関数は一度呼び出されると最後まで実行されなければなりませんが、コルーチンは処理の途中でUnityに制御を一旦戻し、「次のフレームで再開して」「5秒後に再開して」といった指示を出すことができます。

通常の関数とコルーチンの違いを表す図。通常の関数はゴールまで一気に走り切るが、コルーチンはyieldの旗で休憩を挟み、指定のタイミングで続きから再開できる

これにより、Update関数を複雑にすることなく、時間ベースのイベントや処理の分割を直感的に実装できます。

コルーチンが活躍する定番の場面はこの3つです。どれも「時間をまたぐ処理」であることが共通点です。

コルーチンの活用実例。3秒後に敵を出現させる時間差処理、画面を徐々に暗くするフェード演出、ダメージを受けたキャラクターの点滅

コルーチンの基本的な使い方

コルーチンを実装するには、以下の3つの要素が必要です。

  1. IEnumerator 型の戻り値を持つ関数: これがコルーチン本体です。IEnumeratorはC#の標準的なインターフェースで、「列挙可能なもの」を意味します。
  2. yield return: コルーチンの処理を中断し、Unityに制御を戻すためのキーワードです。この後に再開条件を指定します。
  3. StartCoroutine(): 作成したコルーチンを開始するためのメソッドです。

時間を待つコルーチン

最も基本的な使い方は、一定時間待ってから処理を再開するパターンです。yield return new WaitForSeconds(float seconds)を使います。

using System.Collections;
using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
    void Start()
    {
        Debug.Log("コルーチンを開始します。");
        // メソッド呼び出しの形でコルーチンを開始
        StartCoroutine(WaitAndPrint());
    }

    // IEnumeratorを返す関数としてコルーチンを定義
    IEnumerator WaitAndPrint()
    {
        // 3秒間処理を中断
        yield return new WaitForSeconds(3.0f);

        // 3秒後にここから処理が再開される
        Debug.Log("3秒経過しました!");
    }
}

このスクリプトを実行すると、まず「コルーチンを開始します。」と表示され、その3秒後に「3秒経過しました!」と表示されます。Start関数はコルーチンを開始した直後に終了しますが、コルーチンはバックグラウンドで待機を続け、指定した時間が経過すると処理を再開するのです。

コルーチンの実行タイムライン。StartCoroutineで開始し、yield return new WaitForSeconds(3)で3秒間中断(その間Unityは他の仕事を続ける)、3秒後に続きから再開する

補足: StartCoroutine("WaitAndPrint")のように 文字列 でメソッド名を指定する書き方もありますが、タイプミスに気づけずリネームにも追従しないため、上のコード例のような メソッド呼び出しの形(StartCoroutine(WaitAndPrint()) をおすすめします。文字列指定が必要になるのは、文字列でStopCoroutineしたい場合などの限られた場面だけです。

Sponsored

yield returnで使える様々な中断条件

yield returnの後には、WaitForSeconds以外にも様々な中断・再開条件を指定できます。

yield return の後に指定するもの再開タイミング
null次のフレームのUpdateが呼ばれた後に再開。
new WaitForSeconds(t)指定した秒数が経過した後に再開。
new WaitForSecondsRealtime(t)Time.timeScaleの影響を受けない実時間でt秒後に再開(ポーズ中でも進む)。
new WaitForEndOfFrame()フレームの描画が完了した直後(そのフレームの終わり)に再開。
new WaitForFixedUpdate()次のFixedUpdateの後に再開。
new WaitUntil(() => 条件)指定した条件がtrueになったら再開。
StartCoroutine(AnotherCoroutine())AnotherCoroutineが完全に終了した後に再開。コルーチンを入れ子にできる。

重い処理を分割する際にはyield return null;がよく使われます。一度に全部実行するとその間ゲームが止まって(フリーズして)しまいますが、フレームごとに少しずつ進めれば、ゲームを動かしたまま処理を完了できます。

重い処理の分割の比較図。一度に全部実行するとゲームが止まるが、コルーチンでフレームごとに分割すればゲームを動かしたまま�処理を進められる
IEnumerator HeavyProcess()
{
    for (int i = 0; i < 10000; i++)
    {
        // 重い計算処理
        DoSomethingHeavy(i);

        // 100回ごとに1フレーム待機する
        if (i % 100 == 0)
        {
            Debug.Log(i + "回処理しました。1フレーム休みます。");
            yield return null; // ここで処理を中断し、次のフレームで再開
        }
    }
}

コルーチンの停止

一度開始したコルーチンは、途中で停止することもできます。コルーチンを停止するには、StartCoroutineの戻り値をCoroutine型の変数に保存しておき、StopCoroutineメソッドに渡します。「開始時に受け取った半券を持っておいて、キャンセルしたくなったらその半券で止める」イメージです。

StopCoroutineのイメージ。StartCoroutineで受け取ったCoroutine型の参照(半券)を使って、実行中のコルーチンを途中で停止できる
using System.Collections;
using UnityEngine;

public class StoppableCoroutine : MonoBehaviour
{
    private Coroutine myCoroutine;

    void Start()
    {
        Debug.Log("10秒後に何かを実行するコルーチンを開始します。");
        myCoroutine = StartCoroutine(LongProcess());
    }

    void Update()
    {
        // Cキーが押されたらコルーチンを停止する
        if (Input.GetKeyDown(KeyCode.C))
        {
            if (myCoroutine != null)
            {
                Debug.Log("コルーチンをキャンセルしました。");
                StopCoroutine(myCoroutine);
                myCoroutine = null; // 停止後は参照をクリアしておくのが安全
            }
        }
    }

    IEnumerator LongProcess()
    {
        yield return new WaitForSeconds(10f);
        Debug.Log("10秒経過!処理を実行しました。");
    }
}

StopAllCoroutines()を使えば、そのスクリプトで実行中のすべてのコルーチンを一度に停止することもできます。

実践:警告灯つき自動ドアを順番に動かす

部品がそろったので、1本の演出を最後まで組んでみましょう。SF基地の自動ドア、ダンジョンの仕掛け扉、工場ステージのゲート——「近づくと、警告灯が点滅して、扉がゆっくり開き、少し待って閉じる」という定番のシーケンスです。Updateとタイマー変数で書くと地獄ですが、コルーチンなら 起きる順番のまま上から書くだけ です。

警告灯つき自動ドアのシーケンス図。プレイヤーが接近すると警告灯が3回点滅し、扉が徐々に開き、2秒待って、閉じる——という流れが1本のコルーチンとして順番に進む
using System.Collections;
using UnityEngine;

public class AutoDoor : MonoBehaviour
{
    [SerializeField] private Light warningLight;  // 警告灯
    [SerializeField] private Transform doorPanel; // 上下に動かす扉本体
    [SerializeField] private float openHeight = 3f;
    [SerializeField] private float openSpeed = 2f;

    private Coroutine doorRoutine; // 実行中の演出の「半券」

    private void OnTriggerEnter(Collider other)
    {
        if (!other.CompareTag("Player")) return;
        if (doorRoutine != null) return; // 演出中はもう一度始めない(後述の失敗①対策)

        doorRoutine = StartCoroutine(DoorSequence());
    }

    private IEnumerator DoorSequence()
    {
        // 1. 警告灯を3回点滅させる(WaitForSecondsで0.2秒刻み)
        for (int i = 0; i < 3; i++)
        {
            warningLight.enabled = true;
            yield return new WaitForSeconds(0.2f);
            warningLight.enabled = false;
            yield return new WaitForSeconds(0.2f);
        }

        // 2. 扉を開ける——別コルーチンの「完了」を待つ書き方
        yield return StartCoroutine(MoveDoor(openHeight));

        // 3. 開けたまま2秒待つ
        yield return new WaitForSeconds(2f);

        // 4. 閉じる
        yield return StartCoroutine(MoveDoor(0f));

        doorRoutine = null; // 演出終了。次の接近を受け付ける
    }

    // 目標の高さまで、毎フレーム少しずつ動かす(yield return nullの出番)
    private IEnumerator MoveDoor(float targetY)
    {
        Vector3 target = new Vector3(0f, targetY, 0f);
        while (Vector3.Distance(doorPanel.localPosition, target) > 0.01f)
        {
            doorPanel.localPosition = Vector3.MoveTowards(
                doorPanel.localPosition, target, openSpeed * Time.deltaTime);
            yield return null; // 1フレームに1歩ずつ
        }
        doorPanel.localPosition = target;
    }

    private void OnDisable()
    {
        // 無効化されるとコルーチンは勝手に止まる。半券と見た目をリセットして
        // 「半開きのまま復帰して暴れる」事故を防ぐ
        doorRoutine = null;
        doorPanel.localPosition = Vector3.zero;
        warningLight.enabled = false;
    }
}

注目してほしいのは、この記事で学んだ待機が 演出に必要な順で全部登場している ことです。点滅の間隔はWaitForSeconds、扉のなめらかな移動はyield return nullのループ、開閉の順序制御はyield return StartCoroutine(...)(別コルーチンの完了待ち)。「機能の一覧」ではなく「演出の台本」としてコルーチンを読めるようになれば合格です。

わざと壊して、直す

動いたら、次の3つを わざと壊して みてください。コルーチンの定番バグは、この3つでほぼ説明できます。

  1. 二重起動で扉が暴れる: if (doorRoutine != null) return;の行を消して、トリガーを素早く出入りしてみてください。演出が2本同時に走り、点滅と開閉が重なって扉がガクガクします。 演出系のコルーチンは「実行中なら始めない」か「前を止めてから始める」 のどちらかを必ず入れます。
  2. StopCoroutine(DoorSequence())では止まらない: 止めたいからといってStopCoroutine(DoorSequence())と書いても止まりません。DoorSequence()と書いた時点で 新しい別のIEnumeratorが作られる だけで、走っている本体とは無関係だからです。止めるときは必ず、開始時に受け取った半券(doorRoutine)を渡します。
  3. ポーズすると扉も止まる: Time.timeScale = 0のポーズ中は、WaitForSecondsTime.deltaTimeも止まります。扉は「ゲーム内の存在」なので止まるのが正解ですが、 ポーズメニュー自体のアニメーション のようにポーズ中も動かしたい演出はWaitForSecondsRealtime(とTime.unscaledDeltaTime)を使います。「ゲーム時間か、実時間か」で待機を選び分けるのがコツです。

最後はPlayで答え合わせです。扉の前で出入りを連打しても演出は1本のまま、動いている最中に無効化→再有効化しても半開きで暴れない——そうなっていれば、半券(Coroutine参照)の管理はきちんと効いていて、コルーチンはもう「演出の台本を書く道具」になっています。

おまけ:先に知っておくと良いこと

コルーチンを使い始めると、いずれ次のような話に出会います。

  • コルーチンはオブジェクトと運命を共にする: コルーチンはStartCoroutineしたMonoBehaviourに紐づいて動くため、 そのオブジェクトが非アクティブ化・破棄されると止まります 。「シーンをまたいで動き続けてほしい処理」には向きません。この性質は ライフサイクルの記事 の知識とセットで理解すると腑に落ちます。
  • async/awaitというもうひとつの非同期: C#標準のasync/awaitでも似たことができ、ファイル読み込みや通信ではそちらが適しています。「演出やフレーム同期はコルーチン、IO待ちはasync」が大まかな使い分けです。
  • ポーズ画面とWaitForSeconds: Time.timeScale = 0でゲームを一時停止すると、WaitForSecondsも止まります。ポーズ中でも動かしたい演出(メニューのアニメーションなど)にはWaitForSecondsRealtimeを使いましょう。

まとめ

コルーチンは、Unityにおける非同期処理の基本であり、使いこなせば非常に表現力豊かなロジックをシンプルに記述できます。

  • コルーチンは、処理を一時中断・再開できる特殊な関数。
  • IEnumeratorを戻り値とし、yield returnで処理を中断する。
  • StartCoroutine()で開始し、StopCoroutine()で停止できる。
  • new WaitForSeconds(t)で時間待ち、nullで1フレーム待ちが基本。

時間のかかる演出、AIの思考ルーチン、重い処理の分割など、コルーチンの活躍の場は無限にあります。まずは簡単な時間待ち処理から試してみて、その便利さを体感してみてください。