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

# 新しい記事

この記事も古くなってきたので新しく書き直しました

↓↓↓↓↓↓↓↓↓↓↓
* [【Unity】一定時間後にスクリプトの処理を呼び出す方法まとめ](https://qiita.com/toRisouP/items/e6d4f114d434ee588044) 
↑↑↑↑↑↑↑↑↑↑↑




----

# 以下ちょっと古い内容

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


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

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

###Invokeを使う
```csharp
private void Start()
{
    //DelayMethodを3.5秒後に呼び出す
    Invoke("DelayMethod", 3.5f);
}

void DelayMethod()
{
    Debug.Log("Delay call");
}
```
多分一番シンプルです。
Invokeを使うことで指定メソッドをn秒後に実行させることができます。
ただしメソッドを文字列指定しなくてはいけなかったり、メソッドに引数を渡すことができないなど使い勝手はそこまで良く無いです。

#コルーチンを使う
```csharp
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フレーム後に実行したい

###コルーチンを使う

```csharp
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を使う
```csharp
//ただ呼び出すパターン
//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を使う
```csharp
//ただ呼び出すパターン
//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を使う
```csharp
//次のフレームで実行する
Observable.TimerFrame(1)
    .Subscribe(_ => Debug.Log("Next Update"));

//次のFixedUpdateで実行する
Observable.TimerFrame(1,FrameCountType.FixedUpdate)
    .Subscribe(_ => Debug.Log("Next FixedUpdate"));
```
Observable.Timerとそれほど変わらないです。


###DelayFrameを使う
```csharp
//次のフレームで実行する
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を使った方がスマート。

```csharp:NextFrame
//次のフレームで実行する
Observable.NextFrame()
    .Subscribe(_ => Debug.Log("Next Frame"));
```


#まとめ
ただ処理を遅延させたいだけならコルーチンで書いたほうが楽です。
処理を遅延させた後、まだ何か処理を続けるんであればUniRxでストリーム化した方がいろいろやりやすいです。