Unity
UniRx

【Unity】スクリプトの処理の実行タイミングを操作する

More than 1 year has passed since last update.

Unityでゲーム開発をしていると、「ある処理を一定時間後や特定のタイミングで実行したい」といった事がよくあるので、そのあたりのやり方をまとめてみました。


Unityの標準機能のみで書く場合


処理をn秒後に実行したい


Invokeを使う

private void Start()

{
//DelayMethodを3.5秒後に呼び出す
Invoke("DelayMethod", 3.5f);
}

void DelayMethod()
{
Debug.Log("Delay call");
}

多分一番シンプルです。

Invokeを使うことで指定メソッドをn秒後に実行させることができます。

ただしメソッドを文字列指定しなくてはいけなかったり、メソッドに引数を渡すことができないなど使い勝手はそこまで良く無いです。


コルーチンを使う

private void Start()

{
//3.5秒後に実行する
StartCoroutine(DelayMethod(3.5f, () =>
{
Debug.Log("Delay call");
}));
}

/// <summary>
/// 渡された処理を指定時間後に実行する
/// </summary>
/// <param name="waitTime">遅延時間[ミリ秒]</param>
/// <param name="action">実行したい処理</param>
/// <returns></returns>
private IEnumerator DelayMethod(float waitTime, Action action)
{
yield return new WaitForSeconds(waitTime);
action();
}

コルーチンとWaitForSecondsを組み合わせてあげるパターン。

Invokeと違ってタイプセーフだし、引数も渡せるしとこっちの方が何かと良いかと思います。

MonoBehaviourに拡張メソッドとして生やしておくと便利かも。


nフレーム後に実行したい


コルーチンを使う

private void Start()

{
//5フレーム後に実行する
StartCoroutine(DelayMethod(5, () =>
{
Debug.Log("Delay call");
}));
}

/// <summary>
/// 渡された処理を指定時間後に実行する
/// </summary>
/// <param name="delayFrameCount"></param>
/// <param name="action">実行したい処理</param>
/// <returns></returns>
private IEnumerator DelayMethod(int delayFrameCount, Action action)
{
for (var i = 0; i < delayFrameCount; i++)
{
yield return null;
}
action();
}

コルーチンでn秒待つ奴とそんなに変わらないです。

これも拡張メソッド化して生やしておくと便利かも。


UniRxを使う場合


n秒後に実行したい


Observable.Timerを使う

//ただ呼び出すパターン

//100ミリ秒後にLogを出す
Observable.Timer(TimeSpan.FromMilliseconds(100))
.Subscribe(_ => Debug.Log("Delay call"));

//パラメータを渡すパターン
//現在のプレイヤの座標を500ミリ秒後に表示する
var playerPosition = transform.position;
Observable.Timer(TimeSpan.FromMilliseconds(500))
.Subscribe(_ => Debug.Log("Player Position:" + playerPosition));

Timerを使うパターン。

パラメータを渡そうとするとストリーム外で値の保持が必要になるため、複数同時にタイマ登録すると事故る可能性があります。


Delayを使う

//ただ呼び出すパターン

//100ミリ秒後にLogを出す
Observable.Return(Unit.Default)
.Delay(TimeSpan.FromMilliseconds(100))
.Subscribe(_ => Debug.Log("Delay call"));

//パラメータを渡すパターン
//現在のプレイヤの座標を500ミリ秒後に表示する
Observable.Return(transform.position)
.Delay(TimeSpan.FromMilliseconds(500))
.Subscribe(p => Debug.Log("Player Position:" + p));

複数パラメータ渡したい場合はUniRx.Tupleを使うといいかも。


nフレーム後に実行したい


Observable.TimerFrameを使う

//次のフレームで実行する

Observable.TimerFrame(1)
.Subscribe(_ => Debug.Log("Next Update"));

//次のFixedUpdateで実行する
Observable.TimerFrame(1,FrameCountType.FixedUpdate)
.Subscribe(_ => Debug.Log("Next FixedUpdate"));

Observable.Timerとそれほど変わらないです。


DelayFrameを使う

//次のフレームで実行する

Observable.Return(Unit.Default)
.DelayFrame(1)
.Subscribe(_ => Debug.Log("Next Update"));

//次のFixedUpdateで実行する
Observable.Return(Unit.Default)
.DelayFrame(1, FrameCountType.FixedUpdate)
.Subscribe(_ => Debug.Log("Next FixedUpdate"));

Delayとそんなに変わらないです。


次のフレームで実行する

次のフレームで実行したいといった場合はNextFrameを使った方がスマート。


NextFrame

//次のフレームで実行する

Observable.NextFrame()
.Subscribe(_ => Debug.Log("Next Frame"));


まとめ

ただ処理を遅延させたいだけならコルーチンで書いたほうが楽です。

処理を遅延させた後、まだ何か処理を続けるんであればUniRxでストリーム化した方がいろいろやりやすいです。