MRが楽しい

MRやVRについて学習したことを書き残す

UniRxでPublishを使ってColdの性質のObservableをHotの性質のObservableに変換する

本日はUniRxの小ネタ枠です。
UniRxでPublishを使ってColdの性質のObservableをHotの性質のObservableに変換してみます。

Hot/Cold

UniRxにおけるHotとColdのObservableの違いについては以下の記事を参照ください。
bluebirdofoz.hatenablog.com

Publishオペレータ

Publishオペレータを使うとColdのObservableをHotのObservableに変換することができます。
ただしPublishを行うだけでは通知は流れてこず、Publishの後にConnectオペレータを利用して初めてColdのObservableから通知が流れる点に注意が必要です。

// ColdのObservable
var rangeObservable = Observable.Range(1, 2);

// PublishでHotのObservableの性質を持つObservableに変換されるが
// この時点でColdのObservableから通知は流れてこない
var rangeHotObservable = rangeObservable.Publish();

// Connectを行った時点でColdのObservableから通知が発生する
rangeHotObservable.Connect();

サンプルスクリプト

以下のサンプルスクリプトでColdのObservableを直接購読する場合と、PublishオペレータでHotのObservableに変換したものを購読する場合の動作を比較します。
・PublishTest.cs

using UniRx;
using UnityEngine;

public class PublishTest : MonoBehaviour
{
    [ContextMenu("ColdObservableTest")]
    public void ColdObservableTest()
    {
        // RangeオペレータはColdのObservableになる
        // Coldのため、この時点では 1, 2, 3 の通知が発行されない
        var rangeObservable = Observable.Range(1, 2);
        
        Debug.Log("Subscribe");
        // ColdのObservableは購読ごとに別々のストリームが生成されて購読と同時に通知が発生する
        rangeObservable.Subscribe(x => Debug.Log($"Subscribe1: {x}"));
        rangeObservable.Subscribe(x => Debug.Log($"Subscribe2: {x}"));
    }
    
    [ContextMenu("PublishObservableTest")]
    public void PublishObservableTest()
    {
        // RangeオペレータはColdのObservableになる
        // Coldのため、この時点では 1, 2, 3 の通知が発行されない
        var rangeObservable = Observable.Range(1, 2);
        
        // PublishでHotのObservableを準備する
        // この時点ではColdのObservableから通知は流れてこない
        var rangeHotObservable = rangeObservable.Publish();
        
        Debug.Log("Subscribe");
        // HotのObservableは全ての購読に対して同一のストリームで通知が発生する
        rangeHotObservable.Subscribe(x => Debug.Log($"Subscribe1: {x}"));
        rangeHotObservable.Subscribe(x => Debug.Log($"Subscribe2: {x}"));

        // Connectを行った時点でColdのObservableから通知が発生する
        rangeHotObservable.Connect();
    }
}

ColdのObservableを直接購読する場合

ColdのObservableを直接購読する場合では購読のタイミングで個別のストリームが作成されて全ての通知を受け取ります。
このため、それぞれの購読で 1 ~ 2 の通知を受け取ってログ出力が行われます。

PublishオペレータでHotのObservableに変換したものを購読する場合

HotのObservableに変換した場合、全ての購読が同じストリームを参照して通知を受け取ります。
このため、Connectオペレータ実行後、1 と 2 の通知ごとに購読によるログ出力が行われる形になります。