UniRx - Reactive Extensions for Unity(EN)

27
UniRx - Reactive Extensions for Unity Yoshifumi Kawai - @neuecc

description

https://github.com/neuecc/UniRx

Transcript of UniRx - Reactive Extensions for Unity(EN)

Page 1: UniRx - Reactive Extensions for Unity(EN)

UniRx - Reactive Extensions for Unity

Yoshifumi Kawai - @neuecc

Page 2: UniRx - Reactive Extensions for Unity(EN)

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

Page 3: UniRx - Reactive Extensions for Unity(EN)

Async in UnityCoroutine is not good practice for asynchronous operation

Page 4: UniRx - Reactive Extensions for Unity(EN)

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);}

Page 5: UniRx - Reactive Extensions for Unity(EN)

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-catchtry{

yield return StartCoroutine(GetGoogle());}catch{}

}

It cause operation can’t separate.

We have to write on one IEnumerator.

Page 6: UniRx - Reactive Extensions for Unity(EN)

Or CallbackIEnumerator 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)

Page 7: UniRx - Reactive Extensions for Unity(EN)

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

Page 8: UniRx - Reactive Extensions for Unity(EN)

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.

Page 9: UniRx - Reactive Extensions for Unity(EN)

Reactive Programming

Page 10: UniRx - Reactive Extensions for Unity(EN)

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)

Page 11: UniRx - Reactive Extensions for Unity(EN)

Gartner’s Hype Cycle

2013 Application Architecture/Application Development

On the Rise - Reactive Programming

Page 12: UniRx - Reactive Extensions for Unity(EN)

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

Page 13: UniRx - Reactive Extensions for Unity(EN)

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

Page 14: UniRx - Reactive Extensions for Unity(EN)

Curing Your Asynchronous Programming BluesRx saves your life & codes

Page 15: UniRx - Reactive Extensions for Unity(EN)

UnityAsync with Rx

// x completed then go y and completed z, async flow by LINQ Query Expressionvar 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());

Page 16: UniRx - Reactive Extensions for Unity(EN)

etc, etc....

// Parallel run A and Bvar query = Observable.Zip(

ObservableWWW.Get("http://google.co.jp/"),ObservableWWW.Get("http://bing.com/"),(google, bing) => new { google, bing });

// Retry on errorvar 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 valuecancel.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

Page 17: UniRx - Reactive Extensions for Unity(EN)

Orchestrate MultiThreadingand LINQ to MonoBehaviour Message Events

Page 18: UniRx - Reactive Extensions for Unity(EN)

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 ZipheavyMethod1.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

Page 19: UniRx - Reactive Extensions for Unity(EN)

AsyncA AsyncB

SelectMany – linear join

AsyncA

Zip – parallel join

AsyncB

Result

Page 20: UniRx - Reactive Extensions for Unity(EN)

AsyncA AsyncB

AsyncC

Result

AsyncA().SelectMany(a => AsyncB(a)).Zip(AsyncC(), (b, c) => new { b, c });

SelectMany + Zip – Compose Sample

Page 21: UniRx - Reactive Extensions for Unity(EN)

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

Page 22: UniRx - Reactive Extensions for Unity(EN)

Extra Gems

Page 23: UniRx - Reactive Extensions for Unity(EN)

Extra methods only for Unity// Observable.EveryUpdate/FixedUpdate// produce value on every frameObservable.EveryUpdate()

.Subscribe(_ => Debug.Log(DateTime.Now.ToString()));

// ObservableMonoBehaviourpublic class Hoge : ObservableMonoBehaviour{

public override void Awake(){

// All MessageEvent are IObservable<T>var query = this.OnMouseDownAsObservable()

.SelectMany(_ => this.OnMouseDragAsObservable())

.TakeUntil(this.OnMouseUpAsObservable());}

}

Page 24: UniRx - Reactive Extensions for Unity(EN)

Unity用の各支援メソッド// prepare containerpublic 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 callbackUnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) =>{

subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, LogType = type });});

}

return subject.AsObservable();}

}

Convert Unity Callback to IObservable<T>

Page 25: UniRx - Reactive Extensions for Unity(EN)

Unity用の各支援メソッド// prepare containerpublic 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 callbackUnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) =>{

subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, LogType = type });});

}

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();

Page 26: UniRx - Reactive Extensions for Unity(EN)

Conclusion

Page 27: UniRx - Reactive Extensions for Unity(EN)

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