はんなりと、ゆるやかに

アジャイル、スクラムが好きが日々から学んだことをアウトプット

Unityのコルーチンについて調べた

Unityのコルーチンが分かりそうで分からなかったのでコード書いて体感してみました。
コード書いて理解したことを一言でいうと

yield return と書いたところ処理が一時停止し、次のフレームで再開する仕組み。

です。

サンプルプログラムです。ボタンを押すとテキストに書かれた数値が1から10までカウントアップします。

    public GameObject text = null;

    public void OnClick()
    {
        if (text == null)
        {
            return;
        }

        StartCoroutine(Count());
    }

    IEnumerator Count()
    {
        for(int i = 1; i <= 10; ++i)
        {
            text.GetComponent<Text>().text = i.ToString();
            // yield return null;
            yield return new WaitForSeconds(.1f);
        }
    }

コードの説明

重要な部分以外は端折っていますが、画面上のボタンが押されたらOnClickが実行され、Countのコルーチンが実行されます。Countのfor文は10ループしますが、テキストの文字を1にした後、yield return の部分で処理が止まります。0.1秒後に再開されテキストの文字を2にした後、やはりyield return の部分で処理が止まります。10回繰り返せば終わりです。

yield return null; と書けば次のフレームで再開されます。
yield return new WaitForSeconds(.1f); と書けば0.1秒後に再開されます。

補足

StartCoroutineではなく、普通に関数呼び出しするとボタンを押したあといきなり10になります。コルーチンだからこそカウントアップが確認できます。
(関数呼び出しにするときは、yield returnとか使えないので微修正は必要です。)

何が嬉しいか

似たようなことはUpdate関数でも実現できます(Update関数はフレームごとに呼ばれる関数です)。しかしコルーチンを使うと無駄な処理をスキップできたり、負荷が軽減できます。

無駄な処理をスキップ

Update関数で実現する場合、ローカル変数は毎回初期化されるため、カウントを記憶するメンバ変数が必要です。また、毎フレームごとに関数の先頭から処理するため、不要な処理も実行されます。

コルーチンの場合は、一時停止した場所から再開できるため、ローカル変数は一時提出した値から再開できます。また、関数の途中から再開されるため、関数のはじめの方に書いたコードはスキップされます。

コルーチンを使うメリットは以下だと感じました。

  • 変数のスコープを小さく出来るため、可読性が良い
  • 関数の途中から再開できるため、同じ処理は省略できる

負荷を軽減できる

Update関数はフレームごとに呼ばれるため、60FPSだった場合は0.015秒ごとに処理が実行されます。頻度を下げても問題ない場合はyield return new WaitForSeconds(.1f);と書くことで、0.1秒ごとに処理が再開されるため、処理負荷が軽減されます。

定期的な処理をUpdate関数で実行することもできますが、フレームよりも遅い頻度で処理しても良い場合はコルーチンを使うことで処理負荷が軽減できます。

コルーチンを使うメリットは以下だと感じました。

  • 処理頻度を制御しやすい

まとめ

コルーチンについて調べてみました。
コルーチンはyield return と書いたところ処理が一時停止し、次のフレームで再開する仕組みでした。コルーチンを使うことでコードがシンプルになり保守性が上がったり、負荷を分散できる書き方だと思いました。

参考サイト

公式サイトの説明は分かりやすいです。はじめはピンとこなかったのですが、コードを書いてから読み直すと理解できました。
docs.unity3d.com