知識0からのUnityShader勉強

知識0からのUnityShader勉強

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

【UniRx】ファクトリメソッドオペレータ #93

前回の成果

UniTaskのキャンセルについて学んだ。

soramamenatan.hatenablog.com


今回やること

UniRxで何かしたいときに、どのオペレータを呼べばよいのか混乱したので、具体例も合わせてまとめます。


ファクトリメソッド

UniRxが用意してくれているストリームソースメソッド群になります。

値を1つだけ発行

Observable.Return
/// <summary>
/// 値を1つだけ発行
/// </summary>
private void ExcuteReturn() {
    // 発行する値
    int value = 1;
    Observable
        .Return(value)
        .Subscribe(x => Debug.Log("Return : " + x));
}
結果

f:id:soramamenatan:20210227133640p:plain


値を繰り返し発行

Observable.Repeat
/// <summary>
/// 値を繰り返し発行
/// </summary>
private void ExcuteRepeat() {
    // 発行する値
    int value = 1;
    // 繰り返す数
    int repeat = 3;
    Observable
        .Repeat(value, repeat)
        .Subscribe(x => Debug.Log("Repeat : " + x));
}
結果

f:id:soramamenatan:20210227133826p:plain


指定した範囲で数値を発行

Observable.Range
/// <summary>
/// 指定した範囲で数値を発行
/// </summary>
private void ExcuteRange() {
    // 開始する値
    int start = 5;
    // 繰り返す数
    int count = 3;
    Observable
        .Range(start, count)
        .Subscribe(x => Debug.Log("Range : " + x));
}
結果

f:id:soramamenatan:20210227133946p:plain


ObservableをSubscribe時まで遅延

Observable.Defer
/// <summary>
/// ObservableをSubscribe時まで遅延
/// </summary>
private void ExcuteDefer() {
    // 遅延させるObservable
    IObservable<DateTime> deferObservable = Observable.Return(DateTime.Now);
    Observable.Defer(() =>
        deferObservable
    ).Subscribe(x => {
        Debug.Log("Defer : " + x);
    });
}
結果

f:id:soramamenatan:20210227134047p:plain


一定時間後に値を発行

Observable.Timer
/// <summary>
/// 一定時間後に値を発行
/// </summary>
private void ExcuteTimer() {
    int seconds = 1;
    Observable
        .Timer(TimeSpan.FromSeconds(seconds))
        .Subscribe(x => {
            Debug.Log("Timer : " + x);
        });
}
結果

f:id:soramamenatan:20210227134149p:plain


指定フレーム後に値を発行

Observable.TimerFrame
/// <summary>
/// 指定フレーム後に値を発行
/// </summary>
private void ExcuteTimerFrame() {
    int frame = 30;
    Observable
        .TimerFrame(frame)
        .Subscribe(x => {
            Debug.Log("TimerFrame : " + x);
        });
}
結果

f:id:soramamenatan:20210227134242p:plain


一定間隔で値を発行

Observable.Timer, Observable.Interval
/// <summary>
/// 一定間隔で値を発行
/// </summary>
private void ExcuteTimerInterval() {
    // 開始までの時間
    int dueTime = 2;
    // 間隔
    int span = 1;
    // TimerとIntervalとの違いは、開始までの時間を指定できるかどうか
    Observable
        .Timer(TimeSpan.FromSeconds(dueTime), TimeSpan.FromSeconds(span))
        .Subscribe(x => {
            Debug.Log("Timer : " + x);
        });

    Observable
        .Interval(TimeSpan.FromSeconds(span))
        .Subscribe(x => {
            Debug.Log("Interval : " + x);
        });
}
結果

f:id:soramamenatan:20210227134519p:plain


一定フレーム間隔で値を発行

Observable.TimerFrame, Observable.IntervalFrame
/// <summary>
/// 一定フレーム間隔で値を発行
/// </summary>
private void ExcuteFrameTimerInterval() {
    // 開始までの時間
    int dueTime = 120;
    // 間隔
    int span = 60;
    // TimerとIntervalとの違いは、開始までの時間を指定できるかどうか
    Observable
        .TimerFrame(dueTime, span)
        .Subscribe(x => {
            Debug.Log("TimerFrame : " + x);
        });

    Observable
        .IntervalFrame(span)
        .Subscribe(x => {
            Debug.Log("IntervalFrame : " + x);
        });
}
結果

f:id:soramamenatan:20210227134722p:plain


値を発行するストリームの生成

Observable.Create
/// <summary>
/// 値を発行するストリームの生成
/// </summary>
private void ExcuteCreate() {
    Observable
        .Create<int>(observer => {
            int value = 1;
            observer.OnNext(value);
            observer.OnCompleted();
            return Disposable
                .Create(() => {
                    Debug.Log("Dispose");
                });
            }).Subscribe(x => {
            Debug.Log("Create : " + x);
        });
}
結果

f:id:soramamenatan:20210227134831p:plain


onErrorを発行する

Observable.Throw
/// <summary>
/// onErrorを発行する
/// </summary>
private void ExcuteThrow() {
    string message = "error";
    Observable
        .Throw<Unit>(new System.Exception(message))
        .Subscribe(_ => {
            // 呼ばれない
            Debug.Log("Throw onNext");
        }, err => {
            // 呼ばれる
            Debug.Log("Throw onError : " + err);
        }, () => {
            // 呼ばれない
            Debug.Log("Throw OnCompleted");
        });
}
結果

f:id:soramamenatan:20210227134934p:plain


OnCompletedを発行する

Observable.Empty
/// <summary>
/// OnCompletedを発行する
/// </summary>
private void ExcuteEmpty() {
    Observable
        .Empty<Unit>()
        .Subscribe(_ => {
            // 呼ばれない
            Debug.Log("Empty OnNext");
        }, err => {
            // 呼ばれない
            Debug.Log("Empty onError : " + err);
        }, () => {
            // 呼ばれる
            Debug.Log("Empty OnCompleted");
        });
}
結果

f:id:soramamenatan:20210227135048p:plain


何もしないObservableを定義

Observable.Never
/// <summary>
/// 何もしないObservableを定義
/// </summary>
private void ExcuteNever() {
    Observable
        .Never<Unit>()
        .Subscribe(_ => {
            // 呼ばれない
            Debug.Log("Never OnNext");
        }, err => {
            // 呼ばれない
            Debug.Log("Never OnError : " + err);
        }, () => {
            // 呼ばれない
            Debug.Log("Never OnCompleted");
        });
}
結果

f:id:soramamenatan:20210227135145p:plain


EventをObservableに変換する

引数の数によって、使用方法が異なるので注意してください。

Observable.FromEventの引数1つまで
/// <summary>
/// EventをObservableに変換する
/// 引数1つまではこれで対応可能
/// </summary>
private void ExcuteFromEventSimple() {
    Action<int> fromEventAction = (intValue) => {
        Debug.Log("Called FromEventAction. int :  " + intValue);
    };
    Observable
        .FromEvent<int>(
            handler => fromEventAction += handler,
            handler => fromEventAction -= handler
        )
        .Subscribe(x => {
            Debug.Log("FromEventSimple onNext. int : " + x);
        });
    fromEventAction(1);
}
結果

f:id:soramamenatan:20210227135330p:plain


Observable.FromEventの引数2つ以上, Tuple使用
/// <summary>
/// EventをObservableに変換する
/// Tupleバージョン
/// </summary>
private void ExcuteFromEventTuple() {
    Action<int, string> fromEventAction = (intValue, strValue) => {
        Debug.Log(string.Format("Called FromEventAction. int : {0}, string : {1}", intValue, strValue));
    };
    Observable
        .FromEvent<Action<int, string>, Tuple<int, string>>(
            handler => (x, y) => handler(Tuple.Create(x, y)),
            handler => fromEventAction += handler,
            handler => fromEventAction -= handler
        )
        .Subscribe(x => {
            Debug.Log(string.Format("FromEventTuple onNext. int : {0}, string : {1}", x.Item1, x.Item2));
        });
    fromEventAction(1, "message");
}
結果

f:id:soramamenatan:20210227135449p:plain


Observable.FromEventの引数2つ以上, EventArgs使用
/// <summary>
/// EventをObservableに変換する
/// EventArgsバージョン
/// </summary>
private void ExcuteFromEventEventArgs() {
    Action<int, string> fromEventAction = (intValue, strValue) => {
        Debug.Log(string.Format("Called FromEventAction. int : {0}, string : {1}", intValue, strValue));
    };
    Observable
        .FromEvent<Action<int, string>, FromEventActionClass>(
            handler => (x, y) => handler(new FromEventActionClass() { intValue = x, strValue = y }),
            handler => fromEventAction += handler,
            handler => fromEventAction -= handler
        )
        .Subscribe(x => {
            Debug.Log(string.Format("FromEventEventArgs onNext. int : {0}, string : {1}", x.intValue, x.strValue));
        });
    fromEventAction(1, "message");
}

// ExcuteFromEventEventArgsのClass
public class FromEventActionClass : EventArgs
{
    public int intValue;
    public string strValue;
}
結果

f:id:soramamenatan:20210227135649p:plain


UpdateをObservableに変換する

OnCompletedが自動で発行されないので、寿命の管理に注意してください。
また、UpdateAsObservableの方が良い場面の方もあるので使い分けてください。

Observable.EveryUpdate
/// <summary>
/// UpdateをObservableに変換する
/// </summary>
private void ExcuteEveryUpdate() {
    Observable
        .EveryUpdate()
        .Subscribe(x => {
            Debug.Log("EveryUpdate : " + x);
        });
}
結果

f:id:soramamenatan:20210227140118p:plain


FixedUpdateをObservableに変換する

OnCompletedが自動で発行されないので、寿命の管理に注意してください。
また、FixedUpdateAsObservableの方が良い場面の方もあるので使い分けてください。

Observable.EveryFixedUpdate
/// <summary>
/// FixedUpdateをObservableに変換する
/// </summary>
private void ExcuteEveryFixedUpdate() {
    Observable
        .EveryFixedUpdate()
        .Subscribe(x => {
            Debug.Log("EveryFixedUpdate : " + x);
        });
}
結果

f:id:soramamenatan:20210227140244p:plain


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


参考サイト様

qiita.com

befool.co.jp

www.hanachiru-blog.com