Slide 1

Slide 1 text

UnityRx - Reactive Extensions for Unity 2014/04/19 Yoshifumi Kawai - @neuecc

Slide 2

Slide 2 text

Self Introduction @仕事 株式会社グラニ 取締役CTO C# 5.0 + .NET Framework 4.5 + ASP.NET MVC 5 最先端C#によるハイパフォーマンスWebアプリケーション @個人活動 Microsoft MVP for Visual C# Web http://neue.cc/ Twitter @neuecc linq.js - http://linqjs.codeplex.com/ とか作ってます

Slide 3

Slide 3 text

Unity Game Engine but Not Only for Game Use

Slide 4

Slide 4 text

Unityとは 押しも押されぬゲームエンジン http://japan.unity3d.com/ 最近は公式に3Dだけじゃなく2Dもサポートされた クロスプラットフォーム(PC/iOS/Android/Windows Phone/ Windows Store/etc...) 国内でも事例多数(iOS版ドラクエ8などなど) C#で書ける! ※但しバージョン的には3.0相当

Slide 5

Slide 5 text

ゲーム以外の用途にも 映像の出力先としてのUnity VR(Oculus Rift)やNUI(Kinect, Leap Motion)などデバイスがあっ ても、何に表示するの?何を表現するの? そこの最有力候補としてのUnity 強力な開発環境、3D表現、AssetStore、そしてシェア 多くのデバイスがUnity用のSDKを用意している メディアアート ProcessingやopenFrameworksなどのフィールドへ (期待)

Slide 6

Slide 6 text

Async in Unity Coroutine is not good practice for asynchronous operation

Slide 7

Slide 7 text

Unityの非同期 通信処理はyield returnで待機できる コールバック地獄にならない! すばら!すばら! と思ったことは一度もありません:) IEnumerator OnMouseDown() { var www = new WWW("http://bing.com/"); yield return www; // これで待機 Debug.Log(www.text); }

Slide 8

Slide 8 text

yieldの問題点 戻り値が返せない 例外処理ができない IEnumerator GetGoogle() { var www = new WWW("http://google.com/"); yield return www; // 戻り値を返せない!!! www.text is どこ } void OnMouseDown() { // このコードはコンパイルエラー // yieldはtry-catchで囲めないので例外処理できない! try { yield return StartCoroutine(GetGoogle()); } catch { } } これにより処理が分離できないという問題が 発生する。一つのIEnumeratorにダラダラと書 き連ねるしかなくなってしまう

Slide 9

Slide 9 text

あるいはコールバック IEnumerator GetGoogle(Action onCompleted, Action onError) { var www = new WWW("http://google.com/"); yield return www; if (!www.error) onError(new Exception(www.error)); else onCompleted(www.text); } IEnumerator OnMouseDown() { string result; Exception error; yield return StartCoroutine(GetGoogle(x => result = x, x => error = x)); string result2; Exception error2; yield return StartCoroutine(GetGoogle(x => result2 = x, x => error2 = x)); } 結局コールバック地獄かよ! (yieldで完了待機できるので多少マシだけどとはいえ酷い)

Slide 10

Slide 10 text

C# 5.0(async/await)なら? async Task RunAsync() { try { var v = await new HttpClient().GetStringAsync("http://google.co.jp/"); var v2 = await new HttpClient().GetStringAsync("http://google.co.jp/"); return v + v2; } catch (Exception ex) { Debug.WriteLine(ex); throw; } } yield(await)が戻り値を返して受け取れる 例外がtry-catchできる 非同期メソッドが戻り値を返せる

Slide 11

Slide 11 text

Unity 5.0 2014年秋ぐらいに多分リリース! Monoのバージョンは新しくなりません! つまりC#も古い(3.0)のままです! asyncは来ない!知ってた!この先も期待できない! Unity Feedback – Upgrade to Mono 3.0 http://feedback.unity3d.com/suggestions/upgrade-to-mono-30 Mono 3.0でC# 5.0フィーチャー入る。Voteしよう。 Vote数的に、どーもUnityコミュニティ的にも中の人達的にも、言語 あんま重要視してない気配がする……

Slide 12

Slide 12 text

Reactive Programming

Slide 13

Slide 13 text

Reactive Programming #とは 近年注目のアーキテクチャスタイル 昔からあったといえばあったけど、原理原則やライブラリが充実し てきたので、最近一気に華開いた感ある The Rective Manifesto http://www.reactivemanifesto.org/ Reactive Streams http://www.reactive-streams.org/ Principles of Reactive Programming https://www.coursera.org/course/reactive Martin Odersky(Creator of Scala) Eric Meijer(Creator of Reactive Extensions) Roland Kuhn(Akka Tech Lead)

Slide 14

Slide 14 text

Gartner’s Hype Cycle 2013 Application Architecture/Application Development On the Rise - Reactive Programming

Slide 15

Slide 15 text

Reactive Extensions .NETでのReactive Programmingライブラリ https://rx.codeplex.com Microsoftのプロジェクトだったけど現在はOSS化している LINQスタイルでイベントや非同期が処理できる 他言語への移植 RxJava(RxNetよりむしろ注目されてるぐらい) https://github.com/Netflix/RxJava 2070 Stars RxJS(Microsoft自身によるもの) https://github.com/Reactive-Extensions/RxJS 1021 Stars

Slide 16

Slide 16 text

UnityRx - Reactive Extensions for Unity というのを作りました、本日公開! ほとんど(8割ぐらい)は自前で書いた 公式のRxはヘヴィすぎてUnityの古いC#で動かしきれない iOSはAOTの問題もあって回避頑張れる気がしない ので自前で書くことに。 Available Now(?) GitHub - https://github.com/neuecc/UnityRx/ AssetStore – 準備中(数日中に出すので待ってて!) 無料!

Slide 17

Slide 17 text

Curing Your Asynchronous Programming Blues Rx saves your life & codes

Slide 18

Slide 18 text

RxによるUnityの非同期通信 // xが完了したらそれでy、完了したらzのダウンロードの連鎖のフローをLINQクエリ式で var query = from x in ObservableWWW.Get("http://google.co.jp/") from y in ObservableWWW.Get(x) from z in ObservableWWW.Get(y) select new { x, y, z }; // Subscribe = "最後に全部まとまったあとの"コールバック(ネストしないから処理が楽) query.Subscribe(x => Debug.Log(x), ex => Debug.LogException(ex)); // もしくはCoroutineに変換して待機も可能(ToCoroutine is yieldable!) yield return StartCoroutine(query.Do(x => Debug.Log(x)).ToCoroutine());

Slide 19

Slide 19 text

etc, etc.... // AとBを同時並列に走らせて一個にまとめる var query = Observable.Zip( ObservableWWW.Get("http://google.co.jp/"), ObservableWWW.Get("http://bing.com/"), (google, bing) => new { google, bing }); // エラーが起きたらリトライ処理を入れる var cancel = ObservableWWW.Get("http://hogehgoe") .OnErrorRetry((Exception ex) => Debug.LogException(ex), retryCount: 3, delay: TimeSpan.FromSeconds(1)) .Subscribe(Debug.Log); // キャンセルしたい場合は戻り値のDisposeを呼ぶだけ cancel.Dispose(); // などなど、100近くの演算子をメソッドチェーン形式で繋げることができる // あらゆる実行フローを完全にコントロールできる ところでObservableWWWは、UnityRxに同梱しているRxの形式 にラップ済みのWWWクラスで通信可能なメソッドです

Slide 20

Slide 20 text

Orchestrate MultiThreading and LINQ to MonoBehaviour Message Events

Slide 21

Slide 21 text

UnityRx solves MultiThreading problems // なんか重たい処理を別スレッドで開始するStartメソッド var heavyMethod1 = Observable.Start(() => { Thread.Sleep(TimeSpan.FromSeconds(3)); return 1; }); var heavyMethod2 = Observable.Start(() => { Thread.Sleep(TimeSpan.FromSeconds(3)); return 1; }); // Zipで両方並列に走らせながら連結して heavyMethod1.Zip(heavyMethod2, (x, y) => new { x, y }) .ObserveOnMainThread() // MainThreadに戻す! .Subscribe(x => { (GameObject.Find("myGuiText")).guiText.text = x.ToString(); }); 他スレッドと待ち合わせ MainThreadに戻して、そ れ以降のフローは GameObjectにアクセス可 能にする ObserveOnMainThread Subscribe後の戻り値を Disposeするだけでキャン セル可能、などなど、多種 のメソッドがマルチスレッ ド処理を支援する

Slide 22

Slide 22 text

AsyncA AsyncB SelectMany - 直列の結合 AsyncA Zip - 並列の結合 AsyncB Result

Slide 23

Slide 23 text

AsyncA AsyncB AsyncC Result AsyncA().SelectMany(a => AsyncB(a)) .Zip(AsyncC(), (b, c) => new { b, c }); SelectMany + Zip - 合成の例

Slide 24

Slide 24 text

var asyncQuery = from a in AsyncA() from b in AsyncB(a) from c in AsyncC(a, b) select new { a, b, c }; 多重from(SelectMany) AsyncA AsyncB AsyncC Result

Slide 25

Slide 25 text

Extra Gems

Slide 26

Slide 26 text

Unity用の各支援メソッド // ObservableMonoBehaviour public class Hoge : ObservableMonoBehaviour { public override void Awake() { // 全てのMessageEventがRxで扱う形式で取得できる var query = this.OnMouseDownAsObservable() .SelectMany(_ => this.OnMouseDragAsObservable()) .TakeUntil(this.OnMouseUpAsObservable()); } } // Observable.EveryFrame // 毎フレーム毎に値を発行する Observable.EveryFrame() .Subscribe(_ => Debug.Log(DateTime.Now.ToString()));

Slide 27

Slide 27 text

Unity用の各支援メソッド // 入れ物を用意して public class LogCallback { public string Condition; public string StackTrace; public UnityEngine.LogType LogType; } public static class LogHelper { public static IObservable LogCallbackAsObservable() { var subject = new Subject(); // callback内でSubjectに発行してあげるように作ることでRxにコンバート可能 UnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) => { subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, Log }); return subject; } } Unityに多くあるデリゲートによるコールバックを IObservableに変換するとかなりイケテル!

Slide 28

Slide 28 text

Unity用の各支援メソッド // 入れ物を用意して public class LogCallback { public string Condition; public string StackTrace; public UnityEngine.LogType LogType; } public static class LogHelper { public static IObservable LogCallbackAsObservable() { var subject = new Subject(); // callback内でSubjectに発行してあげるように作ることでRxにコンバート可能 UnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) => { subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, Log }); return subject; } } // 各処理が分離して記述可能になる、また合成処理が可能、などのメリットあり LogHelper.LogCallbackAsObservable() .Where(x => x.LogType == LogType.Warning) .Subscribe(); LogHelper.LogCallbackAsObservable() .Where(x => x.LogType == LogType.Error) .Subscribe(); Unityに多くあるデリゲートによるコールバックを IObservableに変換するとかなりイケテル!

Slide 29

Slide 29 text

センサー is IObservable IObservableは時間軸上に並ぶシーケンス 詳しくはググッて私の適当な記事読んでください http://www.atmarkit.co.jp/fdotnet/introrx/index/ Kinect, Leap Motionなどなど は、イベントシーケンスとみなせてRxと相性が非常にいい! というわけでUnityRxで扱ってみてください まだメソッド足りないかもなのでIssueに入れてもらうとうれすぃ

Slide 30

Slide 30 text

Conclusion

Slide 31

Slide 31 text

まとめ IEnumeratorを非同期処理に使うのはいけてない そしてC# 5.0(async/await)は来ない! そこでUnityRx なぜTaskじゃなくてRxなのか? Taskはawaitがなければ機能的に弱くて使いやすくはない マルチスレッド処理やイベント処理など多数の機能も! Available Now(?) GitHub - https://github.com/neuecc/UnityRx/ Assets/UnityRx下のコードが本体なのでそれを持って来れば動く AssetStore – 準備中(数日中に出すので待ってて!) 無料!