SlideShare a Scribd company logo
History & Practices for UniRx(EN)
Work
http://grani.jp/
Unity
Private
http://neue.cc/
@neuecc
https://github.com/neuecc
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>();
History
https://github.com/neuecc/UniRx
Push Event Stream
Event Processing
Interactive/Visualize
CoreLibrary
Framework Adapter
Port of Rx.NET
FromCoroutine
MainThreadScheduler
ObservableTriggers
ReactiveProperty
2014/04/19 - UniRx 1.0
http://www.slideshare.net/neuecc/unityrx-reactive-extensions-for-unity
2014/05/28 - UniRx 4.0
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
2015/01/22 - UniRx 4.4~4.6
2015/03/10 - UniRx 4.7
2015/04/10 - UniRx 4.8
2015/04/16
http://www.slideshare.net/neuecc/observable-everywhere-rxuni-rx
2015/05/26 - UniRx 4.8.1
Practices & Pitfalls
https://github.com/neuecc/LightNode
ObservableWWW + Frequent methods
// 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);
}
}
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);
})
}
}
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>();
});
}
}
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>();
});
}
}
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>();
});
}
}
// 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();
}
}
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));
}
}
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 => { });
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;
});
WhenAll in Infinite Sequence?
Single(1) for "-Async" suffix
History & Practices for UniRx(EN)
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();
}
}
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
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
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);
}
}
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);
}
}
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);
}
}
MonoBehaviour's order is uncontrollable
// 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);
}
// 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();
}
}
https://github.com/neuecc/UniRx#presenterbase
Doesn't fire(I forget Subscribe)
// Return type is IObservable<Unit>
// Nothing happens...
new ObservableClient().SetNewData(100);
Doesn't fire(I forget Subscribe)
// button click in uGUI to asynchronous operation
button.OnClickAsObservable()
.SelectMany(_ => new ObservableClient().FooBarAsync(100))
.Subscribe(_ =>
{
Debug.Log("done");
});
button.OnClickAsObservable()
.SelectMany(_ =>
{
throw new Exception("something happen");
return new ObservableClient(). FooBarAsync(100);
})
.Subscribe(_ =>
{
Debug.Log("done");
});
button.OnClickAsObservable()
.SelectMany(_ =>
{
throw new Exception("something happen");
return new ObservableClient(). FooBarAsync(100);
})
.OnErrorRetry()
.Subscribe(_ =>
{
Debug.Log("done");
});
button.OnClickAsObservable().Subscribe(_ =>
{
// Subscribe in Subscribe...!
new ObservableClient(). FooBarAsync(100).Subscribe(__ =>
{
Debug.Log("done");
});
});
Can't avoid error and Retry is dangerous
button.OnClickAsObservable().Subscribe(_ =>
{
// Subscribe in Subscribe...!
new ObservableClient().NanikaHidoukiAsync(100).Subscribe(__ =>
{
Debug.Log("done");
});
});
Conclusion
We use UniRx
Real World UniRx

More Related Content

PDF
Deep Dive async/await in Unity with UniTask(EN)
PDF
UniRx - Reactive Extensions for Unity(EN)
PDF
A Brief History of UniRx/UniTask, IUniTaskSource in Depth
PDF
今日からできる!簡単 .NET 高速化 Tips
PPTX
Introduction to Rust language programming
PDF
20分くらいでわかった気分になれるC++20コルーチン
PDF
.NET最先端技術によるハイパフォーマンスウェブアプリケーション
PDF
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
Deep Dive async/await in Unity with UniTask(EN)
UniRx - Reactive Extensions for Unity(EN)
A Brief History of UniRx/UniTask, IUniTaskSource in Depth
今日からできる!簡単 .NET 高速化 Tips
Introduction to Rust language programming
20分くらいでわかった気分になれるC++20コルーチン
.NET最先端技術によるハイパフォーマンスウェブアプリケーション
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう

What's hot (20)

PDF
A quick tour of the Cysharp OSS
PPT
PDF
OSTree: OSイメージとパッケージシステムの間にGitのアプローチを
PPTX
Rust programming-language
PDF
How to Reverse Engineer Web Applications
PDF
【Unite Tokyo 2019】大量のアセットも怖くない!~HTTP/2による高速な通信の実装例~
PDF
gRPC と nginx による HTTP/2 サービスメッシュ構築
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
PDF
Kea DHCP – the new open source DHCP server from ISC
PDF
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
PDF
Modern C++ 프로그래머를 위한 CPP11/14 핵심
PDF
Introduction to React Hooks
PDF
マーブル図で怖くないRxJS
PDF
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
PDF
[오픈소스컨설팅]Scouter 설치 및 사용가이드(JBoss)
PDF
Logstashを愛して5年、370ページを超えるガチ本を書いてしまった男の話.
PDF
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
PDF
Detecting headless browsers
PPTX
スキトラ Spring + mybatis
PDF
会社でClojure使ってみて分かったこと
A quick tour of the Cysharp OSS
OSTree: OSイメージとパッケージシステムの間にGitのアプローチを
Rust programming-language
How to Reverse Engineer Web Applications
【Unite Tokyo 2019】大量のアセットも怖くない!~HTTP/2による高速な通信の実装例~
gRPC と nginx による HTTP/2 サービスメッシュ構築
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Kea DHCP – the new open source DHCP server from ISC
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Introduction to React Hooks
マーブル図で怖くないRxJS
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
[오픈소스컨설팅]Scouter 설치 및 사용가이드(JBoss)
Logstashを愛して5年、370ページを超えるガチ本を書いてしまった男の話.
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
Detecting headless browsers
スキトラ Spring + mybatis
会社でClojure使ってみて分かったこと
Ad

Viewers also liked (19)

PDF
LINQPad with LINQ to BigQuery - Desktop Client for BigQuery
PDF
linq.js - Linq to Objects for JavaScript
PDF
Photon Server Deep Dive - View from Implmentation of PhotonWire, Multiplayer ...
PDF
How to make the Fastest C# Serializer, In the case of ZeroFormatter
PPTX
linq.js ver.3 and JavaScript in Visual Studio 2012
PPTX
RuntimeUnitTestToolkit for Unity(English)
PDF
LINQ in Unity
PDF
Introduction to NotifyPropertyChangedGenerator
PDF
ZeroFormatter/MagicOnion - Fastest C# Serializer/gRPC based C# RPC
PPTX
Clash of Oni Online - VR Multiplay Sword Action
PDF
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
PDF
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
PDF
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
PDF
Photon Server Deep Dive - PhotonWireの実装から見つめるPhotonServerの基礎と応用
PDF
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
PDF
Binary Reading in C#
PDF
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
PDF
NextGen Server/Client Architecture - gRPC + Unity + C#
PPTX
RuntimeUnitTestToolkit for Unity
LINQPad with LINQ to BigQuery - Desktop Client for BigQuery
linq.js - Linq to Objects for JavaScript
Photon Server Deep Dive - View from Implmentation of PhotonWire, Multiplayer ...
How to make the Fastest C# Serializer, In the case of ZeroFormatter
linq.js ver.3 and JavaScript in Visual Studio 2012
RuntimeUnitTestToolkit for Unity(English)
LINQ in Unity
Introduction to NotifyPropertyChangedGenerator
ZeroFormatter/MagicOnion - Fastest C# Serializer/gRPC based C# RPC
Clash of Oni Online - VR Multiplay Sword Action
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
Photon Server Deep Dive - PhotonWireの実装から見つめるPhotonServerの基礎と応用
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Binary Reading in C#
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
NextGen Server/Client Architecture - gRPC + Unity + C#
RuntimeUnitTestToolkit for Unity
Ad

Similar to History & Practices for UniRx(EN) (20)

PDF
Durable functions 2.0 (2019-10-10)
PDF
Apache Beam de A à Z
PDF
Tornadoweb
PDF
Java Programming Must implement a storage manager that main.pdf
PPTX
Nantes Jug - Java 7
KEY
JavaScript Neednt Hurt - JavaBin talk
PDF
The Open Web and what it means
PDF
Reactive programming on Android
PDF
Groovy for java developers
PDF
Silicon Valley JUG: JVM Mechanics
PDF
The Beauty of Java Script
PDF
dojo.Patterns
PDF
Non Blocking I/O for Everyone with RxJava
DOCX
Soundreader.classpathSoundreader.project Soundre.docx
PDF
Patterns for JVM languages JokerConf
PDF
Android Best Practices
PDF
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
PDF
The Beauty Of Java Script V5a
PDF
Designing a JavaFX Mobile application
PDF
Multithreading in Java
Durable functions 2.0 (2019-10-10)
Apache Beam de A à Z
Tornadoweb
Java Programming Must implement a storage manager that main.pdf
Nantes Jug - Java 7
JavaScript Neednt Hurt - JavaBin talk
The Open Web and what it means
Reactive programming on Android
Groovy for java developers
Silicon Valley JUG: JVM Mechanics
The Beauty of Java Script
dojo.Patterns
Non Blocking I/O for Everyone with RxJava
Soundreader.classpathSoundreader.project Soundre.docx
Patterns for JVM languages JokerConf
Android Best Practices
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
The Beauty Of Java Script V5a
Designing a JavaFX Mobile application
Multithreading in Java

More from Yoshifumi Kawai (9)

PDF
Building the Game Server both API and Realtime via c#
PDF
ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能
PDF
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
PDF
Unity C#と.NET Core(MagicOnion) C# そしてKotlinによるハーモニー
PDF
Implements OpenTelemetry Collector in DotNet
PDF
The Usage and Patterns of MagicOnion
PDF
True Cloud Native Batch Workflow for .NET with MicroBatchFramework
PDF
Memory Management of C# with Unity Native Collections
PDF
Deep Dive async/await in Unity with UniTask(UniRx.Async)
Building the Game Server both API and Realtime via c#
ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
Unity C#と.NET Core(MagicOnion) C# そしてKotlinによるハーモニー
Implements OpenTelemetry Collector in DotNet
The Usage and Patterns of MagicOnion
True Cloud Native Batch Workflow for .NET with MicroBatchFramework
Memory Management of C# with Unity Native Collections
Deep Dive async/await in Unity with UniTask(UniRx.Async)

Recently uploaded (20)

PDF
Chapter 3 Spatial Domain Image Processing.pdf
PPTX
Spectroscopy.pptx food analysis technology
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
Big Data Technologies - Introduction.pptx
PDF
cuic standard and advanced reporting.pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Chapter 3 Spatial Domain Image Processing.pdf
Spectroscopy.pptx food analysis technology
“AI and Expert System Decision Support & Business Intelligence Systems”
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Big Data Technologies - Introduction.pptx
cuic standard and advanced reporting.pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
Review of recent advances in non-invasive hemoglobin estimation
Diabetes mellitus diagnosis method based random forest with bat algorithm
The Rise and Fall of 3GPP – Time for a Sabbatical?
The AUB Centre for AI in Media Proposal.docx
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
Advanced methodologies resolving dimensionality complications for autism neur...
Encapsulation_ Review paper, used for researhc scholars
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
MYSQL Presentation for SQL database connectivity
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf

History & Practices for UniRx(EN)

  • 3. 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>();
  • 6. Push Event Stream Event Processing Interactive/Visualize
  • 7. CoreLibrary Framework Adapter Port of Rx.NET FromCoroutine MainThreadScheduler ObservableTriggers ReactiveProperty
  • 8. 2014/04/19 - UniRx 1.0 http://www.slideshare.net/neuecc/unityrx-reactive-extensions-for-unity 2014/05/28 - UniRx 4.0
  • 9. 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
  • 11. 2015/03/10 - UniRx 4.7 2015/04/10 - UniRx 4.8
  • 16. // 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); } }
  • 17. 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); }) } }
  • 18. 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>(); }); } }
  • 19. 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>(); }); } }
  • 20. 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>(); }); } }
  • 21. // 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(); } }
  • 22. 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)); } }
  • 23. 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 => { });
  • 25. WhenAll in Infinite Sequence?
  • 28. 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(); } }
  • 29. 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
  • 30. 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
  • 31. 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); } }
  • 32. 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); } }
  • 33. 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); } }
  • 34. MonoBehaviour's order is uncontrollable
  • 35. // 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); }
  • 36. // 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(); } }
  • 38. Doesn't fire(I forget Subscribe) // Return type is IObservable<Unit> // Nothing happens... new ObservableClient().SetNewData(100);
  • 39. Doesn't fire(I forget Subscribe)
  • 40. // button click in uGUI to asynchronous operation button.OnClickAsObservable() .SelectMany(_ => new ObservableClient().FooBarAsync(100)) .Subscribe(_ => { Debug.Log("done"); });
  • 41. button.OnClickAsObservable() .SelectMany(_ => { throw new Exception("something happen"); return new ObservableClient(). FooBarAsync(100); }) .Subscribe(_ => { Debug.Log("done"); });
  • 42. button.OnClickAsObservable() .SelectMany(_ => { throw new Exception("something happen"); return new ObservableClient(). FooBarAsync(100); }) .OnErrorRetry() .Subscribe(_ => { Debug.Log("done"); });
  • 43. button.OnClickAsObservable().Subscribe(_ => { // Subscribe in Subscribe...! new ObservableClient(). FooBarAsync(100).Subscribe(__ => { Debug.Log("done"); }); });
  • 44. Can't avoid error and Retry is dangerous button.OnClickAsObservable().Subscribe(_ => { // Subscribe in Subscribe...! new ObservableClient().NanikaHidoukiAsync(100).Subscribe(__ => { Debug.Log("done"); }); });
  • 46. We use UniRx Real World UniRx