Re: note

技術的な知見やポエムなど役に立たない情報を書き連ねる場所

UniRxを使ってみる

f:id:hik0leaf:20190608155416p:plain


ここではUniRxの基本的なオペレータとそのサンプルコードを見ていきたいと思います。 概念を理解するのも大切ですが実際に使って動かしてみるのも理解への近道です。

全体のコードは以下のリポジトリにありますので、別記事の UnityでLINQを使って楽をする も合わせてご覧ください。

github.com

また、本記事は全体的に以下のサイトの記事(RxJava)を参考にさせて頂きました。

qiita.com

What's UniRx?

f:id:hik0leaf:20190608164039j:plain
Everything is a stream

http://slides.com/robwormald/everything-is-a-stream#/

特徴

  • ReactiveXのUnity版
  • マーブルダイアグラム
  • Observable / Subscribe の世界
  • 豊富なオペレータ
  • 非同期並列処理
  • Asset StoreからUniRxをImportして using UniRx で使えるようになるよ

Timer

指定時間後に処理を行います。

f:id:hik0leaf:20190608164747p:plain

using UniRx;
using System;

Debug.Log("Timer START");
Observable.Timer(TimeSpan.FromSeconds(3))
    .Subscribe(_ => Debug.Log("OK")); // 3秒後に"OK"

Interval

指定時間間隔で繰り返します。

f:id:hik0leaf:20190608165058p:plain

using UniRx;
using System;

Observable
    .Interval(TimeSpan.FromSeconds(1))
    .Subscribe(x => Debug.Log(x));
// 0, 1, 2, 3, 4, 5......

Where

条件式がTrueとなるものを流します。

f:id:hik0leaf:20190608165140p:plain

using UniRx;

var list = new List<int> { 1, 2, 3, 4, 5 };

list.ToObservable()
    .Where(x => x > 2)
    .Subscribe(x => Debug.Log(x));
// 3, 4, 5

Distinct

重複を削除します。

f:id:hik0leaf:20190608165210p:plain

using UniRx;

var list = new List<int> { 1, 2, 3, 3, 4, 5, 5 };

list.ToObservable()
    .Distinct()
    .Subscribe(x => Debug.Log(x));
// 1, 2, 3, 4, 5

Take

指定回数だけ流します。

f:id:hik0leaf:20190608165231p:plain

using UniRx;

var list = new List<int> { 1, 2, 3, 4, 5 };

list.ToObservable()
    .Take(3)
    .Subscribe(x => Debug.Log(x));
// 1, 2, 3

Skip

指定回数だけスキップします。

f:id:hik0leaf:20190608165454p:plain

using UniRx;

var list = new List<int> { 1, 2, 3, 4, 5 };

list.ToObservable()
    .Skip(3)
    .Subscribe(x => Debug.Log(x));
// 4, 5

Scan

畳み込みを行います。

f:id:hik0leaf:20190608165521p:plain

using UniRx;

var list = new List<int> { 1, 2, 3, 4, 5 };

list.ToObservable()
    .Scan((a, b) => a + b)
    .Subscribe(x => Debug.Log(x));
// 1, 3, 6, 10, 15

Range

範囲を指定して流します。

f:id:hik0leaf:20190608165540p:plain

using UniRx;

Observable
    .Range(1, 5)
    .Subscribe(x => Debug.Log(x));
// 1, 2, 3, 4, 5

Create

Observableを作成します。

using UniRx;

var observable = Observable.Create<int>(observer => {
    observer.OnNext(1);
    observer.OnNext(2);
    observer.OnNext(3);
    observer.OnNext(4);
    observer.OnNext(5);
    observer.OnCompleted();
    return Disposable.Empty;
});

observable.Subscribe(x => Debug.Log(x));
// 1, 2, 3, 4, 5

ボタンクリックイベント

OnClickイベントをObservable化します。

using UniRx;
using UnityEngine.UI;

[SerializeField] Button button = default;

button
    .OnClickAsObservable()
    .Subscribe(_ => Debug.Log("on click"));

SubscribeOn

Observerを処理するスレッドを指定します。

using UniRx;
using System.Threading;

Debug.Log("START");
var observable = Observable.Create<int>(observer => {
    Thread.Sleep(3000);
    observer.OnNext(1);
    observer.OnCompleted();
    return Disposable.Empty;
}).SubscribeOn(Scheduler.ThreadPool);

observable.Subscribe(x => Debug.Log(x));
Debug.Log("END");
// START
// END
// 1 (3秒後)

Merge

Observableを合成します。順不同 f:id:hik0leaf:20190608165631p:plain

using UniRx;
using System.Threading;

var list1 = Observable.Create<string>(observer => {
    Thread.Sleep(1);
    observer.OnNext("A1");
    Thread.Sleep(8);
    observer.OnNext("A2");
    Thread.Sleep(2);
    observer.OnNext("A3");
    observer.OnCompleted();
    return Disposable.Empty;
}).SubscribeOn(Scheduler.ThreadPool);

var list2 = Observable.Create<string>(observer => {
    Thread.Sleep(3);
    observer.OnNext("B1");
    Thread.Sleep(1);
    observer.OnNext("B2");
    Thread.Sleep(12);
    observer.OnNext("B3");
    observer.OnCompleted();
    return Disposable.Empty;
}).SubscribeOn(Scheduler.ThreadPool);

Observable
    .Merge(list1, list2)
    .Subscribe(x => Debug.Log(x));
// A1, B1, B2, A2, A3, B3

Concat

Observableを合成します。流れる順序は引数受け取り順です。

f:id:hik0leaf:20190608165709p:plain

using UniRx;
using System.Threading;

var list1 = Observable.Create<string>(observer => {
    Thread.Sleep(1);
    observer.OnNext("A1");
    Thread.Sleep(8);
    observer.OnNext("A2");
    Thread.Sleep(2);
    observer.OnNext("A3");
    observer.OnCompleted();
    return Disposable.Empty;
}).SubscribeOn(Scheduler.ThreadPool);

var list2 = Observable.Create<string>(observer => {
    Thread.Sleep(3);
    observer.OnNext("B1");
    Thread.Sleep(1);
    observer.OnNext("B2");
    Thread.Sleep(12);
    observer.OnNext("B3");
    observer.OnCompleted();
    return Disposable.Empty;
}).SubscribeOn(Scheduler.ThreadPool);

Observable
    .Concat(list1, list2)
    .Subscribe(x => Debug.Log(x));
// A1, A2, A3, B1, B2, B3

Zip

MergeやConcatとの違いは異なる型のObservableを扱えます。

f:id:hik0leaf:20190608165743p:plain

using UniRx;

var list1 = new List<string> { "A", "B", "C", "D", "E" }.ToObservable();
var list2 = new List<int> { 1, 2, 3, 4, 5 }.ToObservable();

Observable
    .Zip(list1, list2, (a, b) => a + b)
    .Subscribe(x => Debug.Log(x));
// A1, B2, C3, D4, E5

Subject

外部から任意のタイミングでOnNext、OnComplete、OnErrorを送ることが可能です。

f:id:hik0leaf:20190608165804p:plain

using UniRx;

var subject = new Subject<int>();
subject.Subscribe(x => Debug.Log(x));

subject.OnNext(1);
subject.OnNext(2);
subject.OnNext(3);
// 1, 2, 3

BehaviorSubject

Subjectとの違いは初期値の有無です。

f:id:hik0leaf:20190608165825p:plain

using UniRx;

var subject = new BehaviorSubject<int>(5);
subject.Subscribe(x => Debug.Log(x));

subject.OnNext(1);
subject.OnNext(2);
subject.OnNext(3);
// 5, 1, 2, 3

終わりに

オペレータはこの他にもたくさんありますので、使い方などの詳しい情報はUniRxのリポジトリwikiをみると良いでしょう。それでは良いUniRxライフを。

github.com