UniRxを勉強しよう2

全然関係ないんだけど、今日届いたこのイヤホンがめちゃくちゃいいので音楽聞きながら作業するタイプの人はぜひ買ってほしい。
AIRaftershokz.jp
耳の穴を塞がない骨伝導方式かつワイヤレスなので着けててもうっとうしくないし、適度に環境音が入ってきて集中しやすい。また、僕は趣味でベースを弾くのだが、これを使うと原曲を聞きながら自分の楽器の音も聞こえるのでめちゃくちゃ捗る。
これはたぶん本来の使い方ではないが、耳の穴に直接別のイヤホンをぶっさせばスプリッターなしで二つの音声を聞くことができる。最高。マジ最高。

続きから行くぞ!!

さて、昨日の続きからいきます。

昨日は書いた(コピペした)コードの一行目の解説すらできていなかったけど、おかげでUniRxについての理解が進んだ気がする。引き続き昨日のコードを掘り下げていこう。

void Awake()
{
    button.onClick.AsObservable()
        .Select(_ => 1)
        .Scan(0, (element, acc) => element + acc)
        .Subscribe(count => text.text = count.ToString())
        .AddTo(gameObject);
}

昨日は一行目のAsObservableで、ButtonオブジェクトのonClickイベントをIObservableという型にキャストアップしているということが分かった。
IObservableはSubscribeメソッドによってイベントを受けて実行する処理を定義することができ、IObserver型によって発されたメッセージを受けとることができる。

で、昨日の疑問だが、IObservableとIObserverはいずれもSubjectを基軸に動いているのだが、IObservableに変換されたonClickはどこのSubjectからメッセージを受け取っているんだ?そもそもIObserverはいないように見えるんだけど?

この点は調べたけどいまいちわからんかった…どっかにいるSubjectが仕事してるんだろう。

Select以降のところを考える

二行目以降を考えてみよう。LinQで見たような気がする文言が並んでいる。
この.Selectや.Scanは「オペレータ」と分類されるものだ。オペレータの理屈についてもちゃんと解説記事はあったのだが、なんかよく分からん。
とりあえずオペレータはIObserveが送ってくれたメッセージに対してフィルタリングやメッセージの変換などを行うことができるものらしい。種類はたくさんあるので、今回使用しているものについてだけ調べながら処理内容を見てみる。

Selectオペレータ

値を変換したり、値に関数を適用することができる。
今回は下記のように使われているが…

.Select(_ => 1)

なんこれ。ラムダ式ってのはなんか分かるけど…
多分だけど、受け取るメッセージにあたる部分が「_」なのかな?で、ラムダ式の中で「何を受け取ろうが『1』を返す」というメソッドを定義したのだと思う。
つまり、メッセージを受け取ったときに値を「1」に変換しているのだと思われる。
ためしに1を2に変えたら2ずつ増分し始めたのでどうやらそうらしい。

Scanオペレータ

メッセージの値と前回の結果との両方を使い関数を適用する。ふうん。
コードは以下のようになっている。

.Scan(0, (element, acc) => element + acc)

これ試してみてわかったんですが、「element」と「acc」は仮の名前というか…なのでどんな名前にしてもちゃんと動きます。
scanの第一引数「0」は初期値。
elementには「前回の処理の結果」が格納される。
accには「今回受け取ったメッセージ」が格納される。この場合は「1」がaccに入る。
いくつかscanの活用事例を見たが、初期値は格納していない場合も多かったので、これは無きゃ無いでも動くようだ。

ここでSubscribe

これわかったぞこれ…
つまり
メッセージ(イベント通知)が来る
→selectで値を変換する(1に)
→scanで前回の処理結果+1してこれをメッセージとする
→Subscribeでscanの計算結果をメッセージとして受け取る
って流れなんやろコレ!!!ハイハイハイ!!!

.Subscribe(count => text.text = count.ToString())

渡されたメッセージ(計算結果)はcountに入っている。
ラムダ式でtextオブジェクトのtextプロパティにcountの値を代入していると。

AddToはオペレータじゃなかったです

AddToはIObservableに定義されているメソッドの一つ...なのかな?とにかくIObservableが使用するものらしいです。
これをつかうとSubScribeによるイベントメッセージの購読を終了する…らしい。
転職先の先輩曰く「メモリ開放のことやで、とりあえず今はあんまり気にせずおまじない的に書いておきなさい」とのことだったのでこれは深堀しないでおこう。

.AddTo(gameObject);

今回書いたものは理解できた!!

この「最初マジで意味わかんなかったものが理解できた時の快感」ってやばいよね。
ともかく、これでUpdateメソッドとかInspector内からイベント登録をしなくてもスクリプトだけで動作を取得する方法が分かった。
これを活用して次のステップに進んでいこうと思う。

今日はもう少しやるが、ブログ的には区切りがいいので一旦ここで止めておく。
【参考にしたサイト】
qiita.com
qiita.com