こんにちは、Unityエンジニアのオオバです。
Unityを始めると 「コルーチン」 というキーワードが出てきませんか?
結論から話すと Unityでゲーム開発する上で知っておいた方が良い知識 です。
例えば 「アニメーションした後になにかする」 「オブジェクトをロードした後になにかする」 ことありますよね。
このような「〜〜をした後」とは、 「〜〜」を待機しているということ を指します。
「処理の待機」はゲーム開発では必須の知識 です。待機することを「非同期処理」とも呼ばれます。
この非同期処理しっかり理解しておかないとゲーム開発はできません。
コルーチンはUnityが適用する非同期処理の1つです。
この記事では初心者向けに、 コルーチンの基本的な使い方から実例、注意点まで詳しく解説 しています。
最初は読んでも分かりづらいかもしれませんが何度も繰り返し読み直して少しずつ理解を深めてみてください。
2023年現在非同期処理の実装はコルーチンより「UniTask」が主流 です。
UniTaskについて分からない、使ってみたいという方はこちらの記事を参考にしてみてください。
コルーチンとは中断と再開可能な関数
コルーチンとは中断と再開を可能にした関数のことです。
はじめに結論を簡単にまとめます。
①コルーチンとは中断と再開可能な関数
②コルーチンの作り方は戻り型をIEnumeratorにする
③コルーチンにはyieldが1つ以上必要
④GameObjectがアクティブのとき動作する
⑤コルーチンの中断は再開できるが、停止すると再開不可
⑥WaitForSecondsで任意の秒数待機可能
⑦デリゲートを使うことでコルーチンの結果を受け取れる
こんな感じです。
繰り返しになりますが 「コルーチンとは中断と再開が可能な関数」 。
この言葉だけ聞いても、メリットがピンとこないかもしれません。
以降の章では具体的なコルーチンの作り方、基本的な使い方を紹介します。
後半では コルーチンを使いこなすためのテクニック 、 使用上の注意点 をまとめていますのでぜひ最後まで読んでみてください。
ぜひコルーチンをマスターして ゲーム開発に生かして いきましょう。
👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!
- コルーチンとは?
- コルーチンの作り方と基本的な使い方3手順
- コルーチンを使いこなす9つのテクニック
- MonoBehaviourのStartはコルーチン化可能
- テク①コルーチン内のコルーチンはStartCoroutineを省略可能
- テク②WaitForSecondsで指定秒数待機する方法
- テク③コルーチンの停止3つの手段
- テク④複数のコルーチンを待機するテクニック
- テク⑤ボタンをクリックするまでコルーチンを待機する方法
- テク⑥引数付きのコルーチン
- テク⑦コルーチンの結果を受け取るテクニック
- テク⑧コルーチンでカウントダウン
- テク⑨MonoBehaviourを継承せずにコルーチンを使用する方法
- Unityのコルーチン注意点10選
- 注意点①「yield」のないコルーチンはエラーになる
- 注意点③コンポーネントの非アクティブでコルーチンは止まらない
- 注意点④停止したコルーチンは再開しない
- 注意点⑤コルーチンは必ず1フレーム消費する
- 注意点⑥StopCoroutineするときはnullチェックしよう
- 注意点⑦StartCoroutineは文字列指定しない
- 注意点⑧StopAllCoroutinesは使用禁止
- 注意点⑨「yield return 0」は非推奨「yield return null」を推奨
- 注意点⑩コルーチンの引数に「ref」「in」「out」は使用不可
- Unityコルーチンのまとめ
コルーチンとは?
コルーチンとは中断と再開可能な関数の一種 です。
通常の関数は1フレーム内で処理を終了します。
コルーチンはどうでしょう。
上図の通りコルーチンは実行後、中断と再開を実現できます。
つまりコルーチンを使うことで フレームをまたいで処理を継続することができる のです。
フレームをまたぐというのはゲーム開発では非常に重要です。
わかり易い例は アニメーション です。
アニメーションしている最中は処理を中断し、アニメーションが終了したらダメージ演出を開始するといった感じです。
これから具体的なコルーチンの使い方を紹介していきます。
繰り返しになりますが、 ゲーム開発では必須 なのでぜひ使いこなせるようになりましょう。
コルーチンの作り方と基本的な使い方3手順
Unityのコルーチンの手順と基本的な使い方を紹介します。手順は3つ。
①戻り型IEnumeratorの関数を用意
②yieldを1つ以上記述
③コルーチンの実行にはMonoBehaviourとStartCoroutineが必要
シンプルなコルーチンを実装しながら学んでいきます。
手順①戻り型IEnumeratorの関数を用意
コルーチンの文法の話。
IEnumeratorを戻り型 にします。
次のコードを見てみましょう。
IEnumerator TestCoroutine()
{
}
戻り型が IEnumerator ですね。
これがコルーチンです。
手順②yieldを1つ以上記述
コルーチンのルールなのですが、
コルーチン内には1つ以上「 yield 」が必要です。
IEnumerator TestCoroutine()
{
yield return null;
}
上のソースコードのとおり、 yield return null を記述。
後述しますが、 yield return null は1フレーム待機することになります。
yield が1つもないとエラーになるため注意。
手順③コルーチンの実行にはMonoBehaviourとStartCoroutineが必要
コルーチンを実行するためには MonoBehaviour と StartCoroutine が必要です。
次のコードはコルーチン「TestCoroutine」を実行しています。
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
void Awake()
{
// コルーチンの実行
StartCoroutine(TestCoroutine());
}
IEnumerator TestCoroutine()
{
yield return null;
}
}
StartCoroutineメソッドの引数にコルーチン(TestCoroutine)を指定。
引数に渡されたコルーチンを実行するのが、
StartCoroutineの機能です。
StartCoroutine(TestCoroutine());
具体的には上記の箇所。
StartCoroutine でコルーチンを実行しているのです。
以上でコルーチンの作り方と基本的な使い方の説明は終わり。
コルーチンの使い方自体は簡単だったかと思います。
覚えておくことは以下の2点。
- ① IEnumeratorを戻り型 にしてコルーチン作成
- ② MonoBehaviourのStartCoroutineでコルーチン実行
しかし使い方がわかっても、 どう使ってよいのかわからない ですよね。
そこで以降の章では 具体的なコルーチンの活用方法 を紹介していきます。
オオバが実際のゲーム開発の現場で使ってきたテクニックです。
ぜひ読んでみてください。
ところで今回登場した 「MonoBehaviour」 。Unityを始めた方なら聞いたことはあると思います。MonoBehaviourはUnity開発において必須知識 です。
次の記事ではMonoBehaviourについて徹底的に解説しています。理解がふわふわしている方はぜひ読んでみてください。
コルーチンを使いこなす9つのテクニック
ここからは実践編。
コルーチンを使いこなすための9つのテクニック を紹介していきます。
①コルーチン内のコルーチンはStartCoroutineを省略可能
②WaitForSecondsで指定秒数待機する方法
③コルーチンの停止3つの手段
④複数のコルーチンを待機するテクニック
⑤ボタンをクリックするまでコルーチンを待機する方法
⑥引数付きのコルーチン
⑦コルーチンの結果を受け取るテクニック
⑧コルーチンでカウントダウン
⑨MonoBehaviourを継承せずにコルーチンを使用する方法
MonoBehaviourのStartはコルーチン化可能
具体的なコルーチンの使い方の前に、Startメソッドについて紹介。
実はMonoBehaviourを使うことで使用可能なイベント関数の1つ 「Start」はコルーチン化 できます。
具体的には以下のコードです。
// void Start()ではなくIEnumerator型に変えるだけ
IEnumerator Start()
{
yield return null;
}
void Start()
ではなく IEnumerator Start()
と戻り型を IEnumerator に変えるだけでStartはコルーチンになります。
今後のサンプルでは Startをコルーチンとして使う ため、先に紹介させてもらいました。
注意:AwakeやUpdateはコルーチン化不可
Startと同じイベント関数でも AwakeやUpdateはコルーチン化できません。
あくまで Startのみに提供された機能 です。
ではここから コルーチンを使いこなすテクニック をひとつずつ紹介していきます。
テク①コルーチン内のコルーチンはStartCoroutineを省略可能
コルーチンの中でコルーチンを使うことはよくあります。コルーチンの中でStartCoroutineは省略可能 です。
次のサンプルを見てみましょう。
IEnumerator Start()
{
// yield return StartCoroutine(TestCoroutine()); と同じ
yield return TestCoroutine();
}
IEnumerator TestCoroutine()
{
yield return null;
}
コルーチンStart関数内でコルーチンTestCoroutineを実行しています。
以下の部分です。
yield return TestCoroutine();
本来は yield return StartCoroutine(TestCoroutine());
と書く必要がありそうですが、コルーチンの中ではStartCoroutineは省略可能。
コード量が減らせるのでぜひ覚えておきましょう。
テク②WaitForSecondsで指定秒数待機する方法
よく使うテクニックの1つ 指定秒数待機 する方法です。WaitForSeconds を使います。
次のコードを見てください。
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
IEnumerator Start()
{
// 2秒待機する
Debug.Log("コルーチン開始");
yield return new WaitForSeconds(2f);
Debug.Log("コルーチン終了");
}
}
重要なのは次の行です。
yield return new WaitForSeconds(2f);
WaitForSeconds に「2f」を渡しています。
2秒待機する処理になります。
このテクニックは アニメーション や 演出のテンポ を実装するときによく使うので覚えておきましょう。
コルーチンで1フレーム待機する方法
WaitForSeconds のついでに1フレーム待機する方法も紹介。
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
IEnumerator Start()
{
yield return null;
}
}
「yield return null」 を記述すると 1フレーム待機 します。
WaitForSecondsと合わせて覚えておきましょう。
テク③コルーチンの停止3つの手段
コルーチンの具体的なテクニック 「コルーチンの停止」 。
中断とは違います。完全にコルーチンを止めるのです。
コルーチンを止める手段は3つあります。
停止方法①StopCoroutineでコルーチンの停止
指定したコルーチンを停止する StopCoroutine を使います。
StopCoroutineの引数に停止させるコルーチンをセットすることで停止します。
重要なのは 停止させるコルーチンの取得 。
停止させるコルーチンはStartCoroutineの戻り値 で取得可能です。
次のコードを見ていきましょう。
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
IEnumerator Start()
{
// StartCoroutineの戻り値が停止させるコルーチン
Coroutine coroutine = StartCoroutine(TestCoroutine());
yield return new WaitForSeconds(1f);
// 1秒後にコルーチンの停止
StopCoroutine(coroutine);
}
IEnumerator TestCoroutine()
{
int count = 0;
while(true) {
yield return null;
// 毎フレームログを出力
Debug.Log(count++);
}
}
}
重要なポイントは以下。
まずは 動作しているコルーチンの取得 です。
// 動作するコルーチンの取得
Coroutine coroutine = StartCoroutine(TestCoroutine());
StartCoroutineの戻り値はコルーチンインスタンス です。
StopCoroutine(coroutine);
StopCoroutineの引数に停止させたいコルーチンをセット 。
するとコルーチンは停止します。
停止方法②「yield break」でコルーチンの停止
yield break 使ってコルーチンを停止させる方法です。
条件をクリアしたらコルーチンを止める ときに使えます。
下記の例では変数 count
が10以上になったら停止します。
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
void Awake()
{
StartCoroutine(TestCoroutine());
}
IEnumerator TestCoroutine()
{
int count = 0;
while(true) {
if (count < 10) {
yield return null;
} else {
// countが10以上だったらコルーチンを停止
yield break;
}
Debug.Log(count++);
}
}
}
重要なのは下記の whileの中でif分岐 です。
if (count < 10) {
yield return null;
} else {
// countが10以上だったらコルーチンを停止
yield break;
}
countが10以上になったら yield break でコルーチンを停止します。
停止方法③GameObjectを非アクティブにする
GameObjectを非アクティブ(OFF)にするとコルーチンは停止 します。
GameObjectの非アクティブとは、上図のようにGameObjectのチェックボックスが外れた状態です。
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
IEnumerator Start()
{
StartCoroutine(TestCoroutine());
yield return new WaitForSeconds(1f);
gameObject.SetActive(false);
}
IEnumerator TestCoroutine()
{
int count = 0;
while(true) {
yield return null;
Debug.Log(count++);
}
}
}
プログラミングでは↑上記のとおりです。
コルーチンはを実行したコンポーネントのGameObjectのアクティブ状態に依存 します。
もしコルーチンが意図せず途中で止まってしまうときは、GameObjectのアクティブをチェックしてみてください。
こちらの記事で GameObject について詳しく解説しています。ぜひ読んでみてください。
テク④複数のコルーチンを待機するテクニック
複数のコルーチンがすべて終了するまで待機 する方法です。
このテクニックもよく使います。
例えばバトル中に仲間すべての演出が終了したら、敵のターンに移るといった感じ。
演出の時間がバラバラの場合個別にコルーチンを待機する必要があります。
コルーチンインスタンスを保持してfor文で待機する方法 です。
💻ソースコード : 複数のコルーチンを待機するサンプル
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Test : MonoBehaviour
{
IEnumerator Start()
{
Debug.Log("全コルーチン開始");
List<Coroutine> list = new List<Coroutine>();
list.Add(StartCoroutine(TestCoroutine(20, 0)));
list.Add(StartCoroutine(TestCoroutine(30, 1)));
list.Add(StartCoroutine(TestCoroutine(10, 2)));
foreach(var coroutine in list)
{
yield return coroutine;
}
Debug.Log("全コルーチン終了");
}
IEnumerator TestCoroutine(int max, int id)
{
int count = 0;
while(count++ < max) {
yield return null;
}
Debug.Log($"TestCoroutine終了 ID : {id}");
}
}
重要なのは以下の部分です。
List<Coroutine> list = new List<Coroutine>();
// リストにコルーチンを格納
list.Add(StartCoroutine(TestCoroutine(20, 0)));
list.Add(StartCoroutine(TestCoroutine(30, 1)));
list.Add(StartCoroutine(TestCoroutine(10, 2)));
foreach(var coroutine in list)
{
// 全コルーチンの終了を待機
yield return coroutine;
}
StartCoroutineの戻り値をリストに格納するのがポイント です。
実行するとこのようなログがConsoleウィンドウに表示されます。
並列コルーチンはどうしてもコード量が増えてしまいます。
便利に扱うためのライブラリを開発しました。興味ある方は読んでみてください。
【Unity】並列コルーチンを簡単に実装する方法
コルーチンを同時に実行して、全て終了するまで待機したいときがあります。そんな並列コルーチンを簡単に扱うためのライブラリを作ってみました。
テク⑤ボタンをクリックするまでコルーチンを待機する方法
ゲーム開発していると、ボタンをクリックしたら先に進めたい時があります。
具体的には演出のスキップです。
ガチャを回したときの登場演出、バトルの必殺技演出など。
それらの演出をスキップしたいときありますよね。
そんなスキップを実装する時もコルーチンは使えます。
今回はボタンをクリックすると先に進む例を2つのやり方で紹介。
①WaitUntilを使ってボタンクリックを待機
WaitUntil を使った方法の紹介。WaitUntilは引数の条件がtrueになると先に進むコルーチン です。まずはサンプルコードを見てみましょう。
💻ソースコード : WaitUntilのサンプル
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Test : MonoBehaviour
{
public Button button;
public Text text;
IEnumerator Start()
{
bool isClicked = false;
// ボタンクリック処理
var clickedEvent = new Button.ButtonClickedEvent();
clickedEvent.AddListener(() => isClicked = true);
button.onClick = clickedEvent;
// 画面にテキストを表示
text.text = "クリック待機";
// WaitUntilで待機
yield return new WaitUntil(() => isClicked);
text.text = "クリック済み";
}
}
実行すると以下のようにボタンクリックを待機します。
②whileを使ってボタンクリックを待機
whileを使ってボタンクリックを待機する方法を紹介。
whileは条件がfalseになるまで処理をループします。
次のサンプルを見てみましょう。
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Test : MonoBehaviour
{
public Button button;
public Text text;
IEnumerator Start()
{
bool isClicked = false;
// ボタンクリック処理
var clickedEvent = new Button.ButtonClickedEvent();
clickedEvent.AddListener(() => isClicked = true);
button.onClick = clickedEvent;
// 画面にテキストを表示
text.text = "クリック待機";
// whileで待機
while (isClicked == false) {
yield return null;
}
text.text = "クリック済み";
}
}
while と yield return null を使って待機するのです。
変数が isClicked がtrueになるとループが終了します。
先に紹介したWaitUntilと同じ挙動になります。
テク⑥引数付きのコルーチン
コルーチンに引数を渡したくなります。繰り返しになりますが、コルーチンは中断と再開が可能な関数の一種です。
つまりコルーチンは関数です。 通常の関数と同様に引数を定義し、渡すことができます。
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
void Awake()
{
// 引数付きコルーチンを実行
StartCoroutine(TestCoroutine("shibuya", 24));
}
IEnumerator TestCoroutine(string name, int id)
{
yield return null;
Debug.Log($"{name} / {id}");
}
}
上記のサンプルの通りコルーチンに引数を定義して、コルーチンを開始するタイミングで引数を渡すことができます。
テク⑦コルーチンの結果を受け取るテクニック
コルーチンの結果を受け取るテクニックです。
例えば、ロードしたオブジェクトを取得したい場合です。
ただし、コルーチンは戻り値をCorutineしか返せません。そこでデリゲートを使います。
次のサンプルを見てみましょう。
💻ソースコード : コルーチンの結果を受け取るサンプル
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
IEnumerator Start()
{
// コルーチンの結果を受け取る変数
int coroutineResult = 0;
yield return TestCoroutine(x => coroutineResult = x);
Debug.Log(coroutineResult);
}
// 引数にデリゲートを定義
IEnumerator TestCoroutine(Action<int> callback)
{
int result = 100;
yield return null;
// コルーチンの結果を返却
callback?.Invoke(result);
}
}
デリゲートとは関数を変数として扱う機能です。
デリゲートの定義
IEnumerator TestCoroutine(Action<int> callback)
Action<int>
の部分がデリゲートです。
コルーチンの引数にデリゲートを定義します。
このサンプルではコルーチンの結果を数字で返すため int
にしていますが、結果が文字列だったら Action<string>
とします。
またこのように Action<int, string>
複数定義することも可能です。
コルーチンの結果を返却
callback?.Invoke(result);
コルーチンの中で Action<int>
型の変数callbackに結果を返しています。
コルーチン結果の受け取り方法は以下。
int coroutineResult = 0;
yield return TestCoroutine(x => coroutineResult = x);
コルーチンの呼び出し部分で コルーチンの結果を受け取る のです。
この辺りはデリゲートの知識が必要になるため、すこし難しいかもしれません。
ただし、 よく使う手法なので身につけておきたいスキル です。
テク⑧コルーチンでカウントダウン
今までのテクニックを応用したサンプルの紹介です。
カウントダウンを作ってみましょう。
「④WaitForSecondsで指定秒数待機する方法」 で紹介した
WaitForSeconds
と while
を使うことで簡単にカウントダウンを作れます。 ソースコード自体はとても簡単です。
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
int count = 3;
IEnumerator Start()
{
Debug.Log("カウントダウン開始");
yield return CountDown();
Debug.Log("カウントダウン終了");
}
IEnumerator CountDown()
{
while(count >= 0) {
Debug.Log(count);
yield return new WaitForSeconds(1f);
count --;
}
}
}
コルーチン CountDown
内で while文 と WaitForSeconds を使って実装しています。
よく使うテクニックなので覚えておきましょう。
また、 ログ出力の処理をUI表示に置き換え てみます。
するとこのようにカウントダウンが簡単に実装できました。
テク⑨MonoBehaviourを継承せずにコルーチンを使用する方法
今までのサンプルはMonoBehaviourを継承したクラス内でコルーチンを使ってきました。
MonoBehaviour外でコルーチンを使用する方法 の紹介です。
早速サンプルコードから。
💻ソースコード : CoroutineExecutor.cs(MonoBehaviourを継承していない場合)
sing UnityEngine;
using System.Collections;
public class CoroutineExecutor
{
public CoroutineExecutor(MonoBehaviour mono)
{
mono.StartCoroutine(TestCoroutine());
}
IEnumerator TestCoroutine()
{
yield return null;
}
}
MonoBehaviourを継承せずにコルーチンを使う場合は、
MonoBehavioiurを外から渡すことで実現 します。
上記のコードはコンストラクタでMonoBehaviourインスタンスを渡しています。
// コンストラクタでMonoBehaviourインスタンスを渡す
public CoroutineExecutor(MonoBehaviour mono)
{
// MonoBehaviourインスタンスのStartCoroutineを実行
mono.StartCoroutine(TestCoroutine());
}
上記のようにMonoBehaviourインスタンスの StartCoroutine
を実行するとコルーチンは実行します。
ただし、このままでは動きません。
実際にコルーチンを呼ぶコードはこちら。
💻ソースコード : MonoBehaviourを継承しないコルーチンを実行する
using UnityEngine;
public class Test : MonoBehaviour
{
void Awake()
{
// コルーチンを実行するために
// MonoBehavioiur(つまりthis)を渡す
new CoroutineExecutor(this);
}
}
今回のコードではコンストラクタでMonoBehaviourを渡しましたが、
必ずコンストラクタで渡す必要はありません。
コルーチンを実行するためにはMonoBehaviourが必要 。
MonoBehaviourにさえアクセスできれば大丈夫です。
以上でコルーチンを使いこなすテクニックの紹介は終わりです。
たくさんあったかもしれませんが、1つずつ理解してみてください。
ここからはコルーチンの注意点、トラブルの紹介です。
通常の関数と違ってコルーチンの挙動は特殊 です。
意図しない動きをする場合は次に紹介する注意点をチェックしてみてください。
Unityのコルーチン注意点10選
コルーチンを使いこなす上で注意点も理解しておく必要があります。10つの注意点を紹介します。どれも重要なのでぜひ覚えておいてください。
①「yield」のないコルーチンはエラーになる
②GameObjectが非アクティブでコルーチンは停止
③コンポーネントの非アクティブでコルーチンは止まらない
④停止したコルーチンは再開しない
⑤コルーチンは必ず1フレーム消費する
⑥StopCoroutineするときはnullチェックしよう
⑦StartCoroutineは文字列指定しない
⑧StopAllCoroutinesは使用禁止
⑨「yield return 0」は非推奨「yield return null」を推奨
⑩「ref」「out」は使用不可
注意点①「yield」のないコルーチンはエラーになる
コルーチンには必ず yield
が1つ以上必要です。
例として次のコードを見てみましょう。
IEnumerator TestCoroutine()
{
}
yield
が1つもないコルーチン。
つまりこのコルーチンはエラーで、Unityを再生できません。
以下のようなエラーが出た場合はコルーチンに yield
が記述してあるかチェックしてみてください。
error CS0161: 'クラス名.コルーチン名': not all code paths return a value
正しくは以下です。
IEnumerator TestCoroutine()
{
yield return null;
}
上記の例では1フレーム待機する yield return null
を記述。
これによりエラーは解消します。
注意点③コンポーネントの非アクティブでコルーチンは止まらない
「注意点②GameObjectが非アクティブでコルーチンは停止」 と関連していますが、
コルーチンはコンポーネントのアクティブに依存しません。
上図のようにコルーチンを実行した コンポーネントを非アクティブにしてもコルーチンは停止しません。
GameObjectとコンポーネントの非アクティブがごっちゃになることがあります。
整理しながら設計していきましょう。
Unityでよく出てくるキーワード 「コンポーネント」って何? と疑問に思う方もいるかも知れません。
この記事内でも何度も登場しました。
コンポーネントはUnity開発の肝 。正しく理解していないと不具合を生み出してしまいます。
正直Unity初心者にコンポーネントは理解しづらい部分があります。
そんな理解しづらい コンポーネントを徹底的に解説した記事を書きました。
コンポーネントの理解が怪しいと思う方はぜひ読んでみてください。
注意点④停止したコルーチンは再開しない
コルーチンは 「中断と再開」ができる関数 と説明しました。
しかし、 一度でも停止したコルーチンは再開することはできません。
具体的には StopCoroutie、GameObjectの非アクティブで停止した場合は再開不可 です。
ではどのようなパターンで再開できるのでしょうか。
以下のような場合です。
IEnumerator TestCoroutine()
{
// flag1がtrueになるまで中断
while(fag1 == false) yield return null;
// flag2がtrueになるまで中断
while(fag2 == false) yield return null;
// flag3がtrueになるまで中断
while(fag3 == false) yield return null;
}
このように条件をクリアするまで中断した場合に再開が可能。
つまり コルーチンが生きている間は再開ができる のです
コルーチンが再開しなくてトラブったときは、
コルーチンを停止していないかチェックしてみましょう。
注意点⑤コルーチンは必ず1フレーム消費する
コルーチンの特性なのですが、コルーチンは「yield return」の前後で1フレーム時間が経過 します。
たかが1フレーム、されど1フレーム。コルーチンの使用に1フレーム消費するのは コルーチンの宿命 です。
注意点⑥StopCoroutineするときはnullチェックしよう
「停止方法①StopCoroutineでコルーチンの停止」 で紹介しましたが、StopCoroutineでコルーチンを停止するときは引数にCoroutineインスタンスを渡します。
Coroutineインスタンスがnullの場合どうなるでしょうか。
NullReferenceException: routine is null
上記のエラーでUnityが止まります。
対策はこちら。
if (coroutine != null)
{
StopCoroutine(coroutine);
}
上記のように コルーチンがnullではないかチェック してStopCoroutineを実行しましょう。
注意点⑦StartCoroutineは文字列指定しない
StartCoroutineで開始するコルーチンを指定しますが、文字列でもコルーチンを指定できます。
次のサンプルコードを見てみましょう。
void Awake()
{
// 下記2つは同じ処理
StartCoroutine(TestCoroutine());
// 文字列指定も可能
StartCoroutine("TestCoroutine");
}
IEnumerator TestCoroutine()
{
yield return null;
}
文字列"TestCoroutine"
でもStartCoroutineは動作します。
この仕様は古く、 現在のUnityでは不要 です。
文字列指定ではなく、コルーチン関数を直接StartCoroutineに渡しましょう。
メリットはコードの見通しの良さ。Visual Studioなどのエディタでコルーチンが使われている箇所を調べることができるからです。文字列指定でこの機能は使えません。
コードの見通しの良さは地味に感じるかもしれませんが、非常に強力で重要です。
StartCoroutineの引数は文字列ではなく関数指定 にしましょう。
注意点⑧StopAllCoroutinesは使用禁止
すべてのコルーチンを停止する StopAllCoroutines
の使用はやめたほうが無難です。
一見便利なのですが、意図していないコルーチンが停止してしまう可能性があるからです。コルーチンは1つずつ管理するほうが安全。
オオバはUnity歴11年ですが、一度も StopAllCoroutines を使ったことはありません。
注意点⑨「yield return 0」は非推奨「yield return null」を推奨
最後に細かいコルーチンTipsを紹介します。1フレーム待つ方法は複数あるのですが、その中でも yield return 0
はおすすめしません。
理由は メモリにゴミが溜まるから です。
「yield return 0」の場合は GC Alloc に20Bと表示されています。
これがゴミです。
無駄にメモリを確保されゲームのパフォーマンス低下に繋がります。
「yield return null」では0ですよね。
以上の理由から yield return 0は非推奨 なのです。
注意点⑩コルーチンの引数に「ref」「in」「out」は使用不可
コルーチンはC#の文法的に「ref」「in」「out」が使えません。具体的なソースコードで確認します。次のコードはダメな例です。
IEnumerator Hoge (ref int foo)
{
yield return null;
foo = 1;
}
コルーチンの引数に「ref」、「in」、「out」を使用するとコンパイルエラーになります。
error CS1623: Iterators cannot have ref, in or out parameters
このようなエラーログがConsoleウィンドウに表示され、Unityを実行することができません。結論、コルーチンには「ref」「in」「out」が使えないので注意しましょう。
Unityコルーチンのまとめ
この記事ではUnityのコルーチンについて徹底的に解説してきました。
簡単に内容をまとめます。
①コルーチンとは中断と再開可能な関数の一種
②コルーチンはIEnumeratorを戻り型とする
③1つ以上「yield」が必要
④StartCoroutineでデコルーチン開始
⑤StopCoroutine、GameObjectの非アクティブでコルーチン停止
⑥一度停止したコルーチンは再開不可
⑦StopCoroutineの引数はnullチェック必須
こんな感じです。
コルーチンを理解する上で、C#の知識も必要なる部分もありました。初心者には難しかったかもしれません。
本記事でコルーチンに関する基本的な内容は網羅できていると思います。ぜひ この記事をブックマークして困ったときに読み直してみてください。
ゲーム開発で処理の待機は必ず登場 します。最近のUnityではUniTaskの方が主流ですが、Unity初心者はコルーチンを学んでおいて損はありません。
コルーチンを理解した上で別の手法(UniTask)を手に入れていくのが良いと考えてます。この記事があなたのゲーム開発に少しでもお役に立てたら嬉しいです。
ところで、ツール開発をしているとEditモード中にコルーチンを実行させたくなりますが、コルーチンはUnity実行中のみ動きます。しかし Editor Coroutinesを使うとEditモード中にコルーチンが動作する のです。ぜひ次の記事をあわせて読んでみてください。
この記事が気に入ったらフォローしよう
「Unity初心者大学」というUnity初心者向けのYouTube始めました!!
ぜひチャンネル登録をお願いします!
最後まで読んでいただきありがとうございました!
すばらしいコルーチンライフをお過ごしください。
- Unity2020.3.31f1