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.

History & Practices for UniRx(EN)

2,428 views

Published on

UniRx - https://github.com/neuecc/UniRx

Published in: Technology
  • Be the first to comment

History & Practices for UniRx(EN)

  1. 1. Work http://grani.jp/ Unity Private http://neue.cc/ @neuecc https://github.com/neuecc
  2. 2. LINQ to GameOject https://github.com/neuecc/LINQ-to-GameObject-for-Unity https://www.assetstore.unity3d.com/jp/#!/content/24256 // destroy all filtered(tag == "foobar") objects root.Descendants() .Where(x => x.tag == "foobar") .Destroy(); // get FooScript under self childer objects and self var fooScripts = root.ChildrenAndSelf().OfComponent<FooScript>();
  3. 3. History
  4. 4. https://github.com/neuecc/UniRx
  5. 5. Push Event Stream Event Processing Interactive/Visualize
  6. 6. CoreLibrary Framework Adapter Port of Rx.NET FromCoroutine MainThreadScheduler ObservableTriggers ReactiveProperty
  7. 7. 2014/04/19 - UniRx 1.0 http://www.slideshare.net/neuecc/unityrx-reactive-extensions-for-unity 2014/05/28 - UniRx 4.0
  8. 8. 2014/07/10 - UniRx 4.3 https://github.com/neuecc/UniRx/wiki/AOT-Exception-Patterns-and-Hacks 2014/07/30 http://www.slideshare.net/neuecc/reactive-programming-by-unirxfor-asynchronous-event-processing
  9. 9. 2015/01/22 - UniRx 4.4~4.6
  10. 10. 2015/03/10 - UniRx 4.7 2015/04/10 - UniRx 4.8
  11. 11. 2015/04/16 http://www.slideshare.net/neuecc/observable-everywhere-rxuni-rx 2015/05/26 - UniRx 4.8.1
  12. 12. Practices & Pitfalls
  13. 13. https://github.com/neuecc/LightNode
  14. 14. ObservableWWW + Frequent methods
  15. 15. // Wrap the ObservableWWW and aggregate network access public class ObservableClient { public IObservable<WWW> GetHogeAsync(int huga) { return ObservableWWW.GetWWW("http://hogehoge.com/Hoge?huga=" + huga); } }
  16. 16. public class ObservableClient { public IObservable<HogeResponse> GetHogeAsync(int huga) { return ObservableWWW.GetWWW("http://hogehoge.com/Hoge?huga=" + huga) .Select(www => { // This is used JSON.NET for Unity(payed asset) // LitJson or MiniJson etc, return the deserialized value return JsonConvert.DeserializeObject<HogeResponse>(www.text); }) } }
  17. 17. public class ObservableClient { public IObservable<HogeResponse> GetHogeAsync(int huga) { return ObservableWWW.GetWWW("http://hogehoge.com/Hoge?huga=" + huga) .Select(www => { return JsonConvert.DeserializeObject<HogeResponse>(www.text); }) .Catch((WWWErrorException error) => { // Failed in WWW // For example shows dialog and await user input, // you can do even if input result is IObservable return Observable.Empty<HogeResponse>(); }); } }
  18. 18. public class ObservableClient { public IObservable<HogeResponse> GetHogeAsync(int huga) { return ObservableWWW.GetWWW("http://hogehoge.com/Hoge?huga=" + huga) .Timeout(TimeSpan.FromSeconds(30)) // 30 Seconds timeout .Select(www => { return JsonConvert.DeserializeObject<HogeResponse>(www.text); }) .Catch((WWWErrorException error) => { return Observable.Empty<HogeResponse>(); }); } }
  19. 19. public class ObservableClient { public IObservable<HogeResponse> GetHogeAsync(int huga) { return ObservableWWW.GetWWW("http://hogehoge.com/Hoge?huga=" + huga) .Timeout(TimeSpan.FromSeconds(30)) .Select(www => { return JsonConvert.DeserializeObject<HogeResponse>(www.text); }) .Catch((TimeoutException error) => { // Observable.Empty<T>? Observable.Throw? etc return Observable.Throw<HogeResponse>(error); }) .Catch((WWWErrorException error) => { return Observable.Empty<HogeResponse>(); }); } }
  20. 20. // complex retyable edition public class ObservableClient { public IObservable<HogeResponse> GetHogeAsync(int huga) { //for retry IObservable<HogeResponse> asyncRequest = null; asyncRequest = ObservableWWW.GetWWW("http://hogehoge.com/Hoge?huga=" + huga) .Timeout(TimeSpan.FromSeconds(30)) .Select(www => JsonConvert.DeserializeObject<HogeResponse>(www.text)) .Catch((TimeoutException error) => Observable.Throw<HogeResponse>(error)) .Catch((WWWErrorException error) => { // If retry, you can return self such as "return asyncRequest" // If retry after 3 seconds you can write // asyncRequest.DelaySubscription(TimeSpan.FromSeconds(3)) return Observable.Empty<HogeResponse>(); }); // PublishLast, remove Cold behaivour, returns one result when called multiple // RefCount automate subscribable for PublishLast's .Connect return asyncRequest.PublishLast().RefCount(); } }
  21. 21. public class ObservableClient { // outside in method IObservable<T> WithErrorHandling<T>(IObservable<WWW> source) { IObservable<T> asyncRequest = null; asyncRequest = source .Timeout(TimeSpan.FromSeconds(30)) .Select(www => JsonConvert.DeserializeObject<T>(www.text)) .Catch((TimeoutException error) => Observable.Throw<T>(error)) .Catch((WWWErrorException error) => Observable.Throw<T>(error)) .Catch((Exception error) => Observable.Throw<T>(error)); return asyncRequest.PublishLast().RefCount(); } // public IObservable<HogeResponse> GetHogeAsync(int huga) { return WithErrorHandling<HogeResponse>(ObservableWWW.GetWWW("http://hogehoge.com/Hoge?huga=" + huga)); } public IObservable<HugaResponse> GetHugaAsync(int huga) { return WithErrorHandling<HugaResponse>(ObservableWWW.GetWWW("http://hogehoge.com/Huga?huga=" + huga)); } }
  22. 22. WhenAll makes easy for parallel request var client = new ObservableClient(); Observable.WhenAll( client.GetFooAsync(), client.GetFooAsync(), client.GetFooAsync()) .Subscribe(xs => { }); // Compile error detected when each return type is not same! Observable.WhenAll( client.GetFooAsync(), client.GetBarAsync(), client.GetBazAsync()) .Subscribe(xs => { });
  23. 23. Use Cast Observable.WhenAll( client.GetFooAsync().Cast(default(object)), client.GetBarAsync().Cast(default(object)), client.GetBazAsync().Cast(default(object))) .Subscribe(xs => { var foo = xs[0] as FooResponse; var bar = xs[1] as BarResponse; var baz = xs[2] as BazResponse; });
  24. 24. WhenAll in Infinite Sequence?
  25. 25. Single(1) for "-Async" suffix
  26. 26. Completed callback as IObservable<T> // for example public static class DoTweenExtensions { public static IObservable<Sequence> CompletedAsObservable(this Sequence sequence) { var subject = new AsyncSubject<Sequence>(); sequence.AppendCallback(() => { subject.OnNext(sequence); subject.OnCompleted(); }); return subject.AsObservable(); } }
  27. 27. Represents Value+Event [Serializable] public class Character : MonoBehaviour { public Action<int> HpChanged; [SerializeField] int hp = 0; public int Hp { get { return hp; } set { hp = value; var h = HpChanged; if (h != null) { h.Invoke(hp); } } [Serializable] public class Character : MonoBehaviour { public IntReactiveProperty Hp; } + notify when their value is changed even when it is changed in the inspector
  28. 28. Unity + Rx's UI Pattern 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
  29. 29. 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); } }
  30. 30. Issue of P and serializable value // Child [Serializable] public class ChildPresenter : MonoBehaviour { public IntReactiveProperty Hp; // serializable public ReadOnlyReactiveProperty<bool> IsDead { get; private set; } void Start() { IsDead = Hp.Select(x => x <= 0) .ToReadOnlyReactiveProperty(); } } // Parent [Serializable] public class ParentPresenter : MonoBehaviour { public ChildPresenter ChildPresenter; public Text IsDeadDisplay; void Start() { // Can you touch IsDead? ChildPresenter.IsDead .SubscribeToText(IsDeadDisplay); } }
  31. 31. Issue of P and serializable value // Child [Serializable] public class ChildPresenter : MonoBehaviour { public IntReactiveProperty Hp; // serializable public ReadOnlyReactiveProperty<bool> IsDead { get; private set; } void Start() { IsDead = Hp.Select(x => x <= 0) .ToReadOnlyReactiveProperty(); } } // Parent [Serializable] public class ParentPresenter : MonoBehaviour { public ChildPresenter ChildPresenter; public Text IsDeadDisplay; void Start() { // Can you touch IsDead? ChildPresenter.IsDead .SubscribeToText(IsDeadDisplay); } }
  32. 32. MonoBehaviour's order is uncontrollable
  33. 33. // Parent public class ParentPresenter : PresenterBase { public ChildPresenter ChildPresenter; public Text IsDeadDisplay; protected override IPresenter[] Children { get { return new IPresenter[] { ChildPresenter }; } // Children } protected override void BeforeInitialize() { ChildPresenter.PropagateArgument(1000); // Pass initial argument to child } protected override void Initialize() { // After initialzied ChildPresenter.IsDead.SubscribeToText(IsDeadDisplay); }
  34. 34. // Child [Serializable] public class ChildPresenter : PresenterBase<int> { public IntReactiveProperty Hp; // serializable public ReadOnlyReactiveProperty<bool> IsDead { get; set; } protected override IPresenter[] Children { get { return EmptyChildren; } } protected override void BeforeInitialize(int argument) { } // argument from parent protected override void Initialize(int argument) { Hp.Value = argument; IsDead = Hp.Select(x => x <= 0).ToReadOnlyReactiveProperty(); } }
  35. 35. https://github.com/neuecc/UniRx#presenterbase
  36. 36. Doesn't fire(I forget Subscribe) // Return type is IObservable<Unit> // Nothing happens... new ObservableClient().SetNewData(100);
  37. 37. Doesn't fire(I forget Subscribe)
  38. 38. // button click in uGUI to asynchronous operation button.OnClickAsObservable() .SelectMany(_ => new ObservableClient().FooBarAsync(100)) .Subscribe(_ => { Debug.Log("done"); });
  39. 39. button.OnClickAsObservable() .SelectMany(_ => { throw new Exception("something happen"); return new ObservableClient(). FooBarAsync(100); }) .Subscribe(_ => { Debug.Log("done"); });
  40. 40. button.OnClickAsObservable() .SelectMany(_ => { throw new Exception("something happen"); return new ObservableClient(). FooBarAsync(100); }) .OnErrorRetry() .Subscribe(_ => { Debug.Log("done"); });
  41. 41. button.OnClickAsObservable().Subscribe(_ => { // Subscribe in Subscribe...! new ObservableClient(). FooBarAsync(100).Subscribe(__ => { Debug.Log("done"); }); });
  42. 42. Can't avoid error and Retry is dangerous button.OnClickAsObservable().Subscribe(_ => { // Subscribe in Subscribe...! new ObservableClient().NanikaHidoukiAsync(100).Subscribe(__ => { Debug.Log("done"); }); });
  43. 43. Conclusion
  44. 44. We use UniRx Real World UniRx

×