MRが楽しい

MRやVRについて学習したことを書き残す

Unityのコルーチン機能を使ってみる

本日はUnityの技術枠です。
サンプルプログラムを読んでいたところ、以下の気になるコードを見つけました。

yield return null;

調べてみたところ、Unityのコルーチン機能を利用したコードのようです。
docs.unity3d.com

コルーチン自体はC#で提供されている機能ですが、UnityScriptではフレーム間の処理の制御で色々な応用ができるようです。
処理を再開するタイミングを yield return 後の引数によって決定できます。
以下の記事が詳しいです。
cfm-art.sakura.ne.jp

//次のフレームに再開します。
yield return null;

//次のフレームに再開します。(nullとはフローが異なる)
yield return new WaitForEndOfFrame();

//指定秒数後に再開します。
yield return new WaitForSeconds(秒数);

//再開条件に指定した関数がtrueを返すと再開します。
yield return new WaitUntil(再開条件);

//待機条件にした関数がfalseを返すと再開します。
yield return new WaitWhile(待機条件);

//関数は再開されずにそこで終わります。
yield break;

//別のコルーチンを新たに実行しそれが終わるまで中断します。
yield return StartCoroutine();

yield return null と yield return new WaitForEndOfFrame() は一見同じですが、厳密にはフレーム内での再開タイミングが異なるようです。
robamemo.hatenablog.com

勉強のため、試してみます。
f:id:bluebirdofoz:20171213014629j:plain

以下のスクリプトを作成してみました。
・YieldTest01.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class YieldTest01 : MonoBehaviour {

    public int count = 0;
	// Use this for initialization
	void Start () {
        StartCoroutine(YieldNull("NULL"));
        StartCoroutine(YieldWaitForEndOfFrame("WAIT"));
        StartCoroutine(YieldStartCoroutine("START"));
    }
	
	// Update is called once per frame
	void Update () {
        if (count < 10) {
            Debug.Log("YieldTest01 Update : " + count);
        }
        count++;
    }

    private IEnumerator YieldNull(string comment)
    {
        Debug.Log("YieldNull:Step_01:" + comment);
        yield return null;
        Debug.Log("YieldNull:Step_02:" + comment);
        yield return null;
        Debug.Log("YieldNull:Step_03:" + comment);
        yield return null;
    }

    private IEnumerator YieldWaitForEndOfFrame(string comment)
    {
        Debug.Log("YieldTestWaitForEndOfFrame:Step_01:" + comment);
        yield return new WaitForEndOfFrame();
        Debug.Log("YieldTestWaitForEndOfFrame:Step_02:" + comment);
        yield return new WaitForEndOfFrame();
        Debug.Log("YieldTestWaitForEndOfFrame:Step_03:" + comment);
        yield return new WaitForEndOfFrame();
    }

    private IEnumerator YieldStartCoroutine(string comment)
    {
        Debug.Log("YieldStartCoroutine:Step_01:" + comment);
        yield return StartCoroutine(YieldStartCoroutineNull("STARTNULL"));
        Debug.Log("YieldStartCoroutine:Step_05:" + comment);
        yield break;
        Debug.Log("YieldStartCoroutine:Step_06:" + comment);
        yield break;
    }

    private IEnumerator YieldStartCoroutineNull(string comment)
    {
        Debug.Log("YieldStartCoroutine:Step_02:" + comment);
        yield return null;
        Debug.Log("YieldStartCoroutine:Step_03:" + comment);
        yield return null;
        Debug.Log("YieldStartCoroutine:Step_04:" + comment);
        yield return null;
    }
}

テスト用のゲームオブジェクトにスクリプトをアタッチします。
f:id:bluebirdofoz:20171213014655j:plain

実行結果は以下のようになりました。
f:id:bluebirdofoz:20171213014702j:plain

YieldNull(一回目)
↓
YieldWaitForEndOfFrame(一回目)
↓
YieldStartCoroutine(一回目)
↓
YieldStartCoroutineNull(一回目)
↓
フレーム終了(一回目)
↓
YieldNull(二回目)
↓
YieldStartCoroutineNull(二回目)
↓
YieldWaitForEndOfFrame(二回目)
↓
フレーム終了(二回目)
↓
YieldNull(三回目)
↓
YieldStartCoroutineNull(三回目)
↓
YieldWaitForEndOfFrame(三回目)
↓
フレーム終了(三回目)
↓
YieldStartCoroutine(二回目)
↓
フレーム終了(四回目)

説明通りですね。負荷分散や疑似的な並列処理の実施など色々と応用が利きそうです。
それと共にコードが非常に読みづらくなるので多用は禁物です。