observable everywhere - rxの原則とunirxにみるデータソースの見つけ方
TRANSCRIPT
![Page 1: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/1.jpg)
Observable EverywhereRxの原則とUniRxにみるデータソースの見つけ方
2015/04/16Yoshifumi Kawai - @neuecc
![Page 2: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/2.jpg)
@neuecc - Who, Where, When
2009/04linq.js – LINQ to Objects for JavaScript
https://linqjs.codeplex.com/
この頃からずっとLINQを追いかけてる
2009/09/04
最初のRx記事、以降現在まで70以上は書いてる
http://neue.cc/category/programming/rx/
2011/04~
Microsoft MVP for .NET(C#)
野良C#エヴァンジェリスト(現在も継続中)
![Page 3: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/3.jpg)
@Work, @LINQ
2012/10~
株式会社グラニ取締役CTO
「神獄のヴァルハラゲート」「モンスターハンターロアオブカード」
サーバー/クライアント共に「最先端C# 技術を使った」ゲーム開発
C# 5.0 + .NET Framework 4.5 + ASP.NET MVC 5 + Unity
2014/04/19
UniRx最初の公開 - https://github.com/neuecc/UniRx
現在☆286, ver 4.8.0
2014/09/24
LINQ to BigQuery - https://github.com/neuecc/LINQ-to-BigQuery/
未だにLINQへの関心は衰えずといったところ
![Page 4: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/4.jpg)
Agenda
![Page 5: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/5.jpg)
せつめい
基本的には言語非依存の説明です
RxJavaの人もReactiveCocoaの人もWelcome
サンプルコードはC#になりますので適宜補完してください
Rxの基本原則などを紹介
衒学趣味を蹴っ飛ばして実用的な意味合いを探る
UniRxを例にRx利用時のデータソースの実例を紹介
自分の言語、自分のプラットフォームでも応用効くはず
![Page 6: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/6.jpg)
OnNext* (OnError | OnCompleted)?
![Page 7: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/7.jpg)
The grammar of messages
OnNext* (OnError | OnCompleted)?
日本語でおk
OnNextがゼロ回以上の後に
OnErrorもしくはOnCompletedが一回来る(もしくはどちらも来ない)
public interface IObserver<in T>{
void OnCompleted();void OnError(Exception error);void OnNext(T value);
}
![Page 8: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/8.jpg)
foreachで考えてみる; OnNext
foreach (var x in sequence){
OnNext(x);}
OnNext*
空の場合はOnNextは一度も呼ばれないのでOnNext*
![Page 9: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/9.jpg)
foreachで考えてみる; OnCompleted
foreach (var x in sequence){
OnNext(x);}OnCompleted();
OnNext* (OnCompleted)?
もしsequneceが無限シーケンス(例えばunfold)だったら
OnCompletedまで行かないので
OnNext * (OnCompleted)?
![Page 10: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/10.jpg)
foreachで考えてみる; OnError
try{
foreach (var x in sequence){
OnNext(x);}
}catch (Exception ex){
OnError(ex);// RxでonErrorをハンドルした場合// 再スローはない、これはあくまで再現
throw;}OnCompleted();
OnNext* (OnError | OnCompleted)?
もし途中で例外が起きたらOnCompletedは呼ばれずOnErrorだけが呼ばれることになるので
OnNext * (OnError | OnCompleted)?
![Page 11: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/11.jpg)
意味が通る!でも、なんで?
![Page 12: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/12.jpg)
IEnumeratorから考える
public interface IEnumerator<out T>{
T Current { get; } // 現在の要素bool MoveNext(); // 次の要素があるか確認しCurrentにセット
}
![Page 13: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/13.jpg)
分解
public interface IEnumerator<out T>{
T Current { get; }bool MoveNext() throws Exception; // (C#にthrowsはないけどネ)
}
public interface IEnumerator<out T>{
T Current { get; }true|false|Exception MoveNext();
}
MoveNextを分解するとtrue/false/Exceptionのどれかを返す
![Page 14: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/14.jpg)
集約
public interface IEnumerator<out T>{
T Current { get; }bool MoveNext() throws Exception; // (C#にthrowsはないけどネ)
}
public interface IEnumerator<out T>{
T Current { get; }T|void|Exception GetNext();
}
trueの時Tを、falseの時voidを返すと考えてCurrentも集約
![Page 15: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/15.jpg)
そして(何故か)反転させる
public interface IEnumeratorDual<in T>{
void GotNext(T|void|Exception);}
public interface IEnumerator<out T>{
T|void|Exception GetNext();}
![Page 16: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/16.jpg)
3つにちゃんとバラして
public interface IEnumeratorDual<in T>{
void GotNext(T);void GotNothing(void);void GotException(Exception);
}
public interface IEnumeratorDual<in T>{
void GotNext(T|void|Exception);}
![Page 17: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/17.jpg)
あら不思議、IObserver<T>が!
public interface IEnumeratorDual<in T>{
void GotNext(T);void GotNothing(void);void GotException(Exception);
}
名前を変えれば出来上がり
public interface IObserver<in T>{
void OnNext(T value);void OnCompleted();void OnError(Exception error);
}
![Page 18: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/18.jpg)
IEnumerableのほうも勿論public interface IEnumerable<out T>{
IEnumerator<T> GetEnumerator();}
![Page 19: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/19.jpg)
IEnumerator<T> : IDisposablepublic interface IEnumerable<out T>{
IEnumerator<T> GetEnumerator();}
public interface IEnumerable<out T>{
IDisposable|IEnumerator<T> GetEnumerator();}
IEnumerator<T> : IDisposable
![Page 20: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/20.jpg)
public interface IEnumerableDual<out T>{
IDisposable SetEnumeratorDual(IEnumeratorDual<T>);}
public interface IEnumerable<out T>{
IDisposable|IEnumerator<T> GetEnumerator();}
当然のように(何故か)逆にする
![Page 21: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/21.jpg)
public interface IEnumerableDual<out T>{
IDisposable SetEnumeratorDual(IEnumeratorDual<T>);}
あら不思議、IObservable<T>が!
public interface IObservable<out T>{
IDisposable Subscribe(IObserver<T> observer);}
名前を整えて出来上がり
![Page 22: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/22.jpg)
Mathematical Dual
双対
なるほどわからん
ふかーい関連性がある、ぐらいでいいんじゃまいか
実用的な意味では、OnNext* (OnError | OnCompleted)?だけ覚えればいい
同じように見なすことができる、という発見
IEnumerable<T> = イテレータパターン
IObservable<T> = オブザーバーパターン
一見無関係の2つのデザインパターンに見つけた深い関連性
イテレータのためのLINQがオブザーバーにも同じように適用できる発見
Reactive Extensions = LINQ to Events / LINQ to Asynchronous
![Page 23: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/23.jpg)
Pull(Interactive) vs Push(Reactive)
DataSource
(IEnumerable<T>)
Action
値の要求(Pull)
DataSource
(IObservable<T>)
Action
値の送信(Push)
![Page 24: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/24.jpg)
Length or Time
IEnumerable<T> length
IObservable<T> time
event
async
IE<T>
イベント(や非同期)を時間軸に乗ったシーケンスと見なすことができる
![Page 25: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/25.jpg)
Same Atmosphere
var query = from person in sequencewhere person.Age >= 20select person.Name;
foreach (var item in query){
OnNext(item);}
var query = from person in sequencewhere person.Age >= 20select person.Name;
query.Subscribe(item =>{
OnNext(item);});
IEnumerble<T> IObservable<T>
コレクション処理(filter/map)が超強力で有益なのは既知の話。それと同じように書ける、同じような挙動を取るということが注意深くデザインされている。
これがRxの革命的なポイントであり普及の一助となった
![Page 26: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/26.jpg)
Asynchronous Futures
![Page 27: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/27.jpg)
Synchronous Asynchronous
Sin
gle
(1)
Mu
ltip
le(*
)
var x = f(); var x = await f();
var query = from person in sequencewhere person.Age >= 20select person.Name;
foreach (var item in query){
OnNext(item);}
var query = from person in sequencewhere person.Age >= 20select person.Name;
query.Subscribe(item =>{
OnNext(item);});
IEnumerble<T> IObservable<T>
Func<T> Task<T>
![Page 28: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/28.jpg)
Synchronous Asynchronous
Sin
gle
(1)
Mu
ltip
le(*
)
var x = f(); var x = await f();
var query = from person in sequencewhere person.Age >= 20select person.Name;
foreach (var item in query){
OnNext(item);}
var query = from person in sequencewhere person.Age >= 20select person.Name;
query.Subscribe(item =>{
OnNext(item);});
IEnumerble<T> IObservable<T>
Func<T> Task<T>
FutureとかPromiseとか言われるアレの
C#での表現
![Page 29: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/29.jpg)
C# 5.0での非同期事情
C# 5.0では単発の非同期処理は全てTask<T>を返す
async/awaitという言語構文でのサポートが圧倒的に大きい
Task<T>にawaitするとTが取れるし、.NET Frameworkで全面的に非同期APIが追加された
Task is Observable, Observable is Awaitable
相互変換可能(Taskは長さ1のIObservable、IObservableはLastを取得してTaskに変
換できる)なので、適宜必要な局面で変換
Before async/await
専用構文がないTaskだけとの比較だとRxのほうが圧倒的に使いやすかった
というわけで、他言語においても事情は同じになるかな、と
専用構文が入ればそちらに寄って、それまではRxが使う上では強いはず
![Page 30: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/30.jpg)
What is UniRx?
![Page 31: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/31.jpg)
Welcome to the Reactive Game Architecture!
https://github.com/neuecc/UniRx Unity用のReactive Extensions実装(.NET用のRxはUnityで動かないので自前で実装)
![Page 32: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/32.jpg)
Official(?) ReactiveX Family
http://reactivex.io/ReactiveX公式サイトのLanguagesにラインナップされています(現状、非公式Rxでは唯一)
![Page 33: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/33.jpg)
CoreLibrary
Framework Adapter
Rx.NET
RxJS
RxJava
RxAndroidReactiveUI
ReactiveProperty
![Page 34: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/34.jpg)
CoreLibrary
Framework Adapter
UniRxは全部入り(多分ReactiveCocoaとかもそうでしょうけど)
Port of Rx.NET
MainThreadSchedulerObservableTriggers
ReactiveProperty
![Page 35: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/35.jpg)
Observable Everywhere
![Page 36: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/36.jpg)
データソースを見つけよう
何はともあれObservableの源流が必要
使い方は後からついてくる
通常のLINQ(to Objects)も何がシーケンスとみなせるか(DOMの
Traverseはシーケンスになる、DatabaseのRowはシーケンスとみな
せる、などなど)がまず最初にある
シーケンスを作ろう
シーケンスのように見なせるものがあれば、そこにLINQはある
見なせそうで見なせない、ありそうでないものがあれば、作りだ
そう、そうすればそこにLINQ(to Objects/to Events = Rx)が現れる
![Page 37: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/37.jpg)
Push Event Stream
Event Processing
Interactive/Visualize
![Page 38: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/38.jpg)
Statndard Static Generators
Range, Repeat, ToObservable
OnNext{n} (OnError | OnCompleted)?
Return
OnNext (OnError | OnCompleted)?
Empty
OnCompleted
Never
()
Throw
OnError
単体ではそう使うこともない感じですが、何かと組み合わせていく感じ
.Concat(Observable.Never)で、終了を抑止する(OnCompletedは飛ばさない、あるいは他のオペレータが飛ばす)、的な使い方ができる
![Page 39: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/39.jpg)
AsyncCallback Converter
IEnumerator GetStringAsync(string url, Action<string> onCompleted, Action<Exception> onError);
Callback Hell!謎のIEnumerator(Unity固有事情)
IObservable<string> Get(string url);
Callback to IObservable<T>
UniRxは組み込みの通信メソッドとしてObservableWWW.Get/Postメソッドなどを予め用意してあるので、ユーザーはすぐに非同期をRxで扱える
![Page 40: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/40.jpg)
But...
あれ、一緒……?
ObservableWWW.Get("http://hogemoge").Subscribe(Action<string>, Action<Exception>)
StartCoroutine(GetStringAsync("http://hogemoge", Action<string>, Action<Exception>))
![Page 41: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/41.jpg)
GetStringAsync("http://hogemoge", nextUrl =>{
GetStringsAsync(nextUrl, finalUrl =>{
GetStringsAsync(finalUrl, result =>{
/* result handling */}, ex => { /* third error handling */ });
}, ex => { /* second error handling */ });}, ex => { /* first error handling */ });
Callback Hellネスト地獄による圧倒的な見通しの悪さ
エラーハンドリングの困難(try-
catchで囲んでも意味が無い)
ObservableWWW.Get("http://hogemoge").SelectMany(nextUrl => ObservableWWW.Get(nextUrl)).SelectMany(finalUrl => ObservableWWW.Get(finalUrl)).Subscribe(
result => { /*result handling */ },ex => { /* error handling */ });
ネストを一段に抑えるパイプライン統合のエラー処理
![Page 42: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/42.jpg)
Composability
var query = from nextUrl in ObservableWWW.Get("http://hogemoge")from finalUrl in ObservableWWW.Get(nextUrl)from result in ObservableWWW.Get(finalUrl)select new { nextUrl, finalUrl, result };
var parallelQuery = Observable.WhenAll(ObservableWWW.Get("http//hogehoge"),ObservableWWW.Get("http//hugahuga"),ObservableWWW.Get("http//takotako"));
var parallelQuery = Observable.WhenAll(ObservableWWW.Get("http//hogehoge"),ObservableWWW.Get("http//hugahuga"),ObservableWWW.Get("http//takotako"))
.Timeout(TimeSpan.FromSeconds(15));
var incrementalSearch = userInput.Throttle(TimeSpan.FromMilliseconds(250)).Select(keyword => SearchAsync(keyword)).Switch();
前の結果を保持したままの直列処理
並べるだけで済む並列処理
メソッドを足すだけのTimeout付与
ユーザー入力など他のIObservableとの自然な融和と、複雑なPruning
![Page 43: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/43.jpg)
UIEvent Converter
Button
IObservable<Unit>
InputField
IObservable<string>
Toggle
IObservable<bool>
Slider
IObservable<float>
var incrementalSearch = input.OnValueChangeAsObservable().Throttle(TimeSpan.FromMilliseconds(250)).Select(keyword => SearchAsync(keyword)).Switch();
from InputField
toggle.OnValueChangedAsObservable().Subscribe(x => button.interactable = x);
ToggleのON/OFFでボタンの有効/非有効切り替え
![Page 44: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/44.jpg)
ObservableTriggers
衝突 = IObservable<Collision>
cube.OnCollisionEnterAsObservable().Subscribe();
カメラに入る = OnBecameVisibleAsObservable() = IObservable<Unit>
カメラから出る = OnBecameInvisibleAsObservable() = IObservable<Unit>
などなど、あらゆるインタラクションが全てIObservableになる
![Page 45: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/45.jpg)
Lifecycle as Observable
frame(time)
OnDestroyAsObservable = (OnNext{1} OnCompleted)
ゲームループは毎フレーム(60fpsなら1/60秒)毎にメソッドが呼びだされる
Observable.EveryUpdate().Subscribe(); // IObservable<long>
![Page 46: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/46.jpg)
MultiThreading
Rx = Observable + LINQ + Scheduler
Observable.Start(() =>{
/* なにか重い処理 */
return "hogemoge";}, Scheduler.ThreadPool).ObserveOn(Scheduler.MainThread) // MainThreadに戻す
.DelaySubscription(TimeSpan.FromSeconds(3), Scheduler.MainThreadIgnoreTimeScale)
.Subscribe(x => ui.text = x); // UIパーツはメインスレッドからじゃないと触れない
Schedulerは時空魔法の、時(AbsoluteTime/RelativeTime)と空間(実行スレッド/SynchronizationContext)をコントロール
![Page 47: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/47.jpg)
Polling
ポーリングはIObservableと「見なせる」
IObservable<string> WatchFile(string path){
return Observable.Interval(TimeSpan.FromSeconds(5)).Select(_ => File.ReadAllText(path));
}
IObservable<Notification<Unit>> Ping(string path){
return Observable.Interval(TimeSpan.FromSeconds(5)).SelectMany(_ =>
ObservableWWW.Get("http://ping").Timeout(TimeSpan.FromSeconds(1))).AsUnitObservable().Materialize();
}
![Page 48: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/48.jpg)
EveryValueChanged
更に踏み込んで、値変更を常時監視する黒魔法化
原理はゲームループのUpdate監視による毎フレームチェック
常に必ず呼ばれるから監視コストが相対的に低い(ゲームエンジンならでは)
生存期間管理
無限監視は危険なので、監視対象の生存具合をチェック
GameObjectなら破壊されていたら監視終了
C#オブジェクトなら、WeakReferenceで参照保持し、GCされてたら監視終了
// Position(x,y,z)が変化したら通知するIObservable<Vector3>
this.ObserveEveryValueChanged(x => x.transform.position).Subscribe(x => Debug.Log("poistion changed:" + x));
![Page 49: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/49.jpg)
Logs are EventStream
ログはInProcessにおいてもStreamとして扱えるべき
logger(“Player”)
logger(“Monster”)
logger(“Network”)
logger(“ItemResolver”)
ObservableLogger.Listener
(IObservable<LogEntry>)
SubscribeToLogConsole
SubscribeToInGameDebugWindow
SubscribeToNetWorkLogStorage
![Page 50: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/50.jpg)
EventAggregator/MessageBroker
InProcessのMessageBus(名前はなんでもいいや)
先のObservableLoggerと同じ図式
中央がIObservable<T>なら
購読= IObservable<T>.Subscribe()
解除= subscription.Dispose()
![Page 51: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/51.jpg)
最も単純な(しかし便利な)実装
public static class MessageBroker{
public static void Publish<T>(T message){
Notifier<T>.Instance.OnNext(message);}
public static IObservable<T> Receive<T>(){
return Notifier<T>.Instance.AsObservable();}
static class Notifier<T>{
public static readonly Subject<T> Instance = new Subject<T>();}
}
型(<T>)でフィルタされるグローバルに通知可能な何か、と考える
送信側:
MessageBroker.Publish(new HogeEvent())
受信側:
MessageBroker.Receive<HogeEvent>()
![Page 52: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/52.jpg)
Model-View-Whatever
![Page 53: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/53.jpg)
M-V-Hell
MVVVVVV
![Page 54: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/54.jpg)
M-V-VM
DataBinding!
![Page 55: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/55.jpg)
![Page 56: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/56.jpg)
バインディングに必要なもの
バインディングエンジン
Reactive Extensionsはバインディングエンジン「ではない」
RxでViewに何か処理するにはViewを知らなければならない
Unityにバインディングエンジンはない
作るか諦めるか
どうしても必要なら作るしかない
私はMVVMを選ばないことを選んだ
Unityにおいては(Rxがあれば)そこまで必要ではないと判断した
![Page 57: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/57.jpg)
Model-View-Presenter
DataBindingがないのでViewModelのかわりに
PresenterがViewを直接弄り、イベントを受け取る
Rxで各レイヤーを繋げてしまえば見通しよく構築可能
![Page 58: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/58.jpg)
NotificationModel
Modelは通知可能でなければならない
// 変更通知付きなモデル
public class Enemy{
// HPは変更あったら通知して他のところでなんか変化を起こすよね?
public IReactiveProperty<long> CurrentHp { get; private set; }
// 死んだら通知起こすよね?
public IReadOnlyReactiveProperty<bool> IsDead { get; private set; }
public Enemy(int initialHp){
// 死んだかどうかというのはHPが0以下になったら、で表現できる
CurrentHp = new ReactiveProperty<long>(initialHp);IsDead = CurrentHp.Select(x => x <= 0).ToReadOnlyReactiveProperty();
}}
![Page 59: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/59.jpg)
ReactivePropertypublic class ReactivePresenter : MonoBehaviour{
// ViewからのイベントをUIEventAsObservableで購読
Button button;Text textView;
// ModelからのイベントをReactivePropertyで購読、SubscribeでViewにUpdate
Enemy enemy;
// 宣言的なUI記述
void Start(){
// ボタンクリックでHPを減らす
button.OnClickAsObservable().Subscribe(_ => enemy.CurrentHp.Value -= 99);
// その変更を受けてUIに変更を戻すenemy.CurrentHp.SubscribeToText(textView); // とりあえず現在HPをTextに表示
// もし死んだらボタンクリックできないようにする
enemy.IsDead.Select(isDead => !isDead).SubscribeToInteractable(button);}
}
ReactivePropertyは通知付きモデルの作成を補助値自体が変更をIObservable<T>として通知
![Page 60: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/60.jpg)
Model-View-(Reactive)Presenter
![Page 61: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/61.jpg)
not databinding but databinding
データバインディングっぽい
Rxの力で、かなりそれらしい感触になる
マクロがあればもっと整形できそうだけどC#はマクロないので
別にそこまでは必要じゃない
無理しない
やはりゲームなのでパフォーマンスの読みやすさは必須
バインディングエンジン作成は一朝一夕でできない、それよりMV(R)Pはシンプ
ルな、というか中間レイヤーはなにもないので挙動も読みやすくオーバヘッド
もない
既に実績あるバインディングエンジンがあるなら、それに乗っかればいい
ReactivePropertyはModelの作成を補助するのでMVVMのMやVMのレイヤーで十分使える
![Page 62: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/62.jpg)
Conclusion
![Page 63: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/63.jpg)
資料紹介
Event processing at all scales with Reactive Extensions
http://channel9.msdn.com/events/TechDays/Techdays-2014-the-
Netherlands/Event-processing-at-all-scales-with-Reactive-Extensions
現時点でのRx入門の決定版といえる素晴らすぃVideo
Bart氏は現在はMicrosoftのPrincipal Software Engineer(偉い)
Cloud-Scale Event Processing Using Rx
http://buildstuff14.sched.org/event/174c265e4ec350bd2e005b59bce0275e
激烈にAdvancedな内容
IReactiveProcessingModel, SubjectのDuality, 次世代のRx(3.0)などなど
更なる未来を見据えた内容なので是非一読を
![Page 64: Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方](https://reader031.vdocuments.pub/reader031/viewer/2022012315/55a550d31a28ab483c8b45a3/html5/thumbnails/64.jpg)
まとめ
LINQ to Everywhere
Reactive Extensions is not only Asynchronous
Reactive Extensions is not only Data-Binding
Observable Everywhere
データソースさえ見つかれば、そこにRxはある
あとは無限の合成可能性を活かせばいい!
まず、データソースを探しだしてください