スポンサーリンク
エラー・トラブルシューティング

UnityのCoroutineが動かない原因まとめ|StartCoroutineが実行されない時の完全対処ガイド

エラー・トラブルシューティング

はじめに

「StartCoroutineを書いているのに動かない…」
「エラーも出てないのに処理されない…」
「気づいたらUnityがフリーズしている…」

こういう状況、かなり多いです。私も最初は同じところで何度もハマりました🙂

Coroutineは便利な仕組みなんですが、
「動かない原因がコード上で見えにくい」のが厄介なんですよね。

例えばこんなケースがあります。

  • GameObjectが非アクティブになっていて止まっている
  • そもそもStartCoroutineが呼ばれていない
  • yieldを書き忘れて実行されていない
  • 無限ループでエディタが固まっている

どれも一見すると「ちゃんと書いているように見える」のがポイントです。

だからこそ大事なのは、
「どこを見れば原因が分かるのか」を知っておくことです。

Coroutineの不具合は、次の3つに分けると整理しやすくなります。

  • 状態の問題(GameObjectや有効/無効)
  • 書き方のミス(yield・StartCoroutineの呼び方)
  • 設計の問題(多重起動・ループ構造)

この3つのどこかに必ず原因があります。

ここからは、実際によくある原因を「症状ベース」で分解していきます。
自分の状況と照らし合わせながら読み進めてみてください✨


結論:まず最初に確認すべき5つ

原因を細かく探す前に、まずはここをチェックしてみてください。
実際の開発でも、この段階で解決することがかなり多いです。

  • GameObjectが非アクティブになっていないか
  • StartCoroutineが本当に呼ばれているか
  • コルーチン内にyield returnがあるか
  • Updateで毎フレームStartCoroutineしていないか
  • ループ内にyieldがなくフリーズしていないか

この5つは「発生頻度が高い順」に並んでいます。

特に多いのが次の2つです。

  • GameObjectが非アクティブ → コルーチン自体が止まる
  • Updateで多重起動 → 挙動が壊れる

ここを見落とすと、「コードは正しいのに動かない」という状態になります。

判断のコツとしては、こんな感じです。

  • そもそも動かない → 呼び出し or 状態を疑う
  • 途中で止まる → GameObjectやStop処理を疑う
  • 挙動がおかしい → 多重起動を疑う
  • 固まる → ループ構造を疑う

この時点で「あ、これかも」と思ったものがあれば、かなり当たりです🙂

次からは、それぞれの原因を1つずつ深掘りしていきます。




Coroutineが動かない原因と対処法

ここからは、実際によくある原因を「症状ごと」に分けて見ていきます。
自分の状況に近いものから確認していくと、かなり早く原因にたどり着けます。

GameObjectが非アクティブで動いていない

途中で処理が止まったり、そもそも動かなかったりする場合はまずここを疑います。

  • 症状:途中で止まる / StartCoroutineしても動かない
  • 原因:GameObjectがSetActive(false)になっている
  • 解決方法:Hierarchyでアクティブ状態を確認する

UnityのCoroutineは、アタッチされているGameObjectに依存しています。
つまり、GameObjectが無効になるとコルーチンも止まります。

さらにややこしいのが、
再びアクティブに戻しても自動では再開されないという点です。

再発防止としては、こんな形にすると安定します。

void OnEnable()
{
    StartCoroutine(MyCoroutine());
}

「気づいたら止まってる」系のトラブルは、ほぼここです。


StartCoroutineの呼び出し元が間違っている

コードは書いているのに、まったく動かない場合に多いパターンです。

  • 症状:一切実行されない
  • 原因:MonoBehaviourをnewで生成している
  • 解決方法:AddComponentで生成する

例えばこれはNGです。

var obj = new MyScript();
obj.StartCoroutine(MyCoroutine());

Unityのスクリプトは、シーン上のGameObjectにアタッチされて初めて動きます
newで作っただけでは、Unityの管理外になってしまいます。

正しくはこうです。

gameObject.AddComponent<MyScript>();

「C#としては正しいのに動かない」というときは、このパターンが多いです。


yieldがなくてコルーチンになっていない

IEnumeratorで書いているのに動かない場合、ここを確認します。

  • 症状:すぐ終了する / エラーになる
  • 原因:yieldが1つもない
  • 解決方法:最低1つyieldを書く

Coroutineは「途中で止める」ことで成立しています。
yieldがないと、ただの関数と同じ扱いになります。

IEnumerator MyCoroutine()
{
    Debug.Log("一瞬で終わる");
}

これだとコルーチンとして機能しません。


無限ループでフリーズしている

Unityが固まる場合は、ほぼこれです。

  • 症状:エディタがフリーズする
  • 原因:ループ内にyieldがない
  • 解決方法:yield return nullを入れる

NG例はこちらです。

while (true)
{
    // 何も待たない
}

これを実行すると、1フレーム内で無限ループが走り続けます。
その結果、Unityが応答しなくなります。

正しくはこうです。

while (true)
{
    yield return null;
}

ループを書くときは、「毎回yieldしているか?」を必ず確認します。


Updateで毎フレームStartCoroutineしている

挙動がおかしくなる原因としてかなり多いです。

  • 症状:動きが変になる / 重くなる
  • 原因:毎フレーム新しいCoroutineが起動
  • 解決方法:フラグ管理または1回だけ実行

よくある失敗例です。

void Update()
{
    StartCoroutine(MyCoroutine());
}

これだと、毎フレーム新しいコルーチンが増え続けます。

対策はシンプルです。

bool isRunning = false;

void Update()
{
    if (!isRunning)
    {
        StartCoroutine(MyCoroutine());
    }
}

「とりあえずUpdateで呼ぶ」は、かなり危険なクセです。


StopCoroutineが効かない・エラーになる

止めたつもりなのに止まらない場合はここを見ます。

  • 症状:止まらない / エラーになる
  • 原因:対象のCoroutineが一致していない
  • 解決方法:Coroutineを変数で保持する
Coroutine co;

co = StartCoroutine(MyCoroutine());
StopCoroutine(co);

文字列指定や別インスタンスを渡すと、うまく止まりません。


yield returnの使い方が間違っている

待機処理がうまく動かない場合は、ここが原因です。

  • 症状:待機されない / タイミングがズレる
  • 原因:用途に合っていないyieldを使用
  • 解決方法:正しく使い分ける

例えば、毎フレーム処理したいのにWaitForSecondsを使うと、意図しない遅延が発生します。

「何フレーム待つのか」「何秒待つのか」を意識すると、ミスが減ります。




yield returnの正しい意味と使い分け

「yield return nullって結局なにしてるの?」という疑問、ここでスッキリさせておきましょう。
Coroutineの挙動は、この一行の理解でかなり変わります。

yield return nullの意味

結論から言うと、「次のフレームまで待つ」という意味です。

「何もしない」ではなく、処理を一旦止めて次のフレームに回す動きになります。

IEnumerator MyCoroutine()
{
    Debug.Log("1フレーム目");
    yield return null;
    Debug.Log("2フレーム目");
}

この場合、ログは同時には出ません。
1フレームずれて表示されます。

この「フレームをまたぐ」性質が、Coroutineの本質です。


よく使うyieldの種類

実務でよく使うものを、用途とセットで整理しておきます。

書き方意味使う場面
yield return null1フレーム待機毎フレーム更新したい処理
yield return new WaitForSeconds(1f)1秒待機時間制御
yield break即終了条件で終了したい時
yield return StartCoroutine(…)別Coroutineの完了待ち処理の順番制御

迷ったら、まずはこの2つを基準にすると分かりやすいです。

  • 毎フレーム → yield return null
  • 時間待ち → WaitForSeconds

よくあるミスと注意点

ここは実務でよく引っかかるポイントです。

  • yield return 0
    → 一応動きますが、内部で変換が走るため基本は使いません
  • WaitForSecondsを毎回newしている
    → ループ内で使うとGC(メモリ確保)が増えます

例えばこういうコードは、長時間動かすと少しずつ負荷が増えます。

while (true)
{
    yield return new WaitForSeconds(1f);
}

改善するなら、キャッシュして使います。

WaitForSeconds wait = new WaitForSeconds(1f);

while (true)
{
    yield return wait;
}

体感レベルでは分かりにくいですが、モバイルや長時間プレイでは差が出てきます。


判断に迷ったときの考え方

迷ったらこの視点で考えるとスムーズです。

  • フレーム単位で制御したい → null
  • 時間で制御したい → WaitForSeconds
  • 処理を分割したい → Coroutine

「何を待ちたいのか」をはっきりさせると、選び方で迷わなくなります。




Coroutineと他処理の違い

「Coroutineを使うべきか、それとも別の方法がいいのか」
ここがあいまいだと、バグの原因になりやすいです。

特に混同しやすいのがこの2つです。

  • Updateとの違い
  • async/await(Task)との違い

それぞれ整理していきます。

CoroutineとUpdateの違い

ざっくり言うと、こうなります。

項目CoroutineUpdate
実行タイミング必要なときだけ毎フレーム
待機処理できるできない
用途時間制御・段階処理常時監視・入力処理

判断基準としては、これがシンプルです。

  • ずっとチェックし続けたい → Update
  • 一定時間待ちたい → Coroutine

例えばこんなイメージです。

  • プレイヤーの入力監視 → Update
  • 3秒後に爆発 → Coroutine

Updateで時間管理をしようとすると、こんなコードになります。

float timer = 0f;

void Update()
{
    timer += Time.deltaTime;

    if (timer > 3f)
    {
        // 実行
    }
}

これでも動きますが、処理が増えるほど複雑になります。

Coroutineならシンプルです。

IEnumerator Explosion()
{
    yield return new WaitForSeconds(3f);
    // 実行
}

処理の流れがそのまま書けるのがメリットです。

より詳しく知りたい場合はこちらも参考になります。


Coroutineとasync/awaitの違い

中級者以上で気になってくるのがここです。

項目Coroutineasync/await
仕組みフレーム分割非同期処理(スレッド)
Unity依存ありなし
主な用途ゲーム内の時間制御通信・ファイル処理

大事なのはここです。

  • Coroutine → ゲーム内の「時間の流れ」を制御
  • async/await → 外部処理(通信・I/O)を待つ

例えば、

  • 敵の行動を1秒ごとに変える → Coroutine
  • サーバーからデータ取得 → async/await

この使い分けができると、設計がかなり安定します。

「なんとなくCoroutineで全部やる」は、後から崩れやすいポイントです。




すぐ確認できるチェックリスト

「原因を一つずつ読むのは大変…」というときは、ここから順番に確認すると早いです。
実際のデバッグでも、この順番で見るとほぼ迷いません。

3分以内で確認できる内容に絞っています👇

  • □ GameObjectがアクティブになっているか
  • □ StartCoroutineが実際に呼ばれているか(Debug.Logで確認)
  • □ コルーチン内にyield returnがあるか
  • □ whileやforの中にyieldが入っているか
  • □ Updateで毎フレームStartCoroutineしていないか
  • □ StopCoroutineの対象が正しいか

ポイントは、上から順に確認することです。

いきなり複雑な原因を疑うよりも、
「よくあるミス → 次に可能性の高いもの」と順番に潰す方が早く解決できます。

例えばこんな流れです。

  • 動かない → StartCoroutineが呼ばれているか確認
  • 途中で止まる → GameObjectの状態を確認
  • おかしい → 多重起動を疑う
  • 固まる → ループ構造を確認

このチェックを習慣にすると、
「なんとなくバグってる状態」から抜け出しやすくなります🙂




よくある誤解・注意点

Coroutineは「なんとなく動いている」状態でも成立してしまうので、
理解があいまいなまま使い続けてしまいがちです。

ここでは、実際によくある勘違いを整理しておきます。

Coroutine=非同期処理ではない

名前の印象で「非同期処理」と思われがちですが、少し違います。

Coroutineはスレッドを分けているわけではなく、1フレームずつ処理を分割しているだけです。

そのため、

  • 重い処理を入れると普通にフレーム落ちする
  • 並列で処理しているわけではない

という点は注意が必要です。


yield return null=何もしないではない

これもかなり多い誤解です。

実際には「次のフレームまで待つ」という意味があります。

この1行があるかどうかで、挙動が大きく変わります。

  • ある → フレームごとに分割される
  • ない → 一気に実行される

「何もしていないように見えるけど、実は重要」な行です。


一度止まったCoroutineは自動で再開しない

GameObjectが非アクティブになるとCoroutineは停止しますが、
元に戻しても勝手には再開されません。

この仕様を知らないと、「なんで動かないの?」と悩みやすいです。

必要なら、OnEnableなどで明示的に再スタートします。


一度動いたから正しいとは限らない

これが一番やっかいなポイントです。

例えば、

  • たまたま1回だけ動いた
  • 条件が偶然そろっていた

こういう状態でも「成功した」と思ってしまいがちです。

でも実際には、内部で問題を抱えていることが多いです。

判断の基準としてはこう考えると安全です。

  • 毎回同じタイミングで動くか?
  • 長時間動かしても崩れないか?

この2つを満たしていれば、かなり安定しています。




実務でのベストプラクティス

ここからは、実際に開発しているときに意識しておくと安定するポイントです。
「動く」だけでなく「壊れにくい」コードにするための考え方ですね。

多重起動を防ぐ

Coroutineのトラブルで一番多いのがこれです。

気づかないうちに複数起動してしまうと、

  • 挙動が不安定になる
  • 処理が重くなる
  • StopCoroutineが効かなくなる

といった問題が出てきます。

対策はシンプルです。

bool isRunning = false;

IEnumerator MyCoroutine()
{
    isRunning = true;

    yield return new WaitForSeconds(1f);

    isRunning = false;
}

そして呼び出し側で制御します。

if (!isRunning)
{
    StartCoroutine(MyCoroutine());
}

「今動いているか?」を自分で管理するクセをつけると、かなり安定します。


Coroutineを変数で管理する

停止処理を確実にしたい場合は、Coroutineを保持します。

Coroutine co;

co = StartCoroutine(MyCoroutine());
StopCoroutine(co);

これをやらないと、

  • 別のCoroutineを止めてしまう
  • そもそも止まらない

といった問題が出やすくなります。


WaitForSecondsは使い回す

ループ内で毎回newするのは避けたいところです。

短時間では分かりにくいですが、長時間プレイやモバイル環境では影響が出てきます。

WaitForSeconds wait = new WaitForSeconds(1f);

while (true)
{
    yield return wait;
}

「同じ時間で待つなら再利用する」と覚えておくとOKです。


状態管理とセットで使う

Coroutine単体で制御しようとすると、あとから破綻しやすいです。

例えば、

  • 攻撃中フラグ
  • 移動中フラグ
  • クールタイム状態

こういった状態と組み合わせて使うと、挙動が安定します。

実務では「Coroutine+状態管理」が基本の形です。


経験からの判断基準

実際に開発していると、こういう場面に出会います。

  • 動いているけど、たまにおかしくなる
  • 再現性が低いバグが出る

このときに疑うべきは、

  • 多重起動していないか
  • 状態管理が抜けていないか
  • Coroutineの開始・停止が曖昧になっていないか

Coroutineは「動いているように見えるバグ」が多いです。

だからこそ、
明示的に制御すること(状態・開始・停止)が大事になってきます。




おすすめ学習リソース

Coroutineでつまずく原因の多くは、実は「Unityの仕組み」と「C#の基礎」がつながっていないことにあります。

例えば、

  • IEnumeratorの意味があいまい
  • MonoBehaviourのライフサイクルが理解できていない
  • Updateやフレームの概念がぼんやりしている

このあたりが整理されると、Coroutineの挙動が一気にクリアになります。

体系的に理解を深めたい場合は、こういった基礎をまとめて学べる書籍が役立ちます。

Unityの教科書 Unity 6完全対応版
✅ Amazonでチェックする✅ 楽天でチェックする

断片的に調べるよりも、「全体像」を一度つかんでおくと、
エラーの原因を自分で判断できるようになります。

実際の開発では、Coroutine単体というよりも
「Unity全体の流れの中でどう動いているか」を理解しているかどうかで、安定度がかなり変わります。




まとめ

Coroutineが動かないときは、「なんとなくコードを見る」よりも、
原因のパターンに当てはめて確認する方が圧倒的に早いです。

まず最初に見るべきポイントはこの5つです。

  • GameObjectがアクティブか
  • StartCoroutineが呼ばれているか
  • yield returnがあるか
  • 多重起動していないか
  • 無限ループになっていないか

そして、原因は大きくこの3つに分けられます。

  • 状態の問題(アクティブ/非アクティブ)
  • 書き方の問題(yield・呼び出し方法)
  • 設計の問題(多重起動・管理不足)

この視点を持っておくと、「どこを見ればいいか」で迷わなくなります。

特に意識しておきたいのがこちらです。

  • Coroutineはフレーム分割処理である
  • 一度止まると自動では再開しない
  • 動いていても正しいとは限らない

実務では「たまに動かない」「再現しにくいバグ」が一番厄介です。

そういうときは、

  • 開始タイミングは明確か?
  • 停止条件は明確か?
  • 状態管理できているか?

この3つをチェックしてみてください。

Coroutineは便利ですが、仕様を理解していないと不安定になりやすい仕組みでもあります。
逆に言えば、仕組みを理解してしまえばかなり扱いやすくなります。

「なんで動かないんだろう…」と悩んだときは、今回のチェック項目に戻ってくると解決しやすくなります🙂


よくある質問(FAQ)

Q
Coroutineはどんなときに使うべき?
A

時間の経過や段階的な処理をしたいときに使います。

  • 数秒後に処理を実行したい
  • アニメーションのように順番に処理したい
  • 一定間隔で処理を繰り返したい

逆に「毎フレーム常に監視する処理」はUpdateの方が向いています。

Q
Coroutineが途中で止まるのはなぜ?
A

最も多い原因は、GameObjectが非アクティブになっているケースです。

Unityでは、コルーチンはGameObjectに紐づいているため、

  • SetActive(false)になる
  • オブジェクトが破棄される

と、その時点で停止します。

再開したい場合は、OnEnableなどで再度StartCoroutineする必要があります。

Q
CoroutineとUpdateはどちらを使うべき?
A

目的によって使い分けます。

  • 常に監視・入力処理 → Update
  • 時間待ち・順番処理 → Coroutine

迷ったときは、「待機が必要か?」で判断すると分かりやすいです。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク