Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

UniRxことはじめ

社内勉強会用

  • Login to see the comments

UniRxことはじめ

  1. 1. UniRxことはじめ KLab株式会社 安井彰一
  2. 2. 自己紹介 名前:安井彰一 経歴:コンソールゲームプログラマ5年 ネットワークゲームエンジニア3年 昨年9月 KLab入社 (Uni)Rx歴3ヶ月
  3. 3. 本稿の対象者 Linqがある程度読める人 ( C# ) Rxの資料は読んだがまだ書いたことがない人
  4. 4. 注釈 本稿ではUniRxを題材に扱いますが、 その他のRx実装でも同様の考え方ができます。 ただし、メソッド名などが違う場合が ありますのでご注意ください。
  5. 5. アジェンダ ・(Uni)Rxとは ・Rxの利点 ・Rxの問題点 ・UniRxの導入いろは ・デバッグ方法 ・まとめ
  6. 6. アジェンダ ・(Uni)Rxとは ・Rxの利点 ・Rxの問題点 ・UniRxの導入いろは ・デバッグ方法 ・まとめ
  7. 7. UniRxとは
  8. 8. Rx(Reactive Extensions)の Unity実装です。
  9. 9. Rxとは (Reactive Extensions)
  10. 10. イベントの流れ及びその制御を LINQのような書式で 簡潔に書けるようにしたもの。(意訳)
  11. 11. Rxの実装リスト Rx.NET RxJS RxJava ( RxAndroid ) ReactiveCocoa Rx.py など
  12. 12. ReactiveExtensionsとは プログラミング手法のこと。 C#だけのものではありません。
  13. 13. アジェンダ ・(Uni)Rxとは ・Rxの利点 ・Rxの問題点 ・UniRxの導入いろは ・デバッグ方法 ・まとめ
  14. 14. Rxの利点
  15. 15. 時間を簡単に扱える
  16. 16. 例題
  17. 17. 例題A マウスを押しっぱなしにしている間、 数値をカウントアップさせる。
  18. 18. Rxではない実装 class Hoge : MonoBehaviour { public int Count { get; private set; } public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown ) { ++Count; } } }
  19. 19. Rxではない実装 class Hoge : MonoBehaviour { public int Count { get; private set; } public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown ) { ++Count; } } }
  20. 20. Rxでの実装 class Hoge : MonoBehaviour { public int Count { get; private set; } public void Start() { IObservable<Unit> mouseDown = gameObject.OnMouseDownAsObservable(); mouseDown.Subscribe( _ => ++Count ); } }
  21. 21. Rxでの実装 class Hoge : MonoBehaviour { public int Count { get; private set; } public void Start() { IObservable<Unit> mouseDown = gameObject.OnMouseDownAsObservable(); // ↑マウスが押された時にUnitが流れてくるメッセージストリームを取得 mouseDown.Subscribe( _ => ++Count ); } }
  22. 22. Rxでの実装 class Hoge : MonoBehaviour { public int Count { get; private set; } public void Start() { IObservable<Unit> mouseDown = gameObject.OnMouseDownAsObservable(); // ↑Unitとは何でもないものを表す(後々出てくるストリームの合成で型をあわせる為に必要) mouseDown.Subscribe( _ => ++Count ); } }
  23. 23. Rxでの実装 class Hoge : MonoBehaviour { public int Count { get; private set; } public void Start() { IObservable<Unit> mouseDown = gameObject.OnMouseDownAsObservable(); // ↓イベントが流れてきたら処理を実行する mouseDown.Subscribe( _ => ++Count ); } }
  24. 24. 例題A’ マウスを押しっぱなしにしている間、 一定間隔で数値をカウントアップさせる。
  25. 25. Rxではない実装 class Hoge : MonoBehaviour { private DateTime nextCountUp = DateTime.Now; private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown && nextCountUp <= DateTime.Now ) { ++Count; nextCountUp = DateTime.Now + countUpIntervalSpan; } else if ( false == mouseDown ) { nextCountUp = DateTime.Now; } } }
  26. 26. Rxではない実装 class Hoge : MonoBehaviour { private DateTime nextCountUp = DateTime.Now; // 次回カウントアップまでの秒数管理. private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown && nextCountUp <= DateTime.Now ) { ++Count; nextCountUp = DateTime.Now + countUpIntervalSpan; } else if ( false == mouseDown ) { nextCountUp = DateTime.Now; } } }
  27. 27. Rxではない実装 class Hoge : MonoBehaviour { private DateTime nextCountUp = DateTime.Now; private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); // 押されているかどうかの判定. if ( mouseDown && nextCountUp <= DateTime.Now ) { ++Count; nextCountUp = DateTime.Now + countUpIntervalSpan; } else if ( false == mouseDown ) { nextCountUp = DateTime.Now; } } }
  28. 28. Rxではない実装 class Hoge : MonoBehaviour { private DateTime nextCountUp = DateTime.Now; private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown && nextCountUp <= DateTime.Now ) { // 間隔の制御. ++Count; nextCountUp = DateTime.Now + countUpIntervalSpan; } else if ( false == mouseDown ) { nextCountUp = DateTime.Now; } } }
  29. 29. Rxではない実装 class Hoge : MonoBehaviour { private DateTime nextCountUp = DateTime.Now; private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown && nextCountUp <= DateTime.Now ) { ++Count; // 目的の処理. nextCountUp = DateTime.Now + countUpIntervalSpan; } else if ( false == mouseDown ) { nextCountUp = DateTime.Now; } } }
  30. 30. Rxではない実装 class Hoge : MonoBehaviour { private DateTime nextCountUp = DateTime.Now; private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown && nextCountUp <= DateTime.Now ) { ++Count; nextCountUp = DateTime.Now + countUpIntervalSpan; // 次のカウントアップ時間の予約. } else if ( false == mouseDown ) { nextCountUp = DateTime.Now; } } }
  31. 31. Rxではない実装 class Hoge : MonoBehaviour { private DateTime nextCountUp = DateTime.Now; private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown && nextCountUp <= DateTime.Now ) { ++Count; nextCountUp = DateTime.Now + countUpIntervalSpan; } else if ( false == mouseDown ) { nextCountUp = DateTime.Now; } } // ↑離された場合のカウントアップ予約解除処理 }
  32. 32. Rxでの実装 class Hoge : MonoBehaviour { private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Start() { var mouseDown = gameObject.OnMouseDownAsObservable(); var mouseUp = gameObject.OnMouseUpAsObservable(); mouseDown .SelectMany( _ => Observable.Interval( countUpIntervalSpan ) ) .TakeUntil( mouseUp ).RepeatUntilDestroy( this ) .Subscribe( _ => ++Count ); } }
  33. 33. Rxでの実装 class Hoge : MonoBehaviour { private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Start() { // ↓マウスの状態ストリーム. var mouseDown = gameObject.OnMouseDownAsObservable(); var mouseUp = gameObject.OnMouseUpAsObservable(); mouseDown .SelectMany( _ => Observable.Interval( countUpIntervalSpan ) ) .TakeUntil( mouseUp ).RepeatUntilDestroy( this ) .Subscribe( _ => ++Count ); } }
  34. 34. Rxでの実装 class Hoge : MonoBehaviour { private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Start() { var mouseDown = gameObject.OnMouseDownAsObservable(); var mouseUp = gameObject.OnMouseUpAsObservable(); mouseDown // ←マウスが押されたら. .SelectMany( _ => Observable.Interval( countUpIntervalSpan ) ) .TakeUntil( mouseUp ).RepeatUntilDestroy( this ) .Subscribe( _ => ++Count ); } }
  35. 35. Rxでの実装 class Hoge : MonoBehaviour { private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Start() { var mouseDown = gameObject.OnMouseDownAsObservable(); var mouseUp = gameObject.OnMouseUpAsObservable(); mouseDown .SelectMany( _ => Observable.Interval( countUpIntervalSpan ) ) // ↑一定周期でメッセージが流れるストリームに切り換えて. .TakeUntil( mouseUp ).RepeatUntilDestroy( this ) .Subscribe( _ => ++Count ); } }
  36. 36. Rxでの実装 class Hoge : MonoBehaviour { private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Start() { var mouseDown = gameObject.OnMouseDownAsObservable(); var mouseUp = gameObject.OnMouseUpAsObservable(); mouseDown .SelectMany( _ => Observable.Interval( countUpIntervalSpan ) ) // ↓マウスが離されるまで. .TakeUntil( mouseUp ).RepeatUntilDestroy( this ) .Subscribe( _ => ++Count ); } }
  37. 37. Rxでの実装 class Hoge : MonoBehaviour { private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Start() { var mouseDown = gameObject.OnMouseDownAsObservable(); var mouseUp = gameObject.OnMouseUpAsObservable(); mouseDown .SelectMany( _ => Observable.Interval( countUpIntervalSpan ) ) // ↓マウスが離されたらもう一度押されるのを待つ. .TakeUntil( mouseUp ).RepeatUntilDestroy( this ) .Subscribe( _ => ++Count ); } }
  38. 38. Rxでの実装 class Hoge : MonoBehaviour { private TimeSpan countUpIntervalSpan = TimeSpan.FromSeconds( 1 ); public void Start() { var mouseDown = gameObject.OnMouseDownAsObservable(); var mouseUp = gameObject.OnMouseUpAsObservable(); mouseDown .SelectMany( _ => Observable.Interval( countUpIntervalSpan ) ) .TakeUntil( mouseUp ).RepeatUntilDestroy( this ) .Subscribe( _ => ++Count ); // ←処理. } }
  39. 39. 例題A’’ マウスを一定時間以上 押しっぱなしにしている間、 一定間隔で数値をカウントアップさせる。
  40. 40. Rxではない実装 class Hoge : MonoBehaviour { private DateTime? beginCountUp = null; private TimeSpan countUpHoldSpan = TimeSpan.FromSeconds( 1 ); public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown ) { if ( beginCountUp.HasValue ) { if ( beginCountUp <= DateTime.Now ) { /* 一定間隔でカウントアップする処理 */ } } else { beginCountUp = DateTime.Now + countUpHoldSpan; } } else { beginCountUp = null; } }
  41. 41. Rxではない実装 class Hoge : MonoBehaviour { private DateTime? beginCountUp = null; private TimeSpan countUpHoldSpan = TimeSpan.FromSeconds( 1 ); public void Update() { bool mouseDown = Input.GetMouseButtonDown(0); if ( mouseDown ) { if ( beginCountUp.HasValue ) { if ( beginCountUp <= DateTime.Now ) { /* 一定間隔でカウントアップする処理 */ } } else { beginCountUp = DateTime.Now + countUpHoldSpan; } } else { beginCountUp = null; } }
  42. 42. Rxでの実装 class Hoge : MonoBehaviour { private TimeSpan countUpHoldSpan = TimeSpan.FromSeconds( 1 ); public void Start() { var mouseDown = gameObject.OnMouseDownAsObservable(); var mouseUp = gameObject.OnMouseUpAsObservable(); mouseDown.Delay( countUpHoldSpan ) .SelectMany( _ => Observable.Interval( TimeSpan.FromSeconds( 1 ) ) ) .TakeUntil( mouseUp ).RepeatUntilDestroy( this ) .Subscribe( _ => ++Count ); // ←処理. } }
  43. 43. Rxでの実装 class Hoge : MonoBehaviour { private TimeSpan countUpHoldSpan = TimeSpan.FromSeconds( 1 ); public void Start() { var mouseDown = gameObject.OnMouseDownAsObservable(); var mouseUp = gameObject.OnMouseUpAsObservable(); mouseDown.Delay( countUpHoldSpan ) .SelectMany( _ => Observable.Interval( TimeSpan.FromSeconds( 1 ) ) ) .TakeUntil( mouseUp ).RepeatUntilDestroy( this ) .Subscribe( _ => ++Count ); // ←処理. } }
  44. 44. Rxの利点 このように、Rxの方が時間を操作するための オペレータが多数揃っているという点で優れています。 それ以外にもフィルタリング処理やデータの変換、 複数のストリームをマージすることができたり、 イベント処理と比べて処理を 1箇所にまとめて記述できるなど その他の利点も多数存在します。
  45. 45. アジェンダ ・(Uni)Rxとは ・Rxの利点 ・Rxの問題点 ・UniRxの導入いろは ・デバッグ方法 ・まとめ
  46. 46. Rxの問題点
  47. 47. 問題点 動きが 予測しづらい
  48. 48. 例えば SkipUntilと TakeUntil
  49. 49. SkipUntil/TakeUntilの動き var s = new Subject<int>(); s.SkipUntil( x => ( 0 == ( x % 2 ) ) .TakeUntil( x => ( 0 != ( x % 2 ) ) .Subscribe( x => Debug.Log(x) ); s.OnNext( 1 ); s.OnNext( 2 ); s.OnNext( 3 ); s.OnNext( 4 ); 結果 :
  50. 50. SkipUntil/TakeUntilの動き 結果 : 2で割り切れるまで読み飛ばす var s = new Subject<int>(); s.SkipUntil( x => ( 0 == ( x % 2 ) ) .TakeUntil( x => ( 0 != ( x % 2 ) ) .Subscribe( x => Debug.Log(x) ); s.OnNext( 1 ); s.OnNext( 2 ); s.OnNext( 3 ); s.OnNext( 4 );
  51. 51. SkipUntil/TakeUntilの動き 結果 : 2で割り切れるまで読み飛ばす 2で割り切れる間は続ける var s = new Subject<int>(); s.SkipUntil( x => ( 0 == ( x % 2 ) ) .TakeUntil( x => ( 0 != ( x % 2 ) ) .Subscribe( x => Debug.Log(x) ); s.OnNext( 1 ); s.OnNext( 2 ); s.OnNext( 3 ); s.OnNext( 4 );
  52. 52. SkipUntil/TakeUntilの動き var s = new Subject<int>(); s.SkipUntil( x => ( 0 == ( x % 2 ) ) .TakeUntil( x => ( 0 != ( x % 2 ) ) .Subscribe( x => Debug.Log(x) ); s.OnNext( 1 ); s.OnNext( 2 ); s.OnNext( 3 ); s.OnNext( 4 ); 結果 : 2で割り切れるまで読み飛ばす 2で割り切れる間は続ける 2だけが出力?
  53. 53. SkipUntil/TakeUntilの動き var s = new Subject<int>(); s.SkipUntil( x => ( 0 == ( x % 2 ) ) .TakeUntil( x => ( 0 != ( x % 2 ) ) .Subscribe( x => Debug.Log(x) ); s.OnNext( 1 ); s.OnNext( 2 ); s.OnNext( 3 ); s.OnNext( 4 ); 結果 : 出力なし
  54. 54. SkipUntil/TakeUntilの動き 結果 : 1が流れてきた時点で TakeUntilの条件を満たしてしまい ストリームが終了した var s = new Subject<int>(); s.SkipUntil( x => ( 0 == ( x % 2 ) ) .TakeUntil( x => ( 0 != ( x % 2 ) ) .Subscribe( x => Debug.Log(x) ); s.OnNext( 1 ); s.OnNext( 2 ); s.OnNext( 3 ); s.OnNext( 4 );
  55. 55. SkipUntil/TakeUntilの動き var s = new Subject<int>(); s.SkipUntil( x => ( 0 == ( x % 2 ) ) .TakeUntil( x => ( 0 != ( x % 2 ) ) .Subscribe( x => Debug.Log(x) ); s.OnNext( 1 ); s.OnNext( 2 ); s.OnNext( 3 ); s.OnNext( 4 ); 理由 : SkipUntilで待っている間も TakeUntilの条件は 常にチェックされている。
  56. 56. 解決策 慣れてください
  57. 57. Rxの各オペレータの動き Rxにおける各オペレータは ストリームの性質を定義しているに過ぎず、 LINQと同じ感覚で使っていると 思ったとおりに動かない。
  58. 58. Rxの各オペレータの動き Rxにおける各オペレータは ストリームの性質を定義しているに過ぎず、 LINQと同じ感覚で使っていると 思ったとおりに動かない。 ⇒慣れるしかない
  59. 59. Rxの各オペレータの動き Rxにおける各オペレータは ストリームの性質を定義しているに過ぎず、 LINQと同じ感覚で使っていると 思ったとおりに動かない。 ⇒慣れるしかない http://rxmarbles.com/
  60. 60. 問題点 購読し続ける事により キャプチャされた インスタンスが 解放されない
  61. 61. 解決策 AddToという オペレータを 使ってください
  62. 62. AddTo public static IDisposable AddTo(this IDisposable disposable, GameObject gameObject); public static IDisposable AddTo(this IDisposable disposable, Component gameObjectComponent) public static IDisposable AddTo(this IDisposable disposable, ICollection<IDisposable> container, GameObject gameObject) public static IDisposable AddTo(this IDisposable disposable, ICollection<IDisposable> container, Component gameObjectComponent) public static T AddTo<T>(this T disposable, ICollection<IDisposable> container)
  63. 63. AddTo // OnDestroyで勝手に破棄 Observable...Subscribe().AddTo( this ); Observable...Subscribe().AddTo( gameObject ); // 参照を失った時やDispose()が呼ばれた時に破棄 var d = new CompositeDisposable(); Observable...Subscribe().AddTo( d );
  64. 64. 問題点 オペレータ毎に 増えていく メモリ使用量
  65. 65. 解決策(?) 処理負荷との トレードオフで検討 (GC...)
  66. 66. オペレータ実行時にObserverクラスが生成される。 小さなクラスが大量に生成される。 -> GC負荷が上がる。 欠点:メモリ使用量とGCのコスト
  67. 67. オペレータ実行時にObserverクラスが生成される。 小さなクラスが大量に生成される。 -> GC負荷が上がる。 UniRxの場合、専用のコンポーネントが生成される。 生成コストや上記と同じくGCの負荷が上がる。 欠点:メモリ使用量とGCのコスト
  68. 68. オペレータ実行時にObserverクラスが生成される。 小さなクラスが大量に生成される。 -> GC負荷が上がる。 UniRxの場合、専用のコンポーネントが生成される。 生成コストや上記と同じくGCの負荷が上がる。 不要なオブジェクトの参照を握り続けてしまう。(前述) 解放できない領域が残る可能性がある。 -> メモリリーク 欠点:メモリ使用量とGCのコスト
  69. 69. 変数などに変更が加わった時だけなど 必要なときにだけ処理が実行される。 処理負荷が軽減されることを期待できる。 利点:処理負荷やバグの出難さ
  70. 70. 変数などに変更が加わった時だけなど 必要なときにだけ処理が実行される。 処理負荷が軽減されることを期待できる。 メンバ変数などを保持する必要がなくなる。 変数の状態起因系のバグが出にくい。 利点:処理負荷やバグの出難さ
  71. 71. 変数などに変更が加わった時だけなど 必要なときにだけ処理が実行される。 処理負荷が軽減されることを期待できる。 メンバ変数などを保持する必要がなくなる。 変数の状態起因系のバグが出にくい。 小さな関数の合成によりコーディング中のバグが出にくい。 関数型プログラミングの利点がそのまま利点に。 といっても副作用のある関数だとバグは出ます。 利点:処理負荷やバグの出難さ
  72. 72. 問題点 とっつきにくい
  73. 73. 解決策 慣れてください
  74. 74. 正直、Rxはトライアンドエラーで 動作の感覚を身につけるしか 習熟する方法はないと思います。
  75. 75. 問題点 学習コストが高い
  76. 76. 解決策 慣れるための 環境づくり
  77. 77. 先日のUniRx勉強会で話を聞いたところ、 LINQを知らない人の基準での学習コストは 概ね1ヶ月ほどとのことです。 また、LINQがわかるなら もう少し短縮できるだろうとのことでした。 学習コスト
  78. 78. それでも 使わないとRxのコードも読めません。 そのため、 みんなでRxでコードを書こうという チーム内での合意が必要です! 学習コスト
  79. 79. 問題点 勉強方法
  80. 80. 解決策 ググる
  81. 81. Rxについては ReactiveExtensionsで検索すれば 解説が多数ヒットします。 UniRxについても “UniRx”で検索すれば (Google先生がUnixをお勧めしてくれるので) 解説が多数ヒットします。
  82. 82. 強いて言えば 以下のサイトがお勧めです。 http://rxmarbles.com/ http://qiita.com/tags/unirx https://unityuserj.slack.com/messages/unirx/
  83. 83. 先日行われた UniRx勉強会の資料 はじめてのUniRx 若輩エンジニアから見たUniRxを利用したゲーム開発 Interactive UI with UniRx History & Practices for UniRx UniRxの歴史、 或いは開発(中)タイトルの用例と落とし穴の回避法 「ずいぶんとダサいライティングを使っているのね 〜UniRxを用いた物理ベースライティング制御〜 UniRx勉強会のまとめ
  84. 84. 興味を持ったので UniRxを使ってみたい
  85. 85. でもどこから 手を付けていいのか わからない
  86. 86. ということで
  87. 87. アジェンダ ・(Uni)Rxとは ・Rxの利点と問題点 ・UniRxの導入いろは ・デバッグ方法 ・まとめ
  88. 88. UniRxの 導入いろは
  89. 89. AssetStoreからパッケージで落とす https://www.assetstore.unity3d.com/jp/#!/content/17276 Githubからソースを落とす https://github.com/neuecc/UniRx UniRxをプロジェクトに追加
  90. 90. 手の付け方
  91. 91. public event Action SomeEvent; があった場合、 Observable.FromEvent( h => SomeEvent += h, h => SomeEvent -= h ) を行えばストリームに変換できます。 イベントをストリームに変換する
  92. 92. public delegate void SomeEvent(); public SomeEvent someEvent; があった場合、 Observable.FromEvent<SomeEvent>( h => ()=>h(), h => SomeEvent += h, h => SomeEvent -= h ) // デリゲートに変換 を行えばストリームに変換できます。 デリゲートをストリームに変換する
  93. 93. var s = new Subject<T>(); を定義して s.OnNext(...); で配信することができます。 s.OnCompleted(); でストリームの終了ができます。 Subjectでメッセージを配信
  94. 94. 全てのストリームは Subjectが起点だと思って問題無いです。 Subjectでメッセージを配信
  95. 95. IDisposable d = IObservable<T>.Subscribe<T>( Action<T> onNext, Action<Exception> onError, Action onCompleted ); onNextのみなど各種パターンが用意されています。 配信されているメッセージを購読
  96. 96. Rxではない実装 private int? previousValue = null; void Update() { var value = 監視したい値; if ( previousValue.HasValue && previousValue.Value != value ) { /* 処理 */ } previousValue = value; } 値の監視
  97. 97. 値の監視 Rxでの実装 component.ObserveEveryValueChanged( _ => 監視したい値 )
  98. 98. var rp = new ReactiveProperty<T>(初期値); があれば IObservable<T> rpAsObservable { get { return rp; } } で配信することができます。 配信タイミングは rp.Value = 値; などで値が書き換わった時。 ReactiveProperty<T>
  99. 99. var rp = new ReactiveProperty<T>(初期値); また、ストリームとして以外でも rp.Value でその時点での値が取得できます。 ReactiveProperty<T>
  100. 100. private ReactiveProperty<T> ValueProperty; public IObservable<T> ValueAsObservable { get { return ValueProperty; } } public T Value { get { return ValueProperty.Value; } private set { ValueProperty.Value = value; } } は鉄板だと思います!ぜひスニペットに登録を! ReactiveProperty<T>
  101. 101. ピンと来ない方はExcelなどのセルを 想像して頂けるとわかりやすいかと思います。 そのセルを更新すると他のセルも更新される、という感じです。 ReactiveProperty<T>
  102. 102. その他Bindingもどきなど活用法が多々ありますので いろいろ調べて使ってみてください。 MV(R)P : Model View ReactiveProperty という構造も推奨されています。 今回は割愛します。 ReactiveProperty<T>
  103. 103. ReactiveProperty<T>をSerializeFieldに指定しても ジェネリック型であるため、Inspectorに表示されない。 しかし、ジェネリック型でなくせばその制限がなくなるため、 以下のように基本的な型が用意されている。 IntReactiveProperty LongReactiveProperty ByteReactiveProperty FloatReactiveProperty DoubleReactiveProperty StringReactiveProperty BoolReactiveProperty Vector2ReactiveProperty Vector3ReactiveProperty Vector4ReactiveProperty ColorReactiveProperty RectReactiveProperty AnimationCurveReactiveProperty BoundsReactiveProperty QuaternionReactiveProperty .*ReactiveProperty
  104. 104. List及びDictinaryのReactiveProperty版です。 ReactiveCollection/ReactiveDictionary
  105. 105. AddTo()でちらっと解説しましたが、 IDisposableをまとめて管理してくれるクラスです。 基本はGCされた時にDisposeする用途が多いですが、 Disposeを明示的にコールしたい時などに使用します。 繰り返し使用したい場合はDisposeではなく Clearを使用するようにしましょう。 同系統のクラスはこちらに解説があります。 http://blog.xin9le.net/entry/2014/02/10/120619 CompositeDisposable
  106. 106. ストリームの再購読の方法として Repeat()をよく見かけます。 しかし、これだと無限に 購読し続けるおそれがあります。 UniRxの場合は RepeatUntilDestroy() を 使用するようにしましょう。 Repeat()について
  107. 107. Unityではシングルスレッドの動作しかできませんが、 UniRxを使うことで別スレッドで計算などをさせることができます。 (ただし、私は使ったことがないため、紹介だけ・・・) Observable.Start() : バックグラウンドで処理を実行 ObserveOn(Scheduler.ThreadPool) : 以降の処理を別スレッドで実行 ObserveOnMainThread() : メインスレッドに処理を戻す 別スレッド実行
  108. 108. uGUIやWWWなどのクラスをラップした オペレータが標準で用意されています。 これにより、以下のような簡単なコードで目的が達成できます。 また、各種オペレータにより、タイムアウトやリトライも簡単に! (ただし、これも使ったことないです・・・。) // ボタン押下 Button.onClick.AsObservable().Subscribe() // httpアクセス ObservableWWW.Get() uGUIやhttpなどのラッパー
  109. 109. RxにはHotやColdといった状態があります。 これについては別スライドができるくらいの 情報量がありますので、 解説されたQiitaページの紹介を。 RxのHotとColdについて http://qiita.com/toRisouP/items/f6088963037bfda658d3 Hot/Cold
  110. 110. SelectやWhereなど、LINQでよく見るオペレータは ひと通りあるイメージで問題ありません。 その他Rx独自のオペレータについては 以下のサイトなどをご活用ください。 http://rxmarbles.com/ https://msdn.microsoft.com/en- us/library/system.reactive.linq.observable_methods(v=vs.103). aspx http://www.slideshare.net/okazuki0130/reactive-extensionsv01 その他ストリームに対するオペレータ
  111. 111. UniRxの作者様のブログ 株式会社グラニ 河合 宜文 様 @neuecc http://neue.cc/category/programming/rx その他 http://blog.xin9le.net/entry/rx-intro http://qiita.com/toRisouP その他お勧めな解説サイト
  112. 112. アジェンダ ・(Uni)Rxとは ・Rxの利点と問題点 ・UniRxの導入いろは ・デバッグ方法 ・まとめ
  113. 113. デバッグ方法
  114. 114. Doというオペレータを使用します。 s.Do( x => Debug.Log( x ) ) ストリームの途中の値(到達)を確認する
  115. 115. Finallyというオペレータを使用します。 s.TakeUntil(...) .Finally( () => Debug.Log( “” ) ) .RepeatUntilDestroy() Repeatしたのかどうかを確認する
  116. 116. ストリームという性質上できません。 ステップ実行
  117. 117. アジェンダ ・(Uni)Rxとは ・Rxの利点と問題点 ・UniRxの導入いろは ・デバッグ方法 ・まとめ
  118. 118. まとめ
  119. 119. 多分皆さんが (過去の私も) 思っているほど 難しくないです
  120. 120. なので
  121. 121. ぜひ導入してみてください!
  122. 122. 質疑応答
  123. 123. ご静聴 ありがとう ございました。

×