複数の ReactiveProperty をまとめて監視する

UniRx を使っていて、複数の ReactiveProperty をまとめて監視して何か処理を行いたいときがたまにあります。

例えば、「デバッグモードが ON」かつ「ミュート設定が ON」になったときのみ音声をミュートする、とか。

ベタ書き

デバッグモードとミュート設定をそれぞれ BoolReactiveProperty で持ち、どちらかの値が変わったらミュート状態を切り替える、という実装を考えます。

とりあえず、何も考えずそのままベタ書きしたのが以下のコードです。

public BoolReactiveProperty debugMode;
public BoolReactiveProperty muteAllSounds;

public AudioSource audioSource;

void Start()
{
    debugMode.Subscribe(x => ToggleMute());
    muteAllSounds.Subscribe(x => ToggleMute());
}

void ToggleMute()
{
    audioSource = debugMode.Value && muteAllSounds.Value;
}

そのままだ。そのままですが、だいぶ頭の悪いコードですね……。

せっかく ReactiveProperty を使っているのに、流れてきた値 (x) を使っていないあたりに頭の悪さが滲み出ています。

ストリームを合成する

「ストリームは合成できる」ということを知っていれば、先程のコードは以下のように書き換えることができます。

public BoolReactiveProperty debugMode;
public BoolReactiveProperty muteAllSounds;

public AudioSource audioSource;

void Start()
{
    Observable.Merge(debugMode, muteAllSounds)
        .Subscribe(x => ToggleMute());
}

void ToggleMute()
{
    audioSource = debugMode.Value && muteAllSounds.Value;
}

10 点だったのが、20 点くらいにはなったかな……。

しかしこのやり方だと、流れてくるxの値がdebugModeのものなのかmuteAllSoundsのものなのか判らないので、やっぱり使えないままです。

両方の値が流れてくるようにする

ストリームを Merge で合成すると、debugModeの値が変わった時にはdebugModeの値のみが、muteAllSoundsの値が変わった時にはmuteAllSoundsの値のみが流れて来るので、良くなかった、と。

なら、debugModeの値が変わろうとmuteAllSoundsの値が変わろうと、両方の値が流れてくるようになれば良いわけです。

そのようにしたのがこちら。

public BoolReactiveProperty debugMode;
public BoolReactiveProperty muteAllSounds;

public AudioSource audioSource;

void Start()
{
    Observable.CombineLatest(debugMode, muteAllSounds)
        .Subscribe(list =>
        {
            audioSource = list.All(x => x);
        });
}

だいぶ賢いコードになりましたね!

ストリームを CombineLatest で合成すると、どれかの値が変わったら全部の値が List にまとめられて流れてくるので、「list 内の全要素が true だったら*1ミュート」とすれば、やりたいことが実現できるというわけ。

補足

先程、CombineLatest の説明で「どれかの値が変わったら全部の値が List にまとめられて流れてくる」と書きましたが、これは微妙に違くて、すべてのストリームに一回以上値が流れていないと List は流れてきません。

それなのになぜ上記のコードが valid なのかというと、ReactiveProperty は値が変化した時だけでなく Subscribe した瞬間にもその時点での値が一回流れるので、自動的に「一回以上値が流れている」が満たされるからですね。

参考:

neue cc - Unityにおけるコルーチンの省メモリと高速化について、或いはUniRx 5.3.0でのその反映

(UniRxの)ReactivePropertyはSubscribe時に必ず値をプッシュするようになってる

*1:list.All(x => x) の部分。

The coloring of this site is Dracula PRO🧛🏻‍♂️
This website uses the FontAwesome icons licensed under CC BY 4.0.

2020 GIGA CREATION