知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UniRx】時間系のオペレータ #100

前回の成果

メッセージを変換するオペレータについて学んだ。

soramamenatan.hatenablog.com


今回やること

今回は時間系のオペレータについてまとめます。



一定時間後にメッセージを発行したい

Timer

引数で指定した時間経過後に、メッセージを発行することができます。
フレームを指定するTimerFrameも存在します。

一定時間後にメッセージを発行
/// <summary>
/// 一定時間後にメッセージを発行
/// </summary>
private void ExcuteTimer() {
    Observable
        .Timer(TimeSpan.FromSeconds(1))
        .Subscribe(x => {
            Debug.Log("Timer OnNext : " + x);
        }, () => {
            Debug.Log("Timer OnCompleted");
        });
}
結果

引数で指定した1秒が経過した後にメッセージが発行されます。

f:id:soramamenatan:20210428085412p:plain


一定時間後、一定間隔でメッセージを発行したい

Timer

Timerの第二引数で次の値の発行の間隔を指定できます。
フレームを指定するTimerFrameも同様に、第二引数で次の値の発行の間隔を指定できます。

一定時間後に、一定間隔でメッセージを発行
/// <summary>
/// 一定時間後に、一定間隔でメッセージを発行
/// 発行開始と発行間隔は異なる値で指定できる
/// </summary>
private void ExcuteTimerArgument() {
    TimeSpan timeSpan = TimeSpan.FromSeconds(1);
    Observable
        .Timer(timeSpan, timeSpan)
        .Subscribe(x => {
            // xは0からカウントされるlong型が入る
            Debug.Log("TimerArgument OnNext : " + x);
        }, () => {
            Debug.Log("TimerArgument OnCompleted");
        });
}


結果

第一引数で指定した1秒後に最初のメッセージが発行されます。
第二引数で指定した1秒間隔でメッセージが発行され続けます。

f:id:soramamenatan:20210428085845p:plain


Interval

こちらも同様に引数で、次の値の発行の間隔を指定できます。
Timerとの違いは経過時間と間隔が同様の値で扱われることになります。

定義は以下となっており、どちらもTimerObservableを生成しています。

/// <summary>
/// Intervalの定義
/// </summary>
public static IObservable<long> Interval(TimeSpan period)
{
    return new TimerObservable(period, period, Scheduler.DefaultSchedulers.TimeBasedOperations);
}

/// <summary>
/// Timerの定義
/// </summary>
public static IObservable<long> Timer(TimeSpan dueTime, TimeSpan period)
{
    return new TimerObservable(dueTime, period, Scheduler.DefaultSchedulers.TimeBasedOperations);
}

IntervalはTimerObservableの第一引数、第二引数に同じ値を入れており、
Timerの場合は違う値を入れています。

一定時間後に、一定間隔でメッセージを発行
/// <summary>
/// 一定時間後に、一定間隔でメッセージを発行
/// 発行開始と発行間隔はイコール
/// </summary>
private void ExcuteInterval() {
    Observable
        .Interval(TimeSpan.FromSeconds(1))
        .Subscribe(x => {
            Debug.Log("Interval OnNext : " + x);
        }, () => {
            Debug.Log("Interval OnCompleted");
        });
}
結果

引数で指定した1秒後に最初のメッセージが発行され、1秒間隔でメッセージが発行され続けます。

f:id:soramamenatan:20210428090454p:plain


値の発行を遅れさせたい

Delay

引数に指定した時間、値の発行を遅延できます。
フレームを指定するDelayFrameも存在します。

メッセージの発行を遅延させる
/// <summary>
/// メッセージの発行を遅延させる
/// </summary>
private void ExcuteDelay() {
    Observable
        .Range(0, 1)
        .Do(x => {
            Debug.Log("Range Call");
        })
        .Delay(TimeSpan.FromSeconds(1))
        .Subscribe(x => {
            Debug.Log("Delay OnNext");
        }, () => {
            Debug.Log("Delay OnCompleted");
        });
}
結果

まず、Doで呼んでいる"Range Call"が発行されます。
その後、Delayの引数で指定した1秒後にメッセージが発行されます。

f:id:soramamenatan:20210428090736p:plain


最後のOnNextから一定時間OnNextが発行されない場合、OnErrorを発行したい

Timeout

OnNextを発行してから、引数で指定した時間以内にOnNextが発行されない場合に、OnErrorを発行します。

一定時間値が発行されなかったら、onErrorを発行する
/// <summary>
/// 一定時間値が発行されなかったら、onErrorを発行する
/// </summary>
private void ExcuteTimeout() {
    // キーが押されたらメッセージ発行
    IObservable<Unit> input = this.UpdateAsObservable()
                                    .Where(_ => Input.anyKeyDown);

    Observable
        .Timeout(input, TimeSpan.FromSeconds(3))
        .Subscribe(x => {
            Debug.Log("Timeout OnNext");
        }, err => {
            Debug.Log("Timeout OnError");
        }, () => {
            Debug.Log("Timeout OnCompleted");
        });
}
結果

何かキーが押されたらOnNextを発行します。
キーを押してから引数で指定している3秒以内にキーが押されないとOnErrorが発行されます。

f:id:soramamenatan:20210428091219p:plain


一定時間値が発行されない場合、最後の値を流す

Throttle

引数に指定した時間、値が発行されないと最後の値を発行します。
その間に流れてきた値は全て無視されます。
フレームを指定するThrottleFrameも存在します。

一定時間値が発行されなかったら、最後の値を流す
/// <summary>
/// 一定時間値が発行されなかったら、最後の値を流す
/// </summary>
private void ExcuteThrottle() {
    // キーが押されたらメッセージ発行
    IObservable<Unit> input = this.UpdateAsObservable()
                                    .Where(_ => Input.anyKeyDown);

    Observable
        .Throttle(input, TimeSpan.FromSeconds(3))
        .Subscribe(x => {
            Debug.Log("Throttle OnNext");
        }, () => {
            Debug.Log("Throttle OnCompleted");
        });
}
結果

キーが押されてから、引数で指定した3秒間キーが押されないとOnNextが発行されます。

f:id:soramamenatan:20210428091908p:plain


値が来たら発行し、一定時間は発行しない

ThrottleFirst

最初に値が来たらその値を流し、引数に指定した時間は値の発行を無視します。
フレームを指定するThrottleFirstFrameもあります。
似たオペレータにSampleがありますが、こちらは最初の値を発行します。

最初は値を発行し、一定時間発行をしない
/// <summary>
/// 最初は値を発行し、一定時間発行をしない
/// Sampleとの違いは、最初の値を発行する
/// </summary>
private void ExcuteThrottleFirst() {
    // キーが押されたらメッセージ発行
    IObservable<Unit> input = this.UpdateAsObservable()
                                    .Where(_ => Input.anyKeyDown);

    Observable
        .ThrottleFirst(input, TimeSpan.FromSeconds(3))
        .Subscribe(x => {
            Debug.Log("ThrottleFirst OnNext");
        }, () => {
            Debug.Log("ThrottleFirst OnCompleted");
        });
}
結果

キーが押されるとOnNextが発行されます。
しかしその後、3秒間はキーを押してもOnNextは発行されません。

f:id:soramamenatan:20210429110023p:plain


一定間隔でメッセージを発行する

Sample

引数で指定した間隔で、メッセージが流れてきたら発行します。
似たオペレータThrottleFirstがありますが、こちらは最後の値を発行します。

一定間隔でメッセージを発行
/// <summary>
/// 一定間隔でメッセージを発行
/// ThrottleFirstとの違いは最後の値を発行する
/// </summary>
private void ExcuteSample() {
    // 1フレーム毎にメッセージを発行
    IObservable<long> inteval = Observable.IntervalFrame(1);

    Observable
        .Sample(inteval, TimeSpan.FromSeconds(1))
        .Subscribe(x => {
            Debug.Log("Sample OnNext");
        }, () => {
            Debug.Log("Sample OnCompleted");
        });
}
結果

intervalストリームにより、1フレーム毎にメッセージを発行しようとしています。
しかし、Sampleで1秒毎に指定しているので、1秒毎にOnNextが発行されます。

f:id:soramamenatan:20210429110547p:plain


次のフレームで処理をする

NextFrame

処理を1フレーム遅延させることができます。

次のフレームで処理をする
/// <summary>
/// 次のフレームで処理をする
/// </summary>
private void ExcuteNextFrame() {
    Observable
        .NextFrame()
        .Subscribe(x => {
            Debug.Log("NextFrame OnNext");
        }, () => {
            Debug.Log("NextFrame OnCompleted");
        });
}
結果

1フレーム後にOnNextが発行されます。

f:id:soramamenatan:20210429110826p:plain


今回は以上となります。
ここまでご視聴ありがとうございました。


参考サイト様

light11.hatenadiary.com

techium.hatenablog.com