Minssuy

非同期 - コルーチン (Coroutine)

Unity for Game Development
By Minssuy
Posted 2026/05/242026년 5월 24일 일요일 AM 12:00
7 min read217 words
非同期 - コルーチン (Coroutine)

銀行


皆さんは今日、新しい通帳を作るために車で銀行を訪れました。
銀行に着いてみると、あら、この銀行にはお客様を対応する窓口が一つしかありませんね。
仕方ないので、少し待つことにしましょう。


約1時間ほど待った後、ようやく皆さんの順番が来ました。
銀行員の前に座り、案内に従って通帳を作る手続きを進めます。


銀行員が印鑑が必要だと言うのですが、あいにく皆さんはうっかり車に印鑑を置いて銀行に来てしまいました。
「車に印鑑を置いてきてしまったので、5分ほどお待ちください!」 と伝え、銀行の外に出ました。
皆さんが印鑑を取りに車へ行っている間、銀行員はその間に他の業務を進めます。


同期と非同期


お客様は順番に一人ずつ処理されていたため、皆さんは約1時間ほど何もできずに待つだけでした。
これを 同期 と呼びます。前の処理が完了するまで、ひたすら待ち続けなければならないということですね。


ようやく皆さんの順番が来た後、印鑑を取りに車に行っている間、銀行員は溜まっていた他の業務を進めました。
これを 非同期 と呼びます。通帳を作る業務は一旦待機させ、他の業務を進めるということですね。


ここで重要なのは、窓口が一つだったという点です。これを シングルスレッド (Single Thread) と呼びます。
スレッド という概念は今は理解する必要はなく、忘れてしまっても構いません。

ただし、窓口が一つだったという事実は必ず覚えておいてください。後でより深く理解する際に役立ちます。


同期プログラミング


同期プログラミング、というタイトルをつけると難しそうに聞こえますが、恐れる必要はありません。
皆さんが普段書いているコードが、まさに同期です。

サンプルコードを見てみましょう。

void Start()
{
    WaitInChair(); // 椅子で1時間待機
    MakeAccount(); // 銀行員に通帳作成を依頼
}

void WaitInChair()
{
    int one_hour = 3600;

    for(int i = 0; i < one_hour; i++)
    {
        Debug.Log("1時間待っています...");
    }
}

void MakeAccount()
{
    Debug.Log("通帳作成開始!");
}

上記のコードは実際に1時間待つわけではありませんが、より実感を持っていただくためにこのように書いてみました。
Start() 内に書かれた関数を見ると、WaitInChair が完了した後に MakeAccount が呼び出されます。


これを図で表してみましょう。

Sync 同期プログラミング (Synchronous Programming)

このように順次実行されることを 同期プログラミング と呼びます。皆さんが普段書いているものですね。
ただし、上のコードには皆さんが 「5分待ってください!」 と言ったことや、銀行員が他の業務をする内容は含まれていませんね。


非同期プログラミング


今度は銀行員のサンプルコードを見てみましょう。

void Start()
{
    StartCoroutine(MakeAccount()); // 通帳作成開始
    DoOtherWork(); // 他の業務
}

IEnumerator MakeAccount()
{
    Debug.Log("銀行員 : 通帳作成開始!");
    Debug.Log("銀行員 : 印鑑が必要です。");
    Debug.Log("お客様 : 印鑑を取りに5分だけ行ってきます!");

    yield return new WaitForSeconds(5f);

    Debug.Log("銀行員 : 通帳作成完了!");
}

void DoOtherWork()
{
    Debug.Log("銀行員 : 溜まった書類を整理中...");
}

文法を理解する前に、サンプルコードの結果から確認してみましょう。

銀行員 : 通帳作成開始!
銀行員 : 印鑑が必要です。
お客様 : 印鑑を取りに5分だけ行ってきます!
銀行員 : 溜まった書類を整理中...

(5秒後)
銀行員 : 通帳作成完了!

上のコードで重要なのは、お客様が 「5分待ってください!」 と言ったその瞬間です。

yield return new WaitForSeconds(5f);

これも図で流れを確認してみましょう。

Async 非同期プログラミング (Asynchronous Programming)

銀行員は通帳作成の作業を一旦止めて、次の業務 DoOtherWork に移ります。
5秒経った後、止めた場所に戻って通帳作成を続けて進めます。


一つ補足しておきたい点があります。Start 関数自体は、実は DoOtherWork が終わった瞬間に終了します。
ただし、コルーチンは別途 Unity エンジンが管理しているため、5秒後に止めた場所から再開します。 Unity エンジンが内部でどのように管理しているかは、この記事では扱いません。

それでは、文法を見ていきましょう。


コルーチン (Coroutine)


コンピュータ用語は抽象的な表現が多く、理解するのが難しいことがあります。
そういうときは、単語の意味から調べることが概念の理解に大きく役立ちます。

コルーチンとは、共に という意味の co- という接頭辞と、規則的に行う仕事 という意味の routine が組み合わさった単語です。
直訳すると 「共にする規則的な仕事」 — つまり、他の処理と協調しながら進む処理ですね。どのように共にするのか確認してみましょう。


Unity でコルーチンを使うには、3つだけ覚えておけば大丈夫です。

  • 戻り値の型 : IEnumerator
  • 一時停止のポイント : yield return
  • 関数の呼び出し : StartCoroutine()

一つずつ見ていきましょう。


IEnumerator


一般的な関数は、以下のように書くことができます。

void MakeAccount()
{
    Debug.Log("通帳作成開始!");
}

この関数は戻り値の型が void の形式です。そのため、return の戻り値がない形になっています。


次にコルーチン関数を見てみましょう。

IEnumerator MakeAccount()
{
    Debug.Log("通帳作成開始!");
    yield return new WaitForSeconds(5f);
    Debug.Log("通帳作成完了!");
}

コルーチン関数の戻り値の型は IEnumerator です。
また、関数の中では一般的な return ではなく、yield return で値を返さなければなりません。


yield return


yield の意味は 譲る ということです。単語の意味が少しは伝わってきたでしょうか?
本来、関数は終わるまで呼び出し元(caller)に制御を返しませんが、一時的に処理を譲る(yield)ことで co-routine になるわけです。


戻り値である yield return の後ろに何を書くかによって、譲る時間の長さが変わります。
代表的によく使われる例は以下の通りです。

yield return null; // 次のフレームで再開
yield return new WaitForSeconds(5f); // 5秒待機 (timescale の影響あり)
yield return new WaitForSecondsRealtime(5f); // 5秒待機 (timescale の影響なし)
yield return new WaitUntil(() => isTrue); // 条件が true になるまで待機

この他にも種類はありますが、最初は上の4つだけ知っておけば十分です。残りは必要なときに調べてみてください。


StartCoroutine


一般的な関数の呼び出しは、以下のように行います。

MakeAccount();

次に、コルーチン関数を呼び出す様子を見てみましょう。

StartCoroutine(MakeAccount());

では、もしコルーチン関数を一般的な関数のように呼び出すとどうなるでしょうか?

void Start()
{
    MakeAccount(); // not StartCoroutine
}

IEnumerator MakeAccount()
{
    Debug.Log("通帳作成開始!");
    yield return new WaitForSeconds(5f); 
    Debug.Log("通帳作成完了!");
}

答えは、エラーは発生せず、関数の中のコードが一行も実行されません。
コンソールにも何のログも出力されないため、コルーチン関数は必ず StartCoroutine で実行する必要があります。


コルーチン関数も関数なので、パラメータを渡すことができます。

void Start()
{
    StartCoroutine(MakeAccount("Minssuy", 100));
}

IEnumerator MakeAccount(string name, int money)
{
    Debug.Log($"{name} 様 : {money}万円の通帳作成開始!");
    yield return new WaitForSeconds(5f); 
    Debug.Log($"{name} 様 : 通帳作成完了!");
}
Minssuy 様 : 100万円の通帳作成開始!
(5秒後)

Minssuy 様 : 通帳作成完了!

StopCoroutine


通帳を作っている途中で、お客様が突然 「やっぱり作りたくないです!」 と言うかもしれません。
銀行員は進めていた業務を止めなければなりません。コルーチンも同じく、途中で止めることができます。


コルーチンを止めるには、事前準備が必要です。

Coroutine accountCoroutine;

void Start()
{
    accountCoroutine = StartCoroutine(MakeAccount());
}

StartCoroutine には実は戻り値があり、Coroutine 型のオブジェクトです。
このオブジェクトを保持しておくと、後でそのコルーチンを再度参照できるようになります。これを使って止めてみましょう。

Coroutine accountCoroutine;
bool feelings = true;

void Start()
{
    accountCoroutine = StartCoroutine(MakeAccount());
}

void Update()
{
    if(!feelings)
    {
        CancelAccount();
    }
}

void CancelAccount()
{
    StopCoroutine(accountCoroutine);
    Debug.Log("お客様 : 作りたくないです!");
}

この他にも StopAllCoroutines を使うことで、現在のオブジェクトのすべてのコルーチンを一度に止める方法もあります。
これも必要なときに使い方を調べてみてください。


おわりに


この記事では、非同期の概念とコルーチンの使い方を簡単に見てきました。
ですが、この記事ではまだ気になる部分がいくつか残っています。私が思うに、こんな疑問が浮かんでくるはずです。

  • IEnumerator の正体は何者で、なぜ関数を一時的に止めておけるのか?
  • yield return の次の行に戻ってきたとき、関数はどうやって進行状態を覚えているのか?
  • Unity エンジンはコルーチンをどのように管理しているのか?

さらに、この記事はコルーチンの理解に重点を置いたものなので、概念の説明には少し正確でない部分も含まれています。
次回は、この魔法のような出来事がどうやって起きているのか、そしてコルーチンの正体について深く掘り下げていきます。


Reference


© Powered by Minssuy