Qemelly(けめる)のプログラム備忘録

Unity / AtCoderについて書きます

UniRxのDistinctUntilChangedとWhereの順序には要注意!!【しょうもないミス2】

実際に開発中にハマった罠について(例は変えています)。

例えばジャンプした瞬間にイベントを発行したくなったとしましょう。以下のうちどちらが正しいでしょうか?

DistinctUntilChangedとWhereの順序が変わっています。

private readonly ReactiveProperty<bool> _isJumping = new();

private void Start()
{
    // A
    _isJumping
        .Where(x => x)
        .DistinctUntilChanged()
        .Subscribe(_ => Debug.Log("ジャンプ中!"))

    // B
    _isJumping
        .DistinctUntilChanged()
        .Where(x => x)
        .Subscribe(_ => Debug.Log("ジャンプ中!"))
}

試しに適当なストリームを想定してみます。
0をfalse、1をtrueとして、010010111というストリームがあったとしましょう。

このときの出力の想定は111となります(実際には3回ジャンプしているため)。

// A
 _isJumping
    .Where(x => x)
    // 010010111 -> 11111
    .DistinctUntilChanged()
    // 11111 -> 1

// B
 _isJumping
    .DistinctUntilChanged()
    // 010010111 -> 010101
    .Where(x => x)
    // 010101 -> 111

例を見れば一目瞭然ですが、答えはBとなります。Aで実装すると1回だけ正しく動きますが、以降は無反応になります...。

というわけでこういった場合ではDistinctUntilChanged()→Where()の順で書こうねという話でした。1回だけ発行したい場合はこの限りではありませんが、First()とかを使った方がいいと思います。

ただ、この例だとそもそも_isJumpingをプロパティにして同じ値がそもそも入らないようにしてもいいかもしれません。あとStateパターンを使ったりするのも1つの手でしょう。