バッテラのハローワールド研究室

エンジニア、プログラミングに関する情報を掲載中!

【Unity】UniRXのFactoryメソッドの基本的な使い方

Unity

はじめに

今日はUnityの無料アセットであるUniRXについて勉強していく記事です。

FactoryはObservableを作るタイプのやつですね。

色んなFactoryメソッドがあるので簡単な使い方を解説していこうと思います。

Return

構文

IObservable<int> ObserveReturnTest()
    {
        return Observable.Return(1);
    }
使い所
  • モックアプリを作るときにとりあえず値を返す
特徴

Subscribeした瞬間にOnNextとOnCompleteが呼ばれる

Create

構文

public IObservable<int> Hoge()
{
    return Observable.Create<int>(observer =>
    {
        int num = 1;

        observer.OnNext(num);

        observer.OnCompleted();

        return Disposable.Empty;
    });
}
使いどころ

独自の非同期処理を定義できる

特徴
  • DisposableをCreate内で返す必要がある
  • OnNextとOnCompleteを明示的に呼び出す必要あり
  • 暗黙的キャストできないメソッドであるための部分を2回書く必要があるのに注意
注意事項

・即通知問題
Observale.Returnと同様で非同期処理待ちをせずにOnNextを呼び出し他場合は、その行で即通知されるためシーケンス順によるnull参照に注意すること

NextFrame

構文

Observable.NextFrame(FrameCountType.EndOfFrame)
    .Subscribe(_ =>
    {

    });
使い所
  • 正直わからない。。次のフレームじゃないといけない事があるのかしら。
特徴

・次フレームに行う処理を定義できる ・次フレームにOnNextとOnCompleteが発行される

注意事項

・FrameCountTypeによる精度違いに注意する

FrameCountType 精度
Update OnNextがくるまでにMonoBehaviorのUpdateが1〜3回ランダムで呼ばれている印象
FixedUpdate
EndOfFrame 次のUpdateまでに

EndOfFrame一択でいい気がするがどうなのだろうか。

EveryUpdate

構文

Observable.EveryUpdate()
    .Subscribe(_ =>
    {
           // 毎フレームよばれる
    });
使い所
  • 毎フレーム値を監視したいとき
特性
  • OnNextのみ呼ばれる(OnCompleteは呼ばれない)
注意事項

・自身で寿命管理処理を実装する必要がある
OnCompletedが自動で呼ばれないため、独自にDispose()

Timer

■構文

// 引数1個
Observable.Timer(TimeSpan.FromSeconds(60.0))
    .Subscribe(_ =>
    {

    });
// 引数2個
Observable.Timer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0f))
    .Subscribe(_ =>
    {

    });

■引数による全く異なる挙動の違いに注意 ・引数1個の挙動 1回きりのタイミング処理するため(OneShot) 引数1に時間をOnNextを呼ぶ時間を設定する 引数1時間経過後OnNextとOnCompleteが必ず呼ばれる

・引数2個の挙動 定期実行で処理するため(LoopShot) 引数1に定期実行までのディレイ時間を入力、 引数1時間経過後に1回目のOnNextがよばれる 引数2に定期時間を入力 引数2の時間定期後にN回目のOnNextがよばれる

OnCompletedは呼ばれない

■ポイント ・Take等の終了判定Operationは無効化される ・引数2個のTimerは、 「Interval」と役割重複している → 可読性が下がるので一定間隔処理は「Interval」に統一すべき

■精度に問題がある 秒以上での指定であれば問題ないが、ミリ秒で指定すると

毎回誤差が発生し合計値でロジックを組むとその秒数を超えている。

おそらくフレームごとに時間経過を監視して超えていたらOnNextするのだが、

超過分をキャリオーバーしていないので、ミリ秒でのずれが毎回発生する

TimerFrame

■構文

// 引数1個
Observable.TimerFrame(5, FrameCountType.EndOfFrame)
    .Subscribe(_ =>
    {

    });
// 引数2個
Observable.TimerFrame(1, 10)
    .Subscribe(_ =>
    {

    });

■ポイント 「Timer」のフレーム版 精度はTimerより高い(Timerがミリ秒以上なら同じ)

FromCoroutine

■概要 ・Unity独自システムのコルーチンをObservableに変換しSubscribeできるようにする ・ライブラリ依存でコルーチンを使わざるを得ない状況で活用する

■構文

// Coroutineを定義する
private IEnumerator TestCoroutine()
{
    // 1秒止めるというテストコード
    yield return new WaitForSeconds(1.0f);
}

// 引数にコルーチンメソッドを指定する
Observable.FromCoroutine(TestCoroutine)
    .Subscribe(_ =>
    {
        Debug.Log("OnNext");
    },
    () =>
    {
        Debug.Log("OnCompleted");
    }
    ).AddTo(this);

// 1秒後に OnNextとOnCompltedが呼ばれる

ObservableWWW

Unity2018.3以降で非推奨となってしまった。 今後使用しないこと!

ObserveEveryValueChanged

■概要 ・全てのクラスに拡張メソッドとして用意されているもの ・クラスのメンバ変数の変更を検知して通知する ・クラス全体の監視ではなくクラスの一部だけを監視するもの

■構文

// 監視対象の適当なクラス
private class Hoge
{
    public int a = 0;
    public float b = 0;
    public string c = "";
}

// xは自身のクラスを指す
this.hoge.ObserveEveryValueChanged(x => x.a)
.Subscribe(val =>
    {
        // aの変数が変更されたら通知がくる
        Debug.Log($"a changed!! val={val}");

    }).AddTo(this);

this.hoge.ObserveEveryValueChanged(x => x.b)
    .Subscribe(val =>
    {
        Debug.Log($"b changed!! val={val}");

    }).AddTo(this);

this.hoge.ObserveEveryValueChanged(x => x.c)
    .Subscribe(val =>
    {
        Debug.Log($"c changed!! val={val}");

    }).AddTo(this);

■変数の中身が変わっても通知されないケースがある ・クラス変数はが中身が変更されても通知されない ・LIst等のコレクションが変更(追加、削除)されても通知されない