知識0からのUnityShader勉強

知識0からのUnityShader勉強

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

【UniRx】メッセージを変換するオペレータ #99

前回の成果

Observableを分岐させるオペレータについて学んだ。

soramamenatan.hatenablog.com


今回やること

今回はメッセージの変換をするオペレータについてまとめます。


値を変換したい

Select

メッセージで流れてきた値を別の値へと変換することができます。

値を変換する
/// <summary>
/// 値を変換する
/// </summary>
private void ExcuteSelect()
{
    Debug.Log("--------Change Type--------");
    // 型変換
    Observable
        .Range(0, 1)
        // intからstringに変換
        .Select(x => x.ToString())
        .Subscribe(x => {
            Debug.Log("Select onNext : " + x.GetType());
        }, () => {
            Debug.Log("Select onCompleted");
        });
    Debug.Log("--------Change Value--------");
    // 値を変換
    Observable
        .Range(0, 1)
        // 値を変換
        .Select(x => 1000)
        .Subscribe(x => {
            Debug.Log("Select onNext : " + x);
        }, () => {
            Debug.Log("Select onCompleted");
        });
}
結果

intからstringの型変換や、0から1000の値の変換をしています。

f:id:soramamenatan:20210420090246p:plain


型変換をし、失敗時にはOnErrorを発行

Cast

型を変更し、失敗時にはOnErrorを発行しそこでストリームを止めます。
似たオペレータにOfTypeが存在し、そちらはOnErrorを発行しません。

型を変換し、失敗時はonErrorを発行する
public class Enemy { public string Name { get; set; } };
public class Slime : Enemy { };
public class Drakee : Enemy { };

/// <summary>
/// 型を変換し、失敗時はonErrorを発行する
/// </summary>
private void ExcuteCast() {
    Subject<Enemy> subject = new Subject<Enemy>();

    subject
        .Cast<Enemy, Slime>()
        .Subscribe(x => {
            Debug.Log("Cast onNext : " + x.Name);
        }, error => {
            Debug.LogException(error);
        }, () => {
            Debug.Log("Cast onCompleted");
        });

    subject.OnNext(new Slime { Name = "スライム" });
    // cast失敗
    subject.OnNext(new Drakee { Name = "ドラキー" });
    // 通らない
    subject.OnNext(new Slime { Name = "スライムベス" });
    // 通らない
    subject.OnCompleted();
}
結果

スライムはSlime型に変換できるのでOnNextが発行されています。
ですが、ドラキーはDrakee型なので、Slime型に変換できずOnErrorが発行されます。
また、失敗した箇所以下のスライムベスとOnCompletedは通りません。

f:id:soramamenatan:20210420090628p:plain


型変換をし、失敗しても無視する

OfType

型変換をし、失敗しても無視して次の処理へと移ります。
似たオペレータにCastが存在し、そちらは失敗時にOnErrorを発行します。

型変換をし、失敗しても無視する
public class Enemy { public string Name { get; set; } };
public class Slime : Enemy { };
public class Drakee : Enemy { };

/// <summary>
/// 型変換をし、失敗しても無視する
/// </summary>
private void ExcuteOfType() {
    Subject<Enemy> subject = new Subject<Enemy>();

    subject
        .OfType<Enemy, Slime>()
        .Subscribe(x => {
            Debug.Log("OfType onNext : " + x.Name);
        }, error => {
            Debug.LogException(error);
        }, () => {
            Debug.Log("OfType onCompleted");
        });

    subject.OnNext(new Slime { Name = "スライム" });
    // cast失敗
    subject.OnNext(new Drakee { Name = "ドラキー" });
    // 通る
    subject.OnNext(new Slime { Name = "スライムベス" });
    // 通る
    subject.OnCompleted();
}
結果

スライムはSlime型に変換できるのでOnNextが発行されています。 ドラキーはDrakee型なので、Slime型に変換できないので、無視されます。
そして、次のスライムベスとOnCompletedが呼ばれます。

f:id:soramamenatan:20210420091203p:plain


発行されたメッセージの値を別のObservableと合成

SelectMany

Observableと別のObservableを合成させて、新たな結果を発行することができます。

メッセージの値を元に別のObservableと合成する
/// <summary>
/// メッセージの値を元に別のObservableと合成する
/// </summary>
private void ExcuteSelectManyObservable()
{
    Subject<string> subject = new Subject<string>();
    subject
        .SelectMany(x => Observable
                            .Range(0, 3)
                            .Select(y => x + y.ToString()))
        .Subscribe(x => {
            Debug.Log("SelectMany OnNext : " + x);
        } ,() => {
            Debug.Log("SelectMany OnCompleted");
        });

    subject.OnNext("A");
    subject.OnNext("B");
    subject.OnNext("C");
    subject.OnCompleted();
}
結果

SelectManyで指定しているRangeの0~2の値と、OnNextでストリームに流れてくるA~Cの値が合成されています。

f:id:soramamenatan:20210420091633p:plain


ListのListの中身を発行

SelectMany

List<List>のように、Listの中にListがあるものをforeachを使わずに発行することができます。

ListのListの中身
/// <summary>
/// ListのListの中身
/// </summary>
private void ExcuteSelectManyList()
{
    List<string> list1 = new List<string> { "A", "B", "C" };
    List<string> list2 = new List<string> { "D", "E", "F" };
    List<string> list3 = new List<string> { "G", "H", "I" };

    List<List<string>> listToList = new List<List<string>>();
    listToList.Add(list1);
    listToList.Add(list2);
    listToList.Add(list3);

    listToList
        .ToObservable()
        .SelectMany(lists => lists)
        .Subscribe(x => {
            Debug.Log("List Value : " + x);
        }, () => {
            Debug.Log("OnCompleted");
        });
}
結果

各要素が発行されています。

f:id:soramamenatan:20210420091940p:plain


メッセージにOn〇〇を付与

Materialize

OnNext、OnError、OnCompletedをメッセージに付与することができます。

メッセージにonNext,onError,onCompletedの情報を付与
/// <summary>
/// メッセージにonNext,onError,onCompletedの情報を付与
/// </summary>
private void ExcuteMaterialize() {
    // onNextとonComplete
    IObservable<int> stream = Observable.Range(0, 3);
    Observable
        .Materialize(stream)
        .Subscribe(x => {
            Debug.Log("Materialize onNext : <color=blue>" + x + "</color>");
        }, () => {
            Debug.Log("Materialize onComplete");
        });

    // onError
    Observable
        .Throw<Unit>(new Exception("Error Message"))
        .Materialize()
        .Subscribe(x => {
            Debug.Log("Materialize onNext : <color=blue>" + x + "</color>");
        });

}
結果

メッセージにOn〇〇の情報が付与され、発行された値はかっこ内に入っています。

f:id:soramamenatan:20210420092620p:plain


前回のメッセージ発行からの経過時間を表示

TimeInterval

前回のメッセージを発行した時間から、今回メッセージを発行した時間までの経過時間を表示することができます。

前回、値が流れてきたときからの経過時間を表示
/// <summary>
/// 前回、値が流れてきたときからの経過時間を表示
/// </summary>
private void ExcuteTimeInterval()
{
    // キーが押されたらメッセージ発行
    IObservable<Unit> input = this.UpdateAsObservable()
                                    .Where(_ => Input.anyKeyDown);
    // 前回、値が流れてきたときからの経過時間
    Observable
        .TimeInterval(input)
        .Subscribe(x => {
            Debug.Log("TimeInterval : " + x.Interval);
        });
}
結果

前回キーが押された時間から、今回押された時間の経過時間を表示しています。

f:id:soramamenatan:20210421085614p:plain


メッセージにタイムスタンプを付与

TimeStamp

メッセージにタイムスタンプを付与
/// <summary>
/// メッセージにタイムスタンプを付与
/// </summary>
private void ExcuteTimeStamp()
{
    // キーが押されたらメッセージ発行
    IObservable<Unit> input = this.UpdateAsObservable()
                                    .Where(_ => Input.anyKeyDown);
    // タイムスタンプを付与
    Observable
        .Timestamp(input)
        .Subscribe(x => {
            Debug.Log("TimeStamp : " + x.Timestamp);
        });
}
結果

メッセージにタイムスタンプが付与されています。

f:id:soramamenatan:20210421090005p:plain


メッセージをUnit型に変換する

AsUnitObservable

Unit型とは、UniRxで定義されているイベントのタイミングや通知したいものが何も無いときに使用するものになります。
以下のようにSelectを使用することで同じことが出来ます。

Select(_ => Unit.Default)
メッセージをUnit型に変換したい
/// <summary>
/// メッセージをUnit型に変換したい
/// </summary>
private void ExcuteAsUnitObservable()
{
    Observable
        .Range(0, 3)
        .AsUnitObservable()
        .Subscribe(x => {
            Debug.Log("AsUnitObservable onNext : " + x);
        }, () => {
            Debug.Log("AsUnitObservable onCompleted");
        });
}
結果

Rangeで0~2の値を流そうとしていますが、Unit型になっています。

f:id:soramamenatan:20210421090353p:plain


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


参考サイト様

qiita.com

blog.csdn.net

nobollel-tech.hatenablog.com