Slide 1

Slide 1 text

UniRx とか ReactiveProperty とか 2015/03/21 Unity のための C# 勉強会 Manato KAMEYA (@Grabacr07)

Slide 2

Slide 2 text

Self Introduction Work 亀谷学人 – 株式会社グラニ C# + Unity でソーシャルゲーム開発 Private Microsoft MVP for .NET (Visual C#) Room metro Tokyo staff (Windows クライアント開発 勉強会) Twitter: @Grabacr07 Facebook: manato.kameya Blog: http://grabacr.net/ 最先端の C# をフルに活用 CTO: @neuecc (UniRx 開発者)

Slide 3

Slide 3 text

C# Everywhere Windows Desktop WinForms, WPF Windows 8 Windows Universal apps Mac application Xamarin.Mac Web application ASP .NET MVC, ASP .NET Web API Game Unity, Paradox, Unreal Engine Mobile (iOS, Android, WP) MonoTouch, Mono for Android Embedded, IoT .NET Framework Embedded Cloud Windows Azure, AWS

Slide 4

Slide 4 text

Topics Unity 製ゲーム開発 Unity 5.0 + uGUI + UniRx 社内 UniRx ハンズオン (勉強会? ハッカソン?) 的なことをやったり その内容を共有します! Goal Rx と UniRx を知ってもらいたい!

Slide 5

Slide 5 text

Reactive Extensions (Rx) LINQ to Events LINQ が扱うデータ ソースの概念を「イベント」に広げる LINQ to Objects • Array • List • Stream • etc… LINQ to XML • XML • JSON LINQ to Events • User (input) • Sensor device (input) • Web service (notification) • etc... あらゆるものがイベント

Slide 6

Slide 6 text

イベントを、時間軸に乗ったシーケンスと見なす イベント ストリームにメッセージが流れてくる LINQ で使える時属性魔法 (Where でフィルター、Select で射影 (変換)、etc…) 時間をベースにした処理 (計測、非同期、イベントの合成、etc…) Event stream Reactive Extensions (Rx) IObservable onClick time onClick (3 秒後) onClick (5 秒後) onClick (1 秒後)

Slide 7

Slide 7 text

Reactive Extensions (Rx) for .NET Microsoft の DevLabs で 2009 年に公開されたプロジェクト Across Languages その後、多くの言語向けに移植されていく RxJS, bacon.js, RxJava, RxScala, ReactiveCocoa, Rx.rb, etc… つまり今アツい分野!

Slide 8

Slide 8 text

UniRx Reactive Extensions for Unity https://github.com/neuecc/UniRx 弊社 CTO、@neuecc が Unity 向けに移植した Rx オレオレライブラリではない (重要!) 言語は違っても、概念は同じ つまり、覚えておけば他の言語でも使える!

Slide 9

Slide 9 text

Study 勉強用の資料 & 解説ページ等 Reactive Programming by UniRx for Asynchronous & Event Processing 開発者、弊社 CTO @neuecc による Rx + UniRx 解説資料 Rx とは? なぜ Unity で Rx が良いのか? というのが解ります http://www.slideshare.net/neuecc/reactive-programming-by-unirxfor-asynchronous-event-processing 未来のプログラミング技術をUnityで ~UniRx~ @toRisouP 氏による Rx + UniRx 解説資料 サンプル・図解が多く、めっちゃわかりやすい (おすすめ!) http://www.slideshare.net/torisoup/unity-unirx

Slide 10

Slide 10 text

Study ReactiveX Rx の概念や、Observable、各 Operator の動作等を図解付きで詳しく解説したサイト (英語) http://reactivex.io/ Reactive extensions入門 @okazuki 氏による Rx 解説資料 http://www.slideshare.net/okazuki0130/reactive-extensionsv01 Rx入門 @xin9le 氏による Rx 解説ページ http://blog.xin9le.net/entry/rx-intro

Slide 11

Slide 11 text

Reactive Extensions + Unity + uGUI (Unity UI) UniRx

Slide 12

Slide 12 text

Enable / Disable ストリームの射影 (変換と言った方がわかりやすいかも) 入力ボックスが空だったら検索ボタンを押せなくする UI

Slide 13

Slide 13 text

Enable / Disable ストリームの射影 public class EnableDisablePresenter : MonoBehaviour { public Button SearchButton; public InputField WordInput; private void Start() { this.WordInput .OnValueChangeAsObservable() .Select(x => !string.IsNullOrEmpty(x)) .SubscribeToInteractable(this.SearchButton); } }

Slide 14

Slide 14 text

Enable / Disable ストリームの射影 public class EnableDisablePresenter : MonoBehaviour { public Button SearchButton; public InputField WordInput; private void Start() { this.WordInput .OnValueChangeAsObservable() .Select(x => !string.IsNullOrEmpty(x)) .SubscribeToInteractable(this.SearchButton); } } OnValueChangeAsObservable() で UniRx のイベント ストリーム (IObseravable) にする

Slide 15

Slide 15 text

Enable / Disable ストリームの射影 public class EnableDisablePresenter : MonoBehaviour { public Button SearchButton; public InputField WordInput; private void Start() { this.WordInput .OnValueChangeAsObservable() .Select(x => !string.IsNullOrEmpty(x)) .SubscribeToInteractable(this.SearchButton); } } OnValueChangeAsObservable() で UniRx のイベント ストリーム (IObseravable) にする Select() で射影 (というか変換) string から bool へ (空文字なら false、それ以外は true) IObservable な On/Off ストリームにする

Slide 16

Slide 16 text

Enable / Disable ストリームの射影 public class EnableDisablePresenter : MonoBehaviour { public Button SearchButton; public InputField WordInput; private void Start() { this.WordInput .OnValueChangeAsObservable() .Select(x => !string.IsNullOrEmpty(x)) .SubscribeToInteractable(this.SearchButton); } } OnValueChangeAsObservable() で UniRx のイベント ストリーム (IObseravable) にする Select() で射影 (というか変換) string から bool へ (空文字なら false、それ以外は true) IObservable な On/Off ストリームにする Subscribe() でストリームの購読 On/Off ストリームからの interactable 設定は SubscribeToInteractable() が便利

Slide 17

Slide 17 text

ストリームの射影 Enable / Disable OnValueChangeAsObservable() InputField (WordInput) の値が変更されると配信 Select(x => !string.IsNullOrEmpty(x)) string を bool に射影 false Subscribe() イベント購読 true false this.WordInput .OnValueChangeAsObservable() .Select(x => !string.IsNullOrEmpty(x)) .SubscribeToInteractable(this.SearchButton); (空文字) IObservable から IObservable へ True/False ストリーム (= On/Off ストリーム) に変換 "h" true "ho" "hog" true (空文字) true "foo"

Slide 18

Slide 18 text

Enable / Disable ストリームのフィルター public class EnableDisablePresenter : MonoBehaviour { public Button SearchButton; public InputField WordInput; private void Start() { this.WordInput .OnValueChangeAsObservable() .Select(x => !string.IsNullOrEmpty(x)) .DistinctUntilChanged() .SubscribeToInteractable(this.SearchButton); } }

Slide 19

Slide 19 text

Enable / Disable ストリームのフィルター public class EnableDisablePresenter : MonoBehaviour { public Button SearchButton; public InputField WordInput; private void Start() { this.WordInput .OnValueChangeAsObservable() .Select(x => !string.IsNullOrEmpty(x)) .DistinctUntilChanged() .SubscribeToInteractable(this.SearchButton); } } DistinctUntilChanged() でフィルター 同じ値が連続している場合は無視する

Slide 20

Slide 20 text

ストリームのフィルター Enable / Disable OnValueChangeAsObservable() InputField (WordInput) の値が変更されると配信 Select(x => !string.IsNullOrEmpty(x)) string を bool に射影 false Subscribe() イベント購読 true false this.WordInput .OnValueChangeAsObservable() .Select(x => !string.IsNullOrEmpty(x)) .DistinctUntilChanged() .SubscribeToInteractable(this.SearchButton); (空文字) "h" true "ho" "hog" true (空文字) true "foo" DistinctUntilChanged() 同じ値が連続している場合は無視する false true false true 連続した値は配信されない

Slide 21

Slide 21 text

Simple Calculation ストリームの結合 シンプルな足し算をする UI

Slide 22

Slide 22 text

Simple Calculation ストリームの結合 public class CalculatorPresenter : MonoBehaviour { public InputField Left; public InputField Right; public Text Result; void Start() { var left = this.Left .OnValueChangeAsObservable() .Select(x => int.Parse(x)); var right = this.Right .OnValueChangeAsObservable() .Select(x => int.Parse(x)); left.CombineLatest(right, (l, r) => l + r) .SubscribeToText(this.Result); } }

Slide 23

Slide 23 text

Simple Calculation ストリームの結合 public class CalculatorPresenter : MonoBehaviour { public InputField Left; public InputField Right; public Text Result; void Start() { var left = this.Left .OnValueChangeAsObservable() .Select(x => int.Parse(x)); var right = this.Right .OnValueChangeAsObservable() .Select(x => int.Parse(x)); left.CombineLatest(right, (l, r) => l + r) .SubscribeToText(this.Result); } } OnValueChangeAsObservable() で UniRx のイベント ストリーム (IObseravable) にする

Slide 24

Slide 24 text

Simple Calculation ストリームの結合 public class CalculatorPresenter : MonoBehaviour { public InputField Left; public InputField Right; public Text Result; void Start() { var left = this.Left .OnValueChangeAsObservable() .Select(x => int.Parse(x)); var right = this.Right .OnValueChangeAsObservable() .Select(x => int.Parse(x)); left.CombineLatest(right, (l, r) => l + r) .SubscribeToText(this.Result); } } OnValueChangeAsObservable() で UniRx のイベント ストリーム (IObseravable) にする InputField の値は文字列 (string) で配信されるので 整数値 (int) に変換する

Slide 25

Slide 25 text

Simple Calculation ストリームの結合 public class CalculatorPresenter : MonoBehaviour { public InputField Left; public InputField Right; public Text Result; void Start() { var left = this.Left .OnValueChangeAsObservable() .Select(x => int.Parse(x)); var right = this.Right .OnValueChangeAsObservable() .Select(x => int.Parse(x)); left.CombineLatest(right, (l, r) => l + r) .SubscribeToText(this.Result); } } OnValueChangeAsObservable() で UniRx のイベント ストリーム (IObseravable) にする CombineLatest() で 2 つの IObservable を結合 left と right いずれかのイベントが配信されたとき、 互いの最後の値を使って 1 つのイベントを配信する (ただし、両方揃ってからしか配信されない) InputField の値は文字列 (string) で配信されるので 整数値 (int) に変換する

Slide 26

Slide 26 text

ストリームの結合 Left Simple Calculation OnValueChangeAsObservable() InputField の値が変更されると配信 Select(x => int.Parse(x)) string を int に変換 "0" 0 var left = this.Left .OnValueChangeAsObservable() // Left (InputField) の値が変更されるとイベント配信 .Select(x => int.Parse(x)); // 信された入力値 (x) は文字列なので、整数値に変換 "12" 12 "7" 7 ユーザーのテキスト入力

Slide 27

Slide 27 text

ストリームの結合 Left Simple Calculation OnValueChangeAsObservable() InputField の値が変更されると配信 Select(x => int.Parse(x)) string を int に変換 "0" 0 Right OnValueChangeAsObservable() InputField の値が変更されると配信 Select(x => int.Parse(x)) string を int に変換 "25" 25 "12" 12 "7" 7 "0" 0 "6" 6 "3" 3 CombineLatest(left, right) 互いの最後の値を結合して配信 7+3 0+0 12+0 12+6 12+3 7+25

Slide 28

Slide 28 text

ストリームの結合 Left Simple Calculation OnValueChangeAsObservable() InputField の値が変更されると配信 Select(x => int.Parse(x)) string を int に変換 "0" 0 Right OnValueChangeAsObservable() InputField の値が変更されると配信 Select(x => int.Parse(x)) string を int に変換 "25" 25 "12" 12 "7" 7 "0" 0 "6" 6 "3" 3 CombineLatest(left, right) 互いの最後の値を結合して配信 7+3 0+0 12+0 12+6 12+3 7+25 CombineLatest たとえば left ストリームから 12 が配信されたとき、 left の最新値 12 と right の最新値 0 を Combine する

Slide 29

Slide 29 text

uGUI Integration UnityEvent をお手軽ハンドリング Button.OnClickAsObservable Slider.OnValueChangeAsObservable Toggle.OnValueChangeAsObservable InputField.OnEndEditAsObservable IObservable.SubscribeToText IObservable.SubscribeToInteractable this.MyButton .OnClickAsObservable() .Subscribe(_ => Debug.Log("clicked!")); this.MyInput .OnEndEditAsObservable() .SubscribeToText(this.MyText); 文字列ストリームや On/Off ストリームを Subscribe して UI に反映 (頻出パターン) UnityEvent を IObservable の イベント ストリームにする

Slide 30

Slide 30 text

UnityEvent to IObservable OnValueChangeAsObservable() と onValueChange.AsObservable() は何が違うのか? uGUI Integration var left = this.Left .OnValueChangeAsObservable() .Select(x => int.Parse(x)); // どっちがいいの? var left = this.Left.onValueChange .AsObservable() .Select(x => int.Parse(x));

Slide 31

Slide 31 text

ストリームの結合 (onValueChange の場合) Left Simple Calculation onValueChange.AsObservable() InputField の値が変更されると配信 Select(x => int.Parse(x)) string を int に変換 Right onValueChange.AsObservable() InputField の値が変更されると配信 Select(x => int.Parse(x)) string を int に変換 "25" 25 "12" 12 "7" 7 "6" 6 "3" 3 CombineLatest(left, right) 互いの最後の値を結合して配信 7+3 12+6 12+3 7+25 "0" 0 "0" 0 0+0 12+0

Slide 32

Slide 32 text

ストリームの結合 (onValueChange の場合) Left Simple Calculation onValueChange.AsObservable() InputField の値が変更されると配信 Select(x => int.Parse(x)) string を int に変換 Right onValueChange.AsObservable() InputField の値が変更されると配信 Select(x => int.Parse(x)) string を int に変換 "25" 25 "12" 12 "7" 7 "6" 6 "3" 3 CombineLatest(left, right) 互いの最後の値を結合して配信 7+3 12+6 12+3 7+25 "0" 0 "0" 0 0+0 12+0 初期値が配信されない 両方揃うまで配信されない (最初の配信がこのタイミングになる)

Slide 33

Slide 33 text

UnityEvent to IObservable OnValueChangeAsObservable() と onValueChange.AsObservable() は何が違うのか? uGUI Integration var left = this.Left .OnValueChangeAsObservable() .Select(x => int.Parse(x)); // どっちがいいの? var left = this.Left.onValueChange .AsObservable() .Select(x => int.Parse(x)); Control.XxxAsObservable() ったら最強ね! • イベント ストリームをお手軽生成 • UnityEvent.AsObservable() と違い、初期値を供給 → 宣言的な UI を作る上で重要な機能 初期値が供給されるかどうかの違いがある

Slide 34

Slide 34 text

というものを試験運用中 MV(R)P Pattern

Slide 35

Slide 35 text

Model-View-(Reactive)Presenter Unity における M-V-○○ パターン View = Scene, Hierarchy どうやって View を更新するか? Passive View ??? Model updates view state-change events user events update model

Slide 36

Slide 36 text

Model-View-(Reactive)Presenter Unity における M-V-○○ パターン View = Scene, Hierarchy どうやって View を更新するか? View (XAML) ViewModel Model 例えば XAML Platform なら

Slide 37

Slide 37 text

Model-View-(Reactive)Presenter Unity における M-V-○○ パターン View = Scene, Hierarchy どうやって View を更新するか? View (XAML) ViewModel Model INotifyPropertyCanged INotifyPropertyCanged 例えば XAML Platform なら DataBinding system + INotifyProperyChanged をベースとした通知基盤がある ICommand method call

Slide 38

Slide 38 text

Model-View-(Reactive)Presenter Unity における M-V-○○ パターン View = Scene, Hierarchy どうやって View を更新するか? Unity にデータ バインディングは無い 複雑だし、パフォーマンス的にも作りたくない View (XAML) ViewModel Model INotifyPropertyCanged INotifyPropertyCanged 例えば XAML Platform なら DataBinding system + INotifyProperyChanged をベースとした通知基盤がある ICommand method call

Slide 39

Slide 39 text

Reactive Property 通知可能なプロパティ 値の変更を IObservable で配信できる public class Enemy { public ReactiveProperty CurrentHp { get; private set; } public ReactiveProperty IsDead { get; private set; } public Enemy(int initialHp) { // Declarative Property this.CurrentHp = new ReactiveProperty(initialHp); this.IsDead = this.CurrentHp.Select(x => x <= 0).ToReactiveProperty(); } } コンストラクターで宣言的に書く IsDead がどういう動作をするか ( = CurrentHP がどう作用するか) 見て一発でわかりますよね?

Slide 40

Slide 40 text

Model-View-(Reactive)Presenter Unity における M-V-○○ パターン View = Scene, Hierarchy どうやって View を更新するか? UniRx が可能にする MV(R)P View を操作する (Reactive)Presenter バインディングがない以上、誰かが View を知らなければならない ReactiveProperty による通知 Passive View Presenter (Supervising Controller) Model updates view state-change events user events update model

Slide 41

Slide 41 text

Model-View-(Reactive)Presenter IObservable で繋がる M-V-(R)P Passive View Presenter (Supervising Controller) Model updates view state-change events user events update model UIControl.XxxAsObservable UnityEvent.AsObservable ObservableEventTrigger Subscribe ToReactiveProperty ReactiveProperty Subscribe SubscribeToText SubscribeToInteractable

Slide 42

Slide 42 text

Count up / down UniRx (+ Reactive Property) を使って MV(R)P …の、極小サンプルとしてひとつ ・Up ボタンで数値 + 1 ・Down ボタンで数値 -1

Slide 43

Slide 43 text

Count up / down UniRx (+ Reactive Property) を使って MV(R)P // Presenter public class UpDownPresenter : MonoBehaviour { // View public Button UpButton; public Button DownButton; public Text ScoreText; // Model [InspectorDisplay] public IntReactiveProperty Score; private void Start() { this.UpButton.OnClickAsObservable().Subscribe(_ => this.Score.Value++); this.DownButton.OnClickAsObservable().Subscribe(_ => this.Score.Value--); this.Score.SubscribeToText(this.ScoreText); } } Passive View Presenter (Supervising Controller) Model updates view state-change events user events update model

Slide 44

Slide 44 text

Count up / down UniRx (+ Reactive Property) を使って MV(R)P // Presenter public class UpDownPresenter : MonoBehaviour { // View public Button UpButton; public Button DownButton; public Text ScoreText; // Model [InspectorDisplay] public IntReactiveProperty Score; private void Start() { this.UpButton.OnClickAsObservable().Subscribe(_ => this.Score.Value++); this.DownButton.OnClickAsObservable().Subscribe(_ => this.Score.Value--); this.Score.SubscribeToText(this.ScoreText); } } Passive View Presenter (Supervising Controller) Model updates view state-change events user events update model View から Presenter へ user events を配信 (onClick など)

Slide 45

Slide 45 text

Count up / down UniRx (+ Reactive Property) を使って MV(R)P // Presenter public class UpDownPresenter : MonoBehaviour { // View public Button UpButton; public Button DownButton; public Text ScoreText; // Model [InspectorDisplay] public IntReactiveProperty Score; private void Start() { this.UpButton.OnClickAsObservable().Subscribe(_ => this.Score.Value++); this.DownButton.OnClickAsObservable().Subscribe(_ => this.Score.Value--); this.Score.SubscribeToText(this.ScoreText); } } Passive View Presenter (Supervising Controller) Model updates view state-change events user events update model user events を受信した Presenter が Model (Score) を update

Slide 46

Slide 46 text

Count up / down UniRx (+ Reactive Property) を使って MV(R)P // Presenter public class UpDownPresenter : MonoBehaviour { // View public Button UpButton; public Button DownButton; public Text ScoreText; // Model [InspectorDisplay] public IntReactiveProperty Score; private void Start() { this.UpButton.OnClickAsObservable().Subscribe(_ => this.Score.Value++); this.DownButton.OnClickAsObservable().Subscribe(_ => this.Score.Value--); this.Score.SubscribeToText(this.ScoreText); } } Passive View Presenter (Supervising Controller) Model updates view state-change events user events update model Model が state-change event を配信 (ReactiveProperty のイベント ストリーム)

Slide 47

Slide 47 text

Count up / down UniRx (+ Reactive Property) を使って MV(R)P // Presenter public class UpDownPresenter : MonoBehaviour { // View public Button UpButton; public Button DownButton; public Text ScoreText; // Model [InspectorDisplay] public IntReactiveProperty Score; private void Start() { this.UpButton.OnClickAsObservable().Subscribe(_ => this.Score.Value++); this.DownButton.OnClickAsObservable().Subscribe(_ => this.Score.Value--); this.Score.SubscribeToText(this.ScoreText); } } Passive View Presenter (Supervising Controller) Model updates view state-change events user events update model state-change event を受信した Presenter が View (ScoreText) を update

Slide 48

Slide 48 text

Model-View-(Reactive)Presenter UniRx (+ Reactive Property) を使って MV(R)P というものを試験的に導入し開発しています 試験的というかもうガッツリ書き始めました 実際、UniRx にピッタリのパターン UniRx readme: https://github.com/neuecc/UniRx#model-view-reactivepresenter-pattern 是非 Reactive Property と共に導入し、フィードバックして頂けると助かります

Slide 49

Slide 49 text

Conclusion

Slide 50

Slide 50 text

UniRx UniRx を使ってみてください オレオレライブラリではない! 学習コストは無駄にならない (多言語で使える概念) 時間や回数の取り扱いがとても楽に 今日のはほんの触りだけで、本気出した Rx はこんなもんじゃないです! Throttle とか Buffer とか、強力な Operator がたくさんあります

Slide 51

Slide 51 text

Reactive UI Script を書く前提の環境 Inspector での onClick 紐づけ等は無視してます (ノンコーディングでやりたい人向け? うちではあんまりやってない…) Control.XxxAsObservable ったら最強ね! OnClick, OnValueChange, … イベント ストリームお手軽生成 初期値の供給 (UnityEvent.AsObservable と挙動が違う)

Slide 52

Slide 52 text

Model-View-(Reactive)Presenter IObservable で繋がる M-V-(R)P Passive View Presenter (Supervising Controller) Model updates view state-change events user events update model Subscribe ToReactiveProperty ReactiveProperty UIControl.XxxAsObservable UnityEvent.AsObservable ObservableEventTrigger Subscribe SubscribeToText SubscribeToInteractable

Slide 53

Slide 53 text

Study 勉強用の資料 & 解説ページ等 (再掲) Reactive Programming by UniRx for Asynchronous & Event Processing 開発者、弊社 CTO @neuecc による Rx + UniRx 解説資料 Rx とは? なぜ Unity で Rx が良いのか? というのが解ります http://www.slideshare.net/neuecc/reactive-programming-by-unirxfor-asynchronous-event-processing 未来のプログラミング技術をUnityで ~UniRx~ @toRisouP 氏による Rx + UniRx 解説資料 サンプル・図解が多く、めっちゃわかりやすい (おすすめ!) http://www.slideshare.net/torisoup/unity-unirx

Slide 54

Slide 54 text

Study ReactiveX Rx の概念や、Observable、各 Operator の動作等を図解付きで詳しく解説したサイト (英語) http://reactivex.io/ Reactive extensions入門 @okazuki 氏による Rx 解説資料 http://www.slideshare.net/okazuki0130/reactive-extensionsv01 Rx入門 @xin9le 氏による Rx 解説ページ http://blog.xin9le.net/entry/rx-intro