読者です 読者をやめる 読者になる 読者になる

Enjoy UniRx

UniRxクックブックてきな

今までに書いてきたよくあるRxのパターンを書いてく

  • 連打対策.
class HogeController : Monobehaviour{
    [SerializeField]
    Button btn;
    
    void do(){
        Debug.Log("てすてすてす");
    }   

    void Awake(){
        btn.OnClick.AsObservable()
            .ThrottleFirst(TimeSpan.FromSeconds(1))
            .Subscribe(_ => do()).AddTo(this);
    }
}

class RootHoge: MonoBehaviour{
    [SerializeField]
    ChildHoge[] childrenComponent;
    
    void Awake(){
        childrenComponent.Select(child => child.OnClicked).Merge().Subscribe(_ => {
            // いずれかの子供のClickStreamが発行された
        }).AddTo(this);
    }
}

class ChildHoge : MonoBehaviour{

    [SerializeField]
    UIButton btn;

    public IObservable<Unit> OnClicked{
        get; private set;
    }
    
    void Awake(){
        OnClicked = btn.OnClick.AsObservable();
    }
}
  • スタミナの回復時間などで前後時間を比較した計算する

例えばゲームのスタミナの回復時間を表示する箇所。 次の最大回復時間をサーバーから受け取って、現在時刻と比較するのが一般的だと思う。 ちょっと拡張して、次の回復までの時間、最大までの時間、現在の時間をRxで計算してみた

DateTime GetCurrentTime(){
    // なんか
    return DateTime.Now;
}

// 初期化済
ReadOnlyReactiveProperty<int> TimeToMax;
ReactiveProperty<int> timeToMax;
ReactiveProperty<int> present;
ReadOnlyReactiveProperty<int> Present;
ReactiveProperty<int> intervalTime;
ReadOnlyReactiveProperty<int> IntervalTime;
ReactiveProperty<int> maxPoint;
ReadOnlyReactiveProperty NextPointRecoverTime;

// 通信するたびに最大回復までの時刻が更新される
void Init(int timeToMax, int presentTime, int intervalTime){

//最大までの時間(秒)
TimeToMax = timeToMax.Select(ttm => 
    Observable.Timer(TimeSpan.FromSeconds(0),TimeSpan.FromSeconds(1))
        .Select(_ => GetCurrentTime()).Buffer(2, 1)
        .Select(buf => (buf[1] - buf[0]).TotalSeconds)
        .Scan((double)0, (prev, current) => prev + current)
        .DistinctUntilChanged()
        .StartWith(0)
        .Select(t => Mathf.RoundToInt(t)
    ).Switch()
    .ToReadonlyReactiveProperty();
    
    
    // 現在のスタミナ値
Present = present
            .CombineLatest(TimeToMax.Select(sec => Mathf.FloorToInt((TimeToMax.Select(sec => Mathf.FloorToInt(MaxPoint.Value - (float)sec / (float)Interval.Value)), (present, max) => Mathf.Max(p1, p2))
            .DistinctUntilChanged()
                .ToReadOnlyReactiveProperty();
                
                
            NextPointRecoverTime = this.TimeToMax.Select(sec => IntervalTime.Value == 0 ? 0 : sec % Interval.Value).ToReadOnlyReactiveProperty();
            
    
            this.maxPoint.Value = maxPoint;
            this.interval.Value = interval;
            this.timeToMax.Value = timeToMax;

}
  • ストリームをmergeするけど、Streamごとに違う処理をはさみたい.

yes おされたときとno押された時に違う処理をした後に共通処理したい。(FinallyはOnError時にも引っかかってしまうのでそれでは困るケース)

HogePopup popup;
popup.OnYesClicked.SelectMany(_ => API.PostYesData().Select(_ => UniRx.Unit))

.Merge(popup.OnNoClicked.SelectMany(_ => API.PostNo().Select(_ => UniRx.Unit))
.Subscribe(_ => {// 共通処理})
.AddTo(this);

  • Switchする

たとえば、自動ページングするリストビューなど子供の要素数が変化するもの。 子供要素が変化が起こるたびにクリックストリームを更新したい

ReactiveCollection<HogeComponent> collection;

// 子供要素が変化するたびにきちんとcollectionを同期する処理がどっかに実装されてる

void Awake(){
    collection = new RactiveCollection<HogeComponent>();
    // hotにしたいかな
    var stream = collection.OnCountChanged().Select(_ => collection.Value.OnClicked).Switch().Publish();
    
    stream.Connect();
    
    stream.Subscribe(_ => {
        // なんか
    });
}