30
15

More than 3 years have passed since last update.

【Unity】非同期処理を理解する〜コルーチン編〜

Posted at

コルーチンとは

  • 非同期処理できる。
  • 関数を任意のタイミングで中断・再開できる機能。
  • 複数の処理を疑似並列できる。(完全な並列ではない)
  • Unity のメインスレッドで処理する。
  • 結果は返せない。
  • GameObject と処理が紐づく。
  • IEnumerator 型を戻り値とした関数を定義する。
  • コルーチンを実行したいときは StartCoroutine(string コルーチン名) または StartCoroutine(IEnumerator型の変数)
  • コルーチンを停止(中断)したいときは StopCoroutine(string コルーチン名) または StopCoroutine(IEnumerator型の変数)

コルーチンの主要な機能一覧

機能 詳細
yield return null 1フレーム分処理を中断、次のフレームで続きの行を処理
yield break コルーチンを途中で終了、再開はできない
yield return new WaitForSeconds(float seconds) 指定した seconds 秒、コルーチンを中断する
yield return new WaitUntil(Func predicate) predicate の関数の返り値が true になったら処理を再開する
yield return new WaitWhile(Func predicate) predicate の関数の返り値が false になったら処理を再開する
yield return StartCoroutine() 指定したコルーチンを実行、完了するまで後続処理は行わない
StopAllCoroutines() コルーチンを全て止める

yield return null

yield return null は、1フレーム分処理を中断して、次のフレームで続きの行を処理します。

using System.Collections;
using UnityEngine;

public class Test : MonoBehaviour
{
    void Start()
    {
        IEnumerator sample1 = Sample1();
        IEnumerator sample2 = Sample2();
        StartCoroutine(sample1);
        StartCoroutine(sample2);
    }

    IEnumerator Sample1()
    {
        Debug.Log("Sample1 Start.");
        yield return null;            // Sample1()の処理は1フレーム待機
        Debug.Log("Sample1 End.");
    }

    IEnumerator Sample2()
    {
        Debug.Log("Sample2 Start.");
        yield return null;            // Sample2()の処理は1フレーム待機
        Debug.Log("Sample2 End.");
    }
}

だから、結果は下の通りで、
1. Sample1 Start.
2. Sample2 Start.
3. Sample1 End.
4. Sample2 End.
の順で実行される。
image.png

yield break

yield break は、コルーチンを途中で終了します。
再開はできないです。

using System.Collections;
using UnityEngine;

public class Test : MonoBehaviour
{
    void Start()
    {
        IEnumerator sample1 = Sample1();
        IEnumerator sample2 = Sample2();
        StartCoroutine(sample1);
        StartCoroutine(sample2);
    }

    IEnumerator Sample1()
    {
        Debug.Log("Sample1 Start.");
        yield break;                  // Sample1() の処理はここで止める
        Debug.Log("Sample1 End.");
    }

    IEnumerator Sample2()
    {
        Debug.Log("Sample2 Start.");
        yield break;                  // Sample2() の処理をここで止める
        Debug.Log("Sample2 End.");
    }
}

コルーチンを途中で終了したので、Sample1 End.Sample2 End. は出力されません。
image.png

yield return new WaitForSeconds(float seconds)

yield return new WaitForSeconds(seconds) は、指定した秒数の間、コルーチンの実行を待ちます。

using System.Collections;
using UnityEngine;

public class Test : MonoBehaviour
{
    void Start()
    {
        IEnumerator sample1 = Sample1();
        IEnumerator sample2 = Sample2();
        StartCoroutine(sample1);
        StartCoroutine(sample2);
    }

    IEnumerator Sample1()
    {
        Debug.Log("Sample1 Start.");
        yield return null;            // Sample1()の処理は1フレーム待機
        Debug.Log("Sample1 End.");
    }

    IEnumerator Sample2()
    {
        Debug.Log("Sample2 Start.");
        yield return new WaitForSeconds (1.0f); // Sample2()の処理は1秒待機
        Debug.Log("Sample2 End.");
    }
}

Sample2 End. が、他より1秒経過してから出力されるようになります。
image.png

yield return new WaitUntil(Func predicate)

yield return new WaitUntil(Func<bool> predicate) は、predicate で指定した関数が true を返したときに再開します。

using System.Collections;
using UnityEngine;

public class Test : MonoBehaviour
{
    bool flg = false;
    void Start()
    {
        IEnumerator sample1 = Sample1();
        IEnumerator sample2 = Sample2();
        StartCoroutine(sample1);
        StartCoroutine(sample2);
    }

    IEnumerator Sample1()
    {
        Debug.Log("Sample1 Start.");
        yield return new WaitUntil(() => flg == true); // flg が true になるまで処理が止まる
        Debug.Log("Sample1 End.");
    }

    IEnumerator Sample2()
    {
        Debug.Log("Sample2 Start.");
        yield return null;
        Debug.Log("Sample2 End.");
        flg = true; // Sample2()の最後に flg を true にする
    }
}

flg が true になるまで、 Sample1 End. が出力されないため、下記のような結果になります。
image.png

yield return new WaitWhile(Func predicate)

yield return new WaitWhile(Func<bool> predicate) は、predicate で指定した関数が false を返したときに再開します。
WaitUntil(Func<bool> predicate) の逆バージョン。
使い方は上と一緒なので省略。

yield return StartCoroutine()

yield return StartCoroutine() は、指定したコルーチンを実行し、完了するまで処理を中断します。

using System.Collections;
using UnityEngine;

public class Test : MonoBehaviour
{
    bool flg = false;
    void Start()
    {
        IEnumerator sample1 = Sample1();
        StartCoroutine(sample1);
    }

    IEnumerator Sample1()
    {
        Debug.Log("Sample1 Start.");
        IEnumerator sample2 = Sample2();
        yield return StartCoroutine(sample2); // Sample2() を実行が終わるまで待機
        Debug.Log("Sample1 End.");
    }

    IEnumerator Sample2()
    {
        Debug.Log("Sample2 Start.");
        yield return null;           // Sample2()の処理は1秒待機
        Debug.Log("Sample2 End.");
    }
}

結果は下のような感じです。
image.png

StopAllCoroutines()

名前の通り、コルーチンの全てを止めます。
Behavior 上で実行されている全てを止めます。

using System.Collections;
using UnityEngine;

public class Test : MonoBehaviour
{
    void Start()
    {
        IEnumerator sample1 = Sample1();
        IEnumerator sample2 = Sample2();
        StartCoroutine(sample1);
        StartCoroutine(sample2);
        StopAllCoroutines(); // 全てのコルーチンを止める
    }

    IEnumerator Sample1()
    {
        Debug.Log("Sample1 Start.");
        yield return null;
        Debug.Log("Sample1 End.");
    }

    IEnumerator Sample2()
    {
        Debug.Log("Sample2 Start.");
        yield return null;
        Debug.Log("Sample2 End.");
    }
}

コルーチンを途中で終了したので、Sample1 End.Sample2 End. は出力されません。
image.png

終わりに

Unity 初心者なので、間違いがあったら教えてくれるとありがたいです。
今まで雰囲気で非同期処理を書いていたので、しっかり勉強するために記事を書くことにしました。
いまのところ、async/await編、UniRx編を書く予定です。
挫折したらごめんなさい。

参考文献

コルーチン - Unity マニュアル
【C#/Unity】コルーチン(Coroutine)とは何なのか
Unityのコルーチン機能を使う

30
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
15