【Unity】【UniRx】ActionやFuncからObservableを生成する

ActionやFuncからObservableを作る方法です。

Observable.ToAsync

Observable.ToAsync()を使うとOnNext()が一回だけ呼ばれてそれと同時にOnCompleted()が呼ばれるようなObservableを簡単に作れます。

Observable.ToAsync(() => {
    // 1秒遅延させる
    Thread.Sleep(1000);
    // 今の時間を流す
    // OnCompletedもここで呼ばれる
    return System.DateTime.Now;
})
.Invoke()
.Subscribe(x => Debug.Log(x));

ToAsync()はIObservableを返すFuncを返してくるので、Invoke()することでIObservableを得られます。

Observable.ToAsync(() => {
    Thread.Sleep(1000);
    return System.DateTime.Now;
})
.Invoke(); // SubscribeしなくてもInvokeすれば処理が開始される

Invoke()の時点でストリームが生成されるため、下記の2パターンで挙動が変わるので注意が必要です。

// 同じObservableを購読するのでThread.Sleep()は1回だけ行われる
var observable = Observable.ToAsync(() => {
    Thread.Sleep(1000);
    return System.DateTime.Now;
})
.Invoke();
observable.Subscribe(x => Debug.Log(x));
observable.Subscribe(x => Debug.Log(x));

// Observableが2つ作られてそれぞれを購読するのでThread.Sleep()は2回行われる
var observableFunc = Observable.ToAsync(() => {
    Thread.Sleep(1000);
    return System.DateTime.Now;
});
observableFunc.Invoke().Subscribe(x => Debug.Log(x));
observableFunc.Invoke().Subscribe(x => Debug.Log(x));

Observable.Start

Observable.Start()で生成したObservableをSubscribe()すると、
Observable.ToAsync()のあとすぐInvoke()したのと同じ効果が得られます。

var observable = Observable.Start(() => {
    Debug.Log("debug");
    Thread.Sleep(1000);
    return System.DateTime.Now;
});
// ストリームは2個生成される
observable.Subscribe(x => Debug.Log(x));
observable.Subscribe(x => Debug.Log(x));

// 上と下は同等の処理

var observableFunc = Observable.ToAsync(() => {
    Debug.Log("debug2");
    Thread.Sleep(1000);
    return System.DateTime.Now;
});
// ストリームは2個生成される
observableFunc.Invoke().Subscribe(x => Debug.Log(x));
observableFunc.Invoke().Subscribe(x => Debug.Log(x));

Observable.FromEvent

Observable.FromEventはいろんなActionやFuncを柔軟にObservable化できます。

using UniRx;
using UnityEngine;

public class UniRxExample : MonoBehaviour
{
    private event System.Action<float> _onTakeDamage;

    private void Start()
    {
        Observable.FromEvent<float>
        (
            h => _onTakeDamage += h, 
            h => _onTakeDamage -= h
        );
    }
}