本日は 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 のタイミングで処理が行われていることが分かります。
コルーチン内で処理を行った場合
コルーチン内でそのまま処理を行った場合も比較してみました。
・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 の処理が実行されていることが分かります。