UniRx - Reactive Extensions for Unity(EN)

15,457 views

Published on

https://github.com/neuecc/UniRx

Published in: Technology, Spiritual

UniRx - Reactive Extensions for Unity(EN)

  1. 1. UniRx - Reactive Extensions for Unity Yoshifumi Kawai - @neuecc
  2. 2. Self Introduction @Work CTO/Director at Grani, Inc Grani is top social game developer in Japan C# 5.0 + .NET Framework 4.5 + ASP.NET MVC 5 @Private Microsoft MVP for Visual C# Web http://neue.cc/ (JPN) Twitter @neuecc (JPN) linq.js - http://linqjs.codeplex.com/ LINQ to Objects for JavaScript
  3. 3. Async in Unity Coroutine is not good practice for asynchronous operation
  4. 4. Async in Unity Network operation is awaitable by yield return Good by callback hell! It’s good! good? I have never thought it is good. IEnumerator OnMouseDown() { var www = new WWW("http://bing.com/"); yield return www; // await Debug.Log(www.text); }
  5. 5. Problem of yield can’t return result can’t handle exception IEnumerator GetGoogle() { var www = new WWW("http://google.com/"); yield return www; // can’t return www.text } IEnumerator OnMouseDown() { // this code is compiler error // yield can’t surround with try-catch try { yield return StartCoroutine(GetGoogle()); } catch { } } It cause operation can’t separate. We have to write on one IEnumerator.
  6. 6. Or Callback 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)); } Welcome to the callback hell! (We can await by yield but terrible yet)
  7. 7. 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; } } If so C# 5.0(async/await) yield(await) can receive return value We can handle exception by try-catch Async method can return result
  8. 8. Unity 5.0 Will be released on 2014 fall But Mono runtime is not renewed C# is still old(3.0) async has not come. IL2CPP - The future of scripting in Unity http://blogs.unity3d.com/2014/05/20/the-future-of-scripting-in- unity/ It’s future.
  9. 9. Reactive Programming
  10. 10. About Reactive Programming Recently attracted architecture style 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)
  11. 11. Gartner’s Hype Cycle 2013 Application Architecture/Application Development On the Rise - Reactive Programming
  12. 12. Reactive Extensions Reactive Programming on .NET https://rx.codeplex.com Project of Microsoft, OSS LINQ to events, asynchronous and more. Port to other languages RxJava(by Netflix) https://github.com/Netflix/RxJava 2070 Stars RxJS(by Microsoft) https://github.com/Reactive-Extensions/RxJS 1021 Stars
  13. 13. UniRx - Reactive Extensions for Unity Why UniRx? Official Rx is great impl but too heavy, can’t work old C# and can’t avoid iOS AOT trap. RxUnity is re-implementation of RxNet for Unity and several Unity specified utilities(Scheduler, ObservableWWW, etc) Available Now GitHub - https://github.com/neuecc/UniRx/ On Unity AssetStore(FREE) http://u3d.as/content/neuecc/uni-rx-reactive-extensions-for- unity/7tT
  14. 14. Curing Your Asynchronous Programming Blues Rx saves your life & codes
  15. 15. UnityAsync with Rx // x completed then go y and completed z, async flow by LINQ Query Expression 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 = “finally callback“(can avoid nested callback) query.Subscribe(x => Debug.Log(x), ex => Debug.LogException(ex)); // or convert to Coroutine and await(ToCoroutine is yieldable!) yield return StartCoroutine(query.Do(x => Debug.Log(x)).ToCoroutine());
  16. 16. etc, etc.... // Parallel run A and B var query = Observable.Zip( ObservableWWW.Get("http://google.co.jp/"), ObservableWWW.Get("http://bing.com/"), (google, bing) => new { google, bing }); // Retry on error var cancel = ObservableWWW.Get("http://hogehgoe") .OnErrorRetry((Exception ex) => Debug.LogException(ex), retryCount: 3, delay: TimeSpan.FromSeconds(1)) .Subscribe(Debug.Log); // Cancel is call Dispose of subscribed value cancel.Dispose(); // etc, etc, Rx has over 100 operators(methods) for method chain // you can control all execution flow of sync and async ObservableWWW is included in RxUnity, it is wrapped WWW, all method return IObservable
  17. 17. Orchestrate MultiThreading and LINQ to MonoBehaviour Message Events
  18. 18. UniRx solves MultiThreading problems // heavy work start on other thread(or specified scheduler) var heavyMethod1 = Observable.Start(() => { Thread.Sleep(TimeSpan.FromSeconds(1)); return 1; }); var heavyMethod2 = Observable.Start(() => { Thread.Sleep(TimeSpan.FromSeconds(3)); return 2; }); // Parallel work and concatenate by Zip heavyMethod1.Zip(heavyMethod2, (x, y) => new { x, y }) .ObserveOnMainThread() // return to MainThread .Subscribe(x => { // you can access GameObject (GameObject.Find("myGuiText")).guiText.text = x.ToString(); }); join other thread ObserveOnMainThread is return to MainThread and after flow can access GameObject easily cancel(call Dispose of subscribed value) etc, many methods supports multi thread programming
  19. 19. AsyncA AsyncB SelectMany – linear join AsyncA Zip – parallel join AsyncB Result
  20. 20. AsyncA AsyncB AsyncC Result AsyncA().SelectMany(a => AsyncB(a)) .Zip(AsyncC(), (b, c) => new { b, c }); SelectMany + Zip – Compose Sample
  21. 21. var asyncQuery = from a in AsyncA() from b in AsyncB(a) from c in AsyncC(a, b) select new { a, b, c }; multiplex from(SelectMany) AsyncA AsyncB AsyncC Result
  22. 22. Extra Gems
  23. 23. Extra methods only for Unity // Observable.EveryUpdate/FixedUpdate // produce value on every frame Observable.EveryUpdate() .Subscribe(_ => Debug.Log(DateTime.Now.ToString())); // ObservableMonoBehaviour public class Hoge : ObservableMonoBehaviour { public override void Awake() { // All MessageEvent are IObservable<T> var query = this.OnMouseDownAsObservable() .SelectMany(_ => this.OnMouseDragAsObservable()) .TakeUntil(this.OnMouseUpAsObservable()); } }
  24. 24. Unity用の各支援メソッド // prepare container 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>(); // publish to subject in callback UnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) => { subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, LogType = ty }); } return subject.AsObservable(); } } Convert Unity Callback to IObservable<T>
  25. 25. Unity用の各支援メソッド // prepare container 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>(); // publish to subject in callback UnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) => { subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, LogType = ty }); } return subject.AsObservable(); } } Convert Unity Callback to IObservable<T> // It’s separatable, composable, etc. LogHelper.LogCallbackAsObservable() .Where(x => x.LogType == LogType.Warning) .Subscribe(); LogHelper.LogCallbackAsObservable() .Where(x => x.LogType == LogType.Error) .Subscribe();
  26. 26. Conclusion
  27. 27. Conclusion IEnumerator for Async is bad and C# 5.0(async/await) hasn’t come Then UniRx Why Rx, not Task? Task is poor function without await And for MultiThreading, Event Operation, any more Available Now FREE GitHub - https://github.com/neuecc/UniRx/ AssetStore – http://u3d.as/content/neuecc/uni-rx-reactive- extensions-for-unity/7tT

×