UniRx - Reactive Extensions for Unity

43,892 views
46,214 views

Published on

Reactive Extensions for Unity
https://github.com/neuecc/UniRx

Published in: Technology, Spiritual
3 Comments
43 Likes
Statistics
Notes
  • @Yoshifumi Kawai なるほど!! お早い返答ありがとうございます。ゲーム系だとgetでどうにかなる事が多いのですが業務系でもunityが使えそうだなと思い試行錯誤中でした。SSL通信の証明書問題とか、かなり標準ライブラリー(& free license)だと難しいことが多い中、このライブラリーに出会えて大変感謝しています。私も成果物を広くフィードバックできるように頑張りたいと思います。有り難うございました。m(_ _)m
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • @正吉 上原 ありがとうございます。なるほど、そうですね、これはちょっと不便で分かりづらい挙動ですね……。 WWWは一個目が完了したらDisposeを内部で呼んでしまっているので、 var query = from x in ObservableWWW.PostWWW (URL1, formData).Select(x => x.text) from y in ObservableWWW.PostWWW (URL2, formData2).Select(y => y.text) select new { x, y }; などのように、直後にSelectを繋げてWWWから必要な情報を取り出したものをそこで作る必要がありそうです。
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • 大変素晴らしいライブラリをありがとうございます 質問があるのですが var query = from x in ObservableWWW.PostWWW (URL1, formData) from y in ObservableWWW.PostWWW (URL2, formData2) select new { x, y }; query.Subscribe (z => { Debug.Log(z.x.text); } とした時に NullReferenceException: WWW class has already been disposed. とでてしまうのはなにか回避策はありますでしょうか。 (ObservableWWW.Postだと、エラーにはならない) セッションを対応したく1個目のアクセスでログイン認証をして、 もどりで色々対応して2個目のアクセスをしようと思ったのですが、 最初につまづいてしまいました。 (どのみちこの書き方ではセッション対応じゃないと思いますが、 まずは複数URLアクセスと戻り値からと思いまして。) お手すきの時にお返事をいただければうれしいです。
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
43,892
On SlideShare
0
From Embeds
0
Number of Embeds
32,638
Actions
Shares
0
Downloads
43
Comments
3
Likes
43
Embeds 0
No embeds

No notes for slide

UniRx - Reactive Extensions for Unity

  1. 1. UniRx - Reactive Extensions for Unity 2014/04/19 Yoshifumi Kawai - @neuecc
  2. 2. 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/ とか作ってます
  3. 3. Unity Game Engine but Not Only for Game Use
  4. 4. Unityとは 押しも押されぬゲームエンジン http://japan.unity3d.com/ 最近は公式に3Dだけじゃなく2Dもサポートされた クロスプラットフォーム(PC/iOS/Android/Windows Phone/ Windows Store/etc...) 国内でも事例多数(iOS版ドラクエ8などなど) C#で書ける! ※但しバージョン的には3.0相当
  5. 5. ゲーム以外の用途にも 映像の出力先としてのUnity VR(Oculus Rift)やNUI(Kinect, Leap Motion)などデバイスがあっ ても、何に表示するの?何を表現するの? そこの最有力候補としてのUnity 強力な開発環境、3D表現、AssetStore、そしてシェア 多くのデバイスがUnity用のSDKを用意している メディアアート ProcessingやopenFrameworksなどのフィールドへ (期待)
  6. 6. Async in Unity Coroutine is not good practice for asynchronous operation
  7. 7. Unityの非同期 通信処理はyield returnで待機できる コールバック地獄にならない! すばら!すばら! と思ったことは一度もありません:) IEnumerator OnMouseDown() { var www = new WWW("http://bing.com/"); yield return www; // これで待機 Debug.Log(www.text); }
  8. 8. yieldの問題点 戻り値が返せない 例外処理ができない IEnumerator GetGoogle() { var www = new WWW("http://google.com/"); yield return www; // 戻り値を返せない!!! www.text is どこ } IEnumerator OnMouseDown() { // このコードはコンパイルエラー // yieldはtry-catchで囲めないので例外処理できない! try { yield return StartCoroutine(GetGoogle()); } catch { } } これにより処理が分離できないという問題が 発生する。一つのIEnumeratorにダラダラと書 き連ねるしかなくなってしまう
  9. 9. あるいはコールバック IEnumerator GetGoogle(Action<string> onCompleted, Action<Exception> 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で完了待機できるので多少マシだけどとはいえ酷い)
  10. 10. async Task<string> 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; } } C# 5.0(async/await)なら? yield(await)が戻り値を返して受け取れる 例外がtry-catchできる 非同期メソッドが戻り値を返せる
  11. 11. Unity 5.0 2014年秋ぐらいに多分リリース! Monoのバージョンは新しくなりません! つまりC#も古い(3.0)のままです! asyncは来ない!知ってた!この先も期待できない! The future of scripting in Unity http://blogs.unity3d.com/2014/05/20/the-future-of-scripting-in- unity/ 今まで出遅れていたのを一気に取り戻そう作戦 Future、って感じでまだまだかなり時間かかりそうだ
  12. 12. Reactive Programming
  13. 13. 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)
  14. 14. Gartner’s Hype Cycle 2013 Application Architecture/Application Development On the Rise - Reactive Programming
  15. 15. 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
  16. 16. UniRx - Reactive Extensions for Unity というのを作りました、本日公開! ほとんど(8割ぐらい)は自前で書いた 公式のRxはヘヴィすぎてUnityの古いC#で動かしきれない iOSはAOTの問題もあって回避頑張れる気がしない ので自前で書くことに。 Available Now(?) GitHub - https://github.com/neuecc/UniRx/ AssetStore(無料) http://u3d.as/content/neuecc/uni-rx-reactive-extensions-for- unity/7tT
  17. 17. Curing Your Asynchronous Programming Blues Rx saves your life & codes
  18. 18. 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());
  19. 19. 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は、 UniRxに同梱されている、Rxの 形式にラップ済みのWWWクラ スで通信可能なメソッドです
  20. 20. Orchestrate MultiThreading and LINQ to MonoBehaviour Message Events
  21. 21. UniRx 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するだけでキャン セル可能、などなど、多種 のメソッドがマルチスレッ ド処理を支援する
  22. 22. AsyncA AsyncB SelectMany - 直列の結合 AsyncA Zip - 並列の結合 AsyncB Result
  23. 23. AsyncA AsyncB AsyncC Result AsyncA().SelectMany(a => AsyncB(a)) .Zip(AsyncC(), (b, c) => new { b, c }); SelectMany + Zip - 合成の例
  24. 24. 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
  25. 25. Extra Gems
  26. 26. Unity用の各支援メソッド // Observable.EveryUpdate // 毎フレーム毎に値を発行する Observable.EveryUpdate() .Subscribe(_ => Debug.Log(DateTime.Now.ToString())); // ObservableMonoBehaviour public class Hoge : ObservableMonoBehaviour { public override void Awake() { // 全てのMessageEventがRxで扱う形式で取得できる var query = this.OnMouseDownAsObservable() .SelectMany(_ => this.OnMouseDragAsObservable()) .TakeUntil(this.OnMouseUpAsObservable()); } }
  27. 27. Unity用の各支援メソッド // 入れ物を用意して public class LogCallback { public string Condition; public string StackTrace; public UnityEngine.LogType LogType; } public static class LogHelper { static Subject<LogCallback> subject; public static IObservable<LogCallback> LogCallbackAsObservable() { if (subject == null) { subject = new Subject<LogCallback>(); // callback内でSubjectに発行してあげるように作ることでRxにコンバート可能 UnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) => { subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, LogType = ty }); } return subject.AsObservable(); } } Unityに多くあるデリゲートによるコールバックを IObservable<T>に変換するとかなりイケテル!
  28. 28. Unity用の各支援メソッド // 入れ物を用意して public class LogCallback { public string Condition; public string StackTrace; public UnityEngine.LogType LogType; } public static class LogHelper { static Subject<LogCallback> subject; public static IObservable<LogCallback> LogCallbackAsObservable() { if (subject == null) { subject = new Subject<LogCallback>(); // callback内でSubjectに発行してあげるように作ることでRxにコンバート可能 UnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) => { subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, LogType = ty }); } return subject.AsObservable(); } } Unityに多くあるデリゲートによるコールバックを IObservable<T>に変換するとかなりイケテル! // 各処理が分離して記述可能になる、また合成処理が可能、などのメリットあり LogHelper.LogCallbackAsObservable() .Where(x => x.LogType == LogType.Warning) .Subscribe(); LogHelper.LogCallbackAsObservable() .Where(x => x.LogType == LogType.Error) .Subscribe();
  29. 29. センサー is IObservable<T> IObservable<T>は時間軸上に並ぶシーケンス 詳しくはググッて私の適当な記事読んでください http://www.atmarkit.co.jp/fdotnet/introrx/index/ Kinect, Leap Motionなどなど は、イベントシーケンスとみなせてRxと相性が非常にいい! というわけでUniRxで扱ってみてください まだメソッド足りないかもなのでIssueに入れてもらうとうれすぃ
  30. 30. Conclusion
  31. 31. まとめ IEnumeratorを非同期処理に使うのはいけてない そしてC# 5.0(async/await)は来ない! そこでUniRx なぜTaskじゃなくてRxなのか? Taskはawaitがなければ機能的に弱くて使いやすくはない マルチスレッド処理やイベント処理など多数の機能も! Available Now(?) GitHub - https://github.com/neuecc/UniRx/ AssetStore – http://u3d.as/content/neuecc/uni-rx-reactive- extensions-for-unity/7tT

×