SlideShare a Scribd company logo
UniRx - Reactive Extensions for Unity
2014/04/19
Yoshifumi Kawai - @neuecc
Self Introduction
@仕事
株式会社グラニ 取締役CTO
C# 5.0 + .NET Framework 4.5 + ASP.NET MVC 5
最先端C#によるハイパフォーマンスWebアプリケーション
@個人活動
Microsoft MVP for Visual C#
Web http://neue.cc/
Twitter @neuecc
linq.js - http://linqjs.codeplex.com/ とか作ってます
Unity
Game Engine but Not Only for Game Use
Unityとは
押しも押されぬゲームエンジン
http://japan.unity3d.com/
最近は公式に3Dだけじゃなく2Dもサポートされた
クロスプラットフォーム(PC/iOS/Android/Windows Phone/
Windows Store/etc...)
国内でも事例多数(iOS版ドラクエ8などなど)
C#で書ける! ※但しバージョン的には3.0相当
ゲーム以外の用途にも
映像の出力先としてのUnity
VR(Oculus Rift)やNUI(Kinect, Leap Motion)などデバイスがあっ
ても、何に表示するの?何を表現するの?
そこの最有力候補としてのUnity
強力な開発環境、3D表現、AssetStore、そしてシェア
多くのデバイスがUnity用のSDKを用意している
メディアアート
ProcessingやopenFrameworksなどのフィールドへ (期待)
Async in Unity
Coroutine is not good practice for asynchronous operation
Unityの非同期
通信処理はyield returnで待機できる
コールバック地獄にならない!
すばら!すばら!
と思ったことは一度もありません:)
IEnumerator OnMouseDown()
{
var www = new WWW("http://bing.com/");
yield return www; // これで待機
Debug.Log(www.text);
}
yieldの問題点
戻り値が返せない
例外処理ができない
IEnumerator GetGoogle()
{
var www = new WWW("http://google.com/");
yield return www;
// 戻り値を返せない!!! www.text is どこ
}
IEnumerator OnMouseDown()
{
// このコードはコンパイルエラー
// yieldはtry-catchで囲めないので例外処理できない!
try
{
yield return StartCoroutine(GetGoogle());
}
catch
{
}
}
これにより処理が分離できないという問題が
発生する。一つのIEnumeratorにダラダラと書
き連ねるしかなくなってしまう
あるいはコールバック
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));
}
結局コールバック地獄かよ!
(yieldで完了待機できるので多少マシだけどとはいえ酷い)
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;
}
}
C# 5.0(async/await)なら?
yield(await)が戻り値を返して受け取れる
例外がtry-catchできる
非同期メソッドが戻り値を返せる
Unity 5.0
2014年秋ぐらいに多分リリース!
Monoのバージョンは新しくなりません!
つまりC#も古い(3.0)のままです!
asyncは来ない!知ってた!この先も期待できない!
The future of scripting in Unity
http://blogs.unity3d.com/2014/05/20/the-future-of-scripting-in-
unity/
今まで出遅れていたのを一気に取り戻そう作戦
Future、って感じでまだまだかなり時間かかりそうだ
Reactive Programming
Reactive Programming #とは
近年注目のアーキテクチャスタイル
昔からあったといえばあったけど、原理原則やライブラリが充実し
てきたので、最近一気に華開いた感ある
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
.NETでのReactive Programmingライブラリ
https://rx.codeplex.com
Microsoftのプロジェクトだったけど現在はOSS化している
LINQスタイルでイベントや非同期が処理できる
他言語への移植
RxJava(RxNetよりむしろ注目されてるぐらい)
https://github.com/Netflix/RxJava 2070 Stars
RxJS(Microsoft自身によるもの)
https://github.com/Reactive-Extensions/RxJS 1021 Stars
UniRx - Reactive Extensions for Unity
というのを作りました、本日公開!
ほとんど(8割ぐらい)は自前で書いた
公式のRxはヘヴィすぎてUnityの古いC#で動かしきれない
iOSはAOTの問題もあって回避頑張れる気がしない
ので自前で書くことに。
Available Now(?)
GitHub - https://github.com/neuecc/UniRx/
AssetStore(無料)
http://u3d.as/content/neuecc/uni-rx-reactive-extensions-for-
unity/7tT
Curing Your Asynchronous
Programming Blues
Rx saves your life & codes
RxによるUnityの非同期通信
// xが完了したらそれでy、完了したらzのダウンロードの連鎖のフローをLINQクエリ式で
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 = "最後に全部まとまったあとの"コールバック(ネストしないから処理が楽)
query.Subscribe(x => Debug.Log(x), ex => Debug.LogException(ex));
// もしくはCoroutineに変換して待機も可能(ToCoroutine is yieldable!)
yield return StartCoroutine(query.Do(x => Debug.Log(x)).ToCoroutine());
etc, etc....
// AとBを同時並列に走らせて一個にまとめる
var query = Observable.Zip(
ObservableWWW.Get("http://google.co.jp/"),
ObservableWWW.Get("http://bing.com/"),
(google, bing) => new { google, bing });
// エラーが起きたらリトライ処理を入れる
var cancel = ObservableWWW.Get("http://hogehgoe")
.OnErrorRetry((Exception ex) => Debug.LogException(ex),
retryCount: 3, delay: TimeSpan.FromSeconds(1))
.Subscribe(Debug.Log);
// キャンセルしたい場合は戻り値のDisposeを呼ぶだけ
cancel.Dispose();
// などなど、100近くの演算子をメソッドチェーン形式で繋げることができる
// あらゆる実行フローを完全にコントロールできる
ところでObservableWWWは、
UniRxに同梱されている、Rxの
形式にラップ済みのWWWクラ
スで通信可能なメソッドです
Orchestrate MultiThreading
and LINQ to MonoBehaviour Message Events
UniRx solves MultiThreading problems
// なんか重たい処理を別スレッドで開始するStartメソッド
var heavyMethod1 = Observable.Start(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(3));
return 1;
});
var heavyMethod2 = Observable.Start(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(3));
return 1;
});
// Zipで両方並列に走らせながら連結して
heavyMethod1.Zip(heavyMethod2, (x, y) => new { x, y })
.ObserveOnMainThread() // MainThreadに戻す!
.Subscribe(x =>
{
(GameObject.Find("myGuiText")).guiText.text = x.ToString();
});
他スレッドと待ち合わせ
MainThreadに戻して、そ
れ以降のフローは
GameObjectにアクセス可
能にする
ObserveOnMainThread
Subscribe後の戻り値を
Disposeするだけでキャン
セル可能、などなど、多種
のメソッドがマルチスレッ
ド処理を支援する
AsyncA AsyncB
SelectMany - 直列の結合
AsyncA
Zip - 並列の結合
AsyncB
Result
AsyncA AsyncB
AsyncC
Result
AsyncA().SelectMany(a => AsyncB(a))
.Zip(AsyncC(), (b, c) => new { b, c });
SelectMany + Zip - 合成の例
var asyncQuery = from a in AsyncA()
from b in AsyncB(a)
from c in AsyncC(a, b)
select new { a, b, c };
多重from(SelectMany)
AsyncA AsyncB AsyncC Result
Extra Gems
Unity用の各支援メソッド
// Observable.EveryUpdate
// 毎フレーム毎に値を発行する
Observable.EveryUpdate()
.Subscribe(_ =>
Debug.Log(DateTime.Now.ToString()));
// ObservableMonoBehaviour
public class Hoge : ObservableMonoBehaviour
{
public override void Awake()
{
// 全てのMessageEventがRxで扱う形式で取得できる
var query = this.OnMouseDownAsObservable()
.SelectMany(_ => this.OnMouseDragAsObservable())
.TakeUntil(this.OnMouseUpAsObservable());
}
}
Unity用の各支援メソッド
// 入れ物を用意して
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>();
// callback内でSubjectに発行してあげるように作ることでRxにコンバート可能
UnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) =>
{
subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, LogType = ty
});
}
return subject.AsObservable();
}
}
Unityに多くあるデリゲートによるコールバックを
IObservable<T>に変換するとかなりイケテル!
Unity用の各支援メソッド
// 入れ物を用意して
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>();
// callback内でSubjectに発行してあげるように作ることでRxにコンバート可能
UnityEngine.Application.RegisterLogCallback((condition, stackTrace, type) =>
{
subject.OnNext(new LogCallback { Condition = condition, StackTrace = stackTrace, LogType = ty
});
}
return subject.AsObservable();
}
}
Unityに多くあるデリゲートによるコールバックを
IObservable<T>に変換するとかなりイケテル!
// 各処理が分離して記述可能になる、また合成処理が可能、などのメリットあり
LogHelper.LogCallbackAsObservable()
.Where(x => x.LogType == LogType.Warning)
.Subscribe();
LogHelper.LogCallbackAsObservable()
.Where(x => x.LogType == LogType.Error)
.Subscribe();
センサー is IObservable<T>
IObservable<T>は時間軸上に並ぶシーケンス
詳しくはググッて私の適当な記事読んでください
http://www.atmarkit.co.jp/fdotnet/introrx/index/
Kinect, Leap Motionなどなど
は、イベントシーケンスとみなせてRxと相性が非常にいい!
というわけでUniRxで扱ってみてください
まだメソッド足りないかもなのでIssueに入れてもらうとうれすぃ
Conclusion
まとめ
IEnumeratorを非同期処理に使うのはいけてない
そしてC# 5.0(async/await)は来ない!
そこでUniRx
なぜTaskじゃなくてRxなのか?
Taskはawaitがなければ機能的に弱くて使いやすくはない
マルチスレッド処理やイベント処理など多数の機能も!
Available Now(?)
GitHub - https://github.com/neuecc/UniRx/
AssetStore – http://u3d.as/content/neuecc/uni-rx-reactive-
extensions-for-unity/7tT

More Related Content

UniRx - Reactive Extensions for Unity