MRが楽しい

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

コルーチンの処理をQueueを使ってLateUpdateのタイミングで実行する

本日は Unity の小ネタ枠です。
コルーチンの処理をQueueを使ってLateUpdateのタイミングで実行する方法を調査したので記事にします。

コルーチンの実行タイミング

Unity のコルーチンでは全ての Update 処理が行われた後、yield 以降の処理が呼び出されます。
そして全てのコルーチンの処理が完了した後、LateUpdate が実行されます。
docs.unity3d.com

今回、コルーチンでの処理を LateUpdate のタイミングで実行したかったので Queue を使って実現するサンプルスクリプトを作成しました。

サンプルスクリプト

以下のサンプルスクリプトを作成しました。コンテキストメニューから[MoveUp]を実行するとオブジェクトが徐々に上移動します。
このとき徐々に移動する座標の計算はコルーチンで行いますが、実際の処理は Queue を通して計算結果を引き渡し LateUpdate で行います。
・CoroutineTest.cs

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

public class CoroutineTest : MonoBehaviour
{
    /// <summary>
    /// ポジション移動タスク用キュー
    /// </summary>
    private Queue<Vector3> p_MovePositionQueue = new Queue<Vector3>();

    /// <summary>
    /// 実行中フラグ
    /// </summary>
    private bool p_Processing = false;

    /// <summary>
    /// 徐々に上方向に移動する
    /// </summary>
    [ContextMenu("MoveUp")]
    public void MoveUp()
    {
        StartCoroutine("LerpMoveUp");
    }

    private IEnumerator LerpMoveUp()
    {
        // 実行中フラグの確認
        if(p_Processing) yield break;
        p_Processing = true;

        // 開始時の座標
        Vector3 startPosition = this.gameObject.transform.position;

        // 終了時の座標
        Vector3 endPosition = new Vector3(
            startPosition.x,
            startPosition.y + 1.0f,
            startPosition.z);

        int loopCount = 100;
        for (int loop = 0; loop < loopCount; loop++)
        {
            // 移動先を計算する
            Vector3 movePosition = Vector3.Lerp(startPosition, endPosition, (1.0f / loopCount) * loop);
            // ここでは移動は行わない
            // キューに移動先を登録するのみ
            p_MovePositionQueue.Enqueue(movePosition);
            yield return null;
        }
        // 最終的な移動先を登録する
        p_MovePositionQueue.Enqueue(endPosition);
        p_Processing = false;
    }

    void LateUpdate()
    {
        Debug.Log(Time.frameCount.ToString() + " : LateUpdate");
        // LateUpdateで行いたい処理を実行する
        lock (p_MovePositionQueue)
        {
            while(p_MovePositionQueue.Count > 0)
            {
                MovePosition(p_MovePositionQueue.Dequeue());
            }
        }
    }

    /// <summary>
    /// 移動処理
    /// </summary>
    /// <param name="a_Position"></param>
    private void MovePosition(Vector3 a_Position)
    {
        Debug.Log(Time.frameCount.ToString() + " : Moved = " + a_Position.ToString());
        this.transform.position = a_Position;
    }
}

スクリプトをアタッチしたシーンを再生してコンテキストメニューから関数を実行してみました。
ログから LateUpdate のタイミングで処理が行われていることが分かります。
f:id:bluebirdofoz:20220414234726j:plain

コルーチン内で処理を行った場合

コルーチン内でそのまま処理を行った場合も比較してみました。
・CoroutineTest2.cs

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

public class CoroutineTest2 : MonoBehaviour
{
    /// <summary>
    /// 実行中フラグ
    /// </summary>
    private bool p_Processing = false;

    /// <summary>
    /// 徐々に上方向に移動する
    /// </summary>
    [ContextMenu("MoveUp")]
    public void MoveUp()
    {
        StartCoroutine("LerpMoveUp");
    }

    private IEnumerator LerpMoveUp()
    {
        // 実行中フラグの確認
        if(p_Processing) yield break;
        p_Processing = true;

        // 開始時の座標
        Vector3 startPosition = this.gameObject.transform.position;

        // 終了時の座標
        Vector3 endPosition = new Vector3(
            startPosition.x,
            startPosition.y + 1.0f,
            startPosition.z);

        int loopCount = 100;
        for (int loop = 0; loop < loopCount; loop++)
        {
            // 移動先を計算する
            Vector3 movePosition = Vector3.Lerp(startPosition, endPosition, (1.0f / loopCount) * loop);
            // 座標に移動する
            MovePosition(movePosition);
            yield return null;
        }
        // 最終的な座標に移動する
        MovePosition(endPosition);
        p_Processing = false;
    }

    void LateUpdate()
    {
        Debug.Log(Time.frameCount.ToString() + " : LateUpdate");
    }

    private void MovePosition(Vector3 a_Position)
    {
        Debug.Log(Time.frameCount.ToString() + " : Moved = " + a_Position.ToString());
        this.transform.position = a_Position;
    }
}

こちらではコルーチン内の処理が行われた後、LateUpdate の処理が実行されていることが分かります。
f:id:bluebirdofoz:20220414234749j:plain