1. Qiita
  2. 投稿
  3. Unity

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

  • 104
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

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フレーム後に実行したい

フラグとカウンタを用意してUpdateでインクリメントする

論外
そんな実装今直ぐ投げ捨てるべき。

コルーチンを使う

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));

Observable.Return + Delayを使うパターン。個人的にはこっちのほうがReadableで好きです。
しかもパラメータの受け渡しがストリーム内で完結してスマート。
複数パラメータ渡したい場合は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でストリーム化した方がいろいろやりやすいです。

あと、Updateで入力を受け付けてFixedUpdateでRigidBodyを操作するというものをUniRxで書こうとしたけど失敗しました。
多分、SampleFrame使えばいいんでしょうけど…?

Comments Loading...