UniRx - Reactive Extensions for Unity(EN)

Post on 09-May-2015

4.223 views 5 download

description

https://github.com/neuecc/UniRx

Transcript of UniRx - Reactive Extensions for Unity(EN)

UniRx - Reactive Extensions for Unity

Yoshifumi Kawai - @neuecc

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

Async in UnityCoroutine is not good practice for asynchronous operation

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

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.

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)

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

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.

Reactive Programming

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)

Gartner’s Hype Cycle

2013 Application Architecture/Application Development

On the Rise - Reactive Programming

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

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

Curing Your Asynchronous Programming BluesRx saves your life & codes

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

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

Orchestrate MultiThreadingand LINQ to MonoBehaviour Message Events

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

AsyncA AsyncB

SelectMany – linear join

AsyncA

Zip – parallel join

AsyncB

Result

AsyncA AsyncB

AsyncC

Result

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

SelectMany + Zip – Compose Sample

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

Extra Gems

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

}

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>

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

Conclusion

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