UniRxについて学んでいきます。
この2つの資料および先輩に聞きながら書いています~
ごちゃごちゃ。自分用メモです。信用しないで
- 概要
- 概要+コード
- 書き方コピペ用
- ストリーム
- Subject
- 観察者・発行・メッセージ出す側
- 購読
- オペレーター
- uGUI
- SubscribeしたらUpdate
- Updateを止めるには
- ReactiveProperty
- N秒ごとにメソッドを実行する
- 結局いつ使うのか
- 最後に
概要
Subject 主題
Observable 観察可能な(subject)
Observer 観察者・・・ObservableなSubjectの変化などを観察してメッセージを発する
Subscribe 購読・・・観察者が発したメッセージを購読(受け取る)する。
観察したいものを(subject)を定義する。
Observerがそのsubjectの変化や終わりを観察してメッセージを発する。
あらかじめSubjectをSubscribe(購読)したひとは、なにかあったらメッセージを受け取る。
受け取った後の処理も書いておく。
SubjectのObserver(観察者)が観察するのは変化だけじゃなくて観察した処理がおわったときなどもあるけど、ここでは変化とだけ書く。
概要+コード
・定義(Subject
観察したいものを(subject)を定義する。
public Subject<int> m_OnValueChange = new Subject<int>();
intのところは送りたいデータの型
・発行
メッセージを発したい場所、送るタイミングで名前.OnNext()などを書く
m_OnValueChange.OnNext(xxxx);
・購読(Subscribe
subjectの購読+受信時のふるまいを別のスクリプトのstart()などに書く
SendScriptA.m_OnValueChange.Subscribe( x => Hoge()).AddTo(this);
受け取ったらHoge()を呼ぶなど。
実行してOnNextが呼ばれると、Subscribeが呼ばれて、その中のHoge()が動く。
書き方コピペ用
<<送る側(発行)スクリプト>>
SendScriptA.cs
<<購読スクリプト>>
ObserverScript.cs
ストリーム
一連の流れのこと
メッセージを発行
メッセージを流す
subscribeする
処理する
Subject
SubjectはIObserverとIObservableの2つの機能を継承したクラス。
IObserverはメッセージを流したりするほう。OnNextとかができる。
IObservableは購読。Subscribeができる
subjectそのものはpublicにしないで、Subjectの2つある機能のうちのIObservableだけをpublicにする。
すると購読する側のスクリプトではSubscribeしかできない。OnNextとかの
上の行をpublicにするだけでも送ることはできるが、OnNextとかのメッセージ発信機能も使えてしまって設計上よくない。
上の<int>の部分は送るものの型。何も送らない場合はUnit
複数送りたいとき
同時にfloatを複数送りたいときSubject<float,float>ってやると怒られた。
考えられる修正で出てきたISubject<float,float>にしたらそこのエラーは消えた。
でも違うみたい。
https://qiita.com/toRisouP/items/2f1643e344c741dd94f8
できないからクラス型を自分で作ればいいそう。
観察者・発行・メッセージ出す側
メッセージ
OnNext・・・メッセージの変化通知
OnError・・・ストリームの停止andエラー通知
OnCompleted・・・ストリームの停止and完了通知
OnError、OnCompletedを受け取ったら、それ以降メッセージを受け取らない。
カッコの中身
メッセージには数字とか゚変数とか゚文字とか関数とか゚を添えることができる。
subject.OnNext("Enemy");
何も添えずに、自分が呼ばれたことだけを知らせたい場合は
m_OnTabletActive.OnNext(Unit.Default);
そのほかは、宣言するときに指定した型
m_OnTabletActive.OnNext(型.中身);
購読
subjectの購読+受信時のふるまいを別のスクリプトのstart()などに書く
SendScriptA.m_OnValueChange.Subscribe( x => Hoge()).AddTo(this);
受け取ったらHoge()を呼ぶ。
=>の左側(引数)
xの部分は何の文字でもいい。OnNextで送ったものがxに入ってHoge()の引数に渡される。
Unit.Defaultのように何も送らなくてもいいけどxは必要。中身は空
逆に複数もらう場合は
Subscribe( (a,b) => Hoge())
のようにかっこで囲む。
=>の右側(処理)
書こうと思えば処理はいっぱいかける。
長くなる時はメソッドにしたほうがいいよ。
2行以上になるときは
Subscribe( (a,b) => { ~~~~~~
~~~~})
のように中カッコ
OnError、OnCompletedなどを発するときは
ラムダ式
このSubscribe()の中身はラムダ式という書き方をしているらしい。
メソッドにせず、引数を与え処理を書く場合の書き方のこと
オペレーター
購読時に受け取り条件を付けたり、受け取ったものを料理したりできる。
m_ReactiveProperty.OnValueChanged
.SkipLatestValueOnSubscribe()
.Where(x => x % 2 == 0)
.Select(x => x * x)
.Subscribe*1;
そのほかの使い方はこちらの記事にまとまっている
uGUI
もとからボタン押下などの発行イベントが用意されているので、購読側だけ書けばいい。
m_Button.onClick.AsObservable().Subscribe( ( _ ) => Hoge());
値の変化 ?
InputField.OnValueChangedAsObservable.Subscribe
SubscribeしたらUpdate
Updateを止めるには
onCompletedを使えないとき。ボタンからの発信なので自分でonCompletedの前の部分を定義できない。。。
disposableを使う。
var disposable = new SingleAssignmentDisposable();
disposable.Disposable = this.UpdateAsObservable().Subscribe(x => {
if (止まる判定) {
disposable.Dispose();
}
}
ReactiveProperty
変数を渡せるSubject
購読側は変わらない
UIの値の変更通知に使うことが多い?
N秒ごとにメソッドを実行する
Observable.Interval(TimeSpan.FromSeconds(STATE_UPDATE_INTERVAL)).Subscribe(_ => UpdateStatus());
FromSeconds()の中はTimeSpanという型。
秒にするには TimeSpan.FromSeconds(10)
結局いつ使うのか
UniRXは結局いつ使うのか。
いままでUpdate()の中で、「もしAがactiveになったらXXする。」みたいに監視する処理を書いてた。でもUpdate()でずっと監視しているのは処理しすぎでよくない。
UniRxでイベントを発信させてその時だけ受け取るようにする。
あとeventに例えられているけど、Eventを使ったことないから分からない。
最後に
完全に理解した
Rxが難しいのはRxがオブザーバーパターンの実装であると同時に関数の実装でもあるからで、関数はOOPから変数の概念を削除したものだから。
— su10@ハイパーカジュアルゲーム開発 (@su10_dev) June 22, 2019
OOPに慣れてる人は変数なしでコーディングしろっていきなり言われても無理なんだよな。関数の作法を勉強しないといけない。
*1:x) => Debug.Log(x