SlideShare a Scribd company logo
ロードオブナイツで使ってる
Unity非同期処理デザイン




          ++C++; 岩永信之 
              2012/10/12
イテレーター構文とUnityのコルーチン

コルーチン
ゲーム ループ
●   ゲームでは、どこか大元でループが回ってる
     while (isAlive)
     {
         // 固定 FPS なら、所定の時間が来るまで Sleep

         gameTime = … // ゲーム時間を進める

         foreach (var obj in gameObjects)
         {
             obj.Update (gameTime);
         }
     }
            1フレームに1回よばれる処理
重たい処理
●   フレームレートよりも時間がかかる処理をし
    ちゃダメ
    ダメな例
    string Load(string path)
    {
        // 30ミリ秒くらいかかるものとする
        var data = ファイルからバイナリロード(path);

        // これも30ミリ秒くらいかかるものとする
          return デシリアライズ(data);

        // 30 FPSだと、このメソッドは33ミリ秒以内に終えないと処理落ち
    }
                     2回に分けたい
そこで、イテレーター
●   イテレーターをコルーチンとして使う
ゲーム ループ中で、毎フレーム
 MoveNextを呼んでもらう
    IEnumerator Load(string path, Action<string> callback)
    {
          var data = ファイルからバイナリロード(path);        1フレーム目
        yield return null;

          callback(デシリアライズ(data));             2フレーム目
        yield return null;
    }
                         returnの代わりに
                        コールバック呼び出し
before/after (1)
●   メソッド宣言
    before
    string Load(string path)

    after
    IEnumerator Load(string path, Action<string> callback)

     ●   戻り値はIEnumerator固定
     ●   本来の戻り値はcallbackごしに返す
before/after (2)
●   return
    before
     return result;
    after
     callback(result);

     ●   returnステートメントの代わりにcallback呼び出し
before/after (3)
●    呼び出し側
    before
    var x = Load("path"); …(後続の処理)

    after
    StartCoroutine(Load("path", x => { …後続の処理 });

     ●      戻り値を直接受け取れない
            ●   形式上の戻り値(IEnumerator)はコルーチン起動のた
                めに使う
     ●      匿名関数を使って後続の処理をつなぐ
.NET Framework 4のTaskクラスを参考に、Unityコルーチンをラッピング

TASKクラス
問題
●   複数のコルーチンを扱いにくい
     StartCoroutine(A);
     StartCoroutine(B);


        ●   A, B両方が完了するのを待ちたいときはどうする?
        ●   Aの完了後にBを開始したいときはどうする?
             ● 特に、Aの(本来の)戻り値をBで使いたいときは?
             ● エラーの伝播を考えるとさらに面倒
Taskクラス
●   こういうクラスを用意
        public class Task<T> : IEnumerator
        {
            IEnumerator Routine;
            public T Result { get; }
            public Exception Error { get; }
            public void OnComplete(… callback);
            public Task<T> ContinueWith<U>(… continuation);
        }


    ●   継続処理の登録
    ●   (本来の)戻り値やエラーの伝播
実例

●   ロードオブナイツのマップ
    ●   64万要素程度の配列をJSONで受け取ってた
         ● 通信もコルーチン
            ●   通信エラーが発生する可能性あり
        ●   デコードもそこそこ高負荷なのでコルーチン化したい
            ●   (行単位でデコード、1フレームに1行ずつとか)
            ●   通信の結果を使う
            ●   デコード エラーが発生する可能性あり
        ●   ローカル ストレージにキャッシュしたい
            ●   IOエラーが発生する可能性あり

※ 最新バージョンではデータを小分けで受信するように改善され、デコード
処理はコルーチンではなくなっている
Task利用例(戻り値)
●   (本来の)戻り値の伝播
     IEnumerator A(Action<int> callback);
     IEnumerator B(int x, Action<string> callback);
     IEnumerator C(string s);

                A, B, C の順で実行したい
                A の戻り値(int)を B で、B の戻り値(string)を C で使いたい
                同期処理なら C(B(A()); だけで書けるもの

     var t = new Task<int>(A)
         .ContinueWith<string>(B)
         .ContinueWith(C);

     StartCoroutine(t);
同期処理と比べて
●   同期処理との対比
     IEnumerator A(Action<int> callback);
     IEnumerator B(int x, Action<string> callback);
     IEnumerator C(string s);

                                          int A();
                                          string B(int x);
                                          void C(string s);

     var t = new Task<int>(A)             var x = A();
         .ContinueWith<string>(B)         var s = B(x);
         .ContinueWith(C);                C(s);

     StartCoroutine(t);                      あるいは
                                            C(B(A()));
Task利用例(例外処理)
●   コルーチン内で発生した例外も受け取れる
    var t = new Task<int>(A)
        .ContinueWith<string>(B)
        .ContinueWith(C)
        .OnComplete(t =>             ●   A, B, C のどこかで例外が発
        {                                生した場合、そこでコルーチン
            if (t.Error != null) …       の実行は中断。
        });                          ●   発生した例外はErrorプロパ
                                         ティにセットされる
    StartCoroutine(t);
以上

More Related Content

What's hot (20)

きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回
Tomoya Kawanishi
 
関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU
Takuro Iizuka
 
Boost.Coroutine
Boost.CoroutineBoost.Coroutine
Boost.Coroutine
melpon
 
C#次世代非同期処理概観 - Task vs Reactive Extensions
C#次世代非同期処理概観 - Task vs Reactive ExtensionsC#次世代非同期処理概観 - Task vs Reactive Extensions
C#次世代非同期処理概観 - Task vs Reactive Extensions
Yoshifumi Kawai
 
TensorFlow XLA 「XLAとは、から、最近の利用事例について」
TensorFlow XLA 「XLAとは、から、最近の利用事例について」TensorFlow XLA 「XLAとは、から、最近の利用事例について」
TensorFlow XLA 「XLAとは、から、最近の利用事例について」
Mr. Vengineer
 
Deep Dive async/await in Unity with UniTask(UniRx.Async)
Deep Dive async/await in Unity with UniTask(UniRx.Async)Deep Dive async/await in Unity with UniTask(UniRx.Async)
Deep Dive async/await in Unity with UniTask(UniRx.Async)
Yoshifumi Kawai
 
LINQ in Unity
LINQ in UnityLINQ in Unity
LINQ in Unity
Yoshifumi Kawai
 
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
Unity Technologies Japan K.K.
 
An other world awaits you
An other world awaits youAn other world awaits you
An other world awaits you
信之 岩永
 
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
Yoshifumi Kawai
 
C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミング
Kohsuke Yuasa
 
UniRxことはじめ
UniRxことはじめUniRxことはじめ
UniRxことはじめ
Shoichi Yasui
 
Visual C++コード分析を支えるSAL
Visual C++コード分析を支えるSALVisual C++コード分析を支えるSAL
Visual C++コード分析を支えるSAL
egtra
 
配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)
配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)
配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)
TatsuyaKatayama
 
Python で munin plugin を書いてみる
Python で munin plugin を書いてみるPython で munin plugin を書いてみる
Python で munin plugin を書いてみる
ftnk
 
SEH on mingw32
SEH on mingw32SEH on mingw32
SEH on mingw32
kikairoya
 
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
Yoshifumi Kawai
 
きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回
Tomoya Kawanishi
 
関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU
Takuro Iizuka
 
Boost.Coroutine
Boost.CoroutineBoost.Coroutine
Boost.Coroutine
melpon
 
C#次世代非同期処理概観 - Task vs Reactive Extensions
C#次世代非同期処理概観 - Task vs Reactive ExtensionsC#次世代非同期処理概観 - Task vs Reactive Extensions
C#次世代非同期処理概観 - Task vs Reactive Extensions
Yoshifumi Kawai
 
TensorFlow XLA 「XLAとは、から、最近の利用事例について」
TensorFlow XLA 「XLAとは、から、最近の利用事例について」TensorFlow XLA 「XLAとは、から、最近の利用事例について」
TensorFlow XLA 「XLAとは、から、最近の利用事例について」
Mr. Vengineer
 
Deep Dive async/await in Unity with UniTask(UniRx.Async)
Deep Dive async/await in Unity with UniTask(UniRx.Async)Deep Dive async/await in Unity with UniTask(UniRx.Async)
Deep Dive async/await in Unity with UniTask(UniRx.Async)
Yoshifumi Kawai
 
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
Unity Technologies Japan K.K.
 
An other world awaits you
An other world awaits youAn other world awaits you
An other world awaits you
信之 岩永
 
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
Yoshifumi Kawai
 
C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミング
Kohsuke Yuasa
 
UniRxことはじめ
UniRxことはじめUniRxことはじめ
UniRxことはじめ
Shoichi Yasui
 
Visual C++コード分析を支えるSAL
Visual C++コード分析を支えるSALVisual C++コード分析を支えるSAL
Visual C++コード分析を支えるSAL
egtra
 
配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)
配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)
配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)
TatsuyaKatayama
 
Python で munin plugin を書いてみる
Python で munin plugin を書いてみるPython で munin plugin を書いてみる
Python で munin plugin を書いてみる
ftnk
 
SEH on mingw32
SEH on mingw32SEH on mingw32
SEH on mingw32
kikairoya
 
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
Yoshifumi Kawai
 

Similar to Async design with Unity3D (20)

後戻りのある手続き型プログラミング
後戻りのある手続き型プログラミング後戻りのある手続き型プログラミング
後戻りのある手続き型プログラミング
Naohiro Yoshikawa
 
Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.Context
Akira Takahashi
 
Windows 8時代のUXを支える非同期プログラミング
Windows 8時代のUXを支える非同期プログラミングWindows 8時代のUXを支える非同期プログラミング
Windows 8時代のUXを支える非同期プログラミング
Yuya Yamaki
 
yieldとreturnの話
yieldとreturnの話yieldとreturnの話
yieldとreturnの話
bleis tift
 
Interaction channel
Interaction channelInteraction channel
Interaction channel
信之 岩永
 
Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~
CHY72
 
Orange Cube 自社フレームワーク 2015/3
Orange Cube 自社フレームワーク 2015/3Orange Cube 自社フレームワーク 2015/3
Orange Cube 自社フレームワーク 2015/3
信之 岩永
 
Unity の Coroutine は何が便利なのか
Unity の Coroutine は何が便利なのかUnity の Coroutine は何が便利なのか
Unity の Coroutine は何が便利なのか
Yu Takahashi
 
脱UniRx&Croutineから始めるUniTask
脱UniRx&Croutineから始めるUniTask脱UniRx&Croutineから始めるUniTask
脱UniRx&Croutineから始めるUniTask
Euglenaching
 
async/await不要論
async/await不要論async/await不要論
async/await不要論
bleis tift
 
C#勉強会 ~ C#9の新機能 ~
C#勉強会 ~ C#9の新機能 ~C#勉強会 ~ C#9の新機能 ~
C#勉強会 ~ C#9の新機能 ~
Fujio Kojima
 
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)
信之 岩永
 
C#6.0の新機能紹介
C#6.0の新機能紹介C#6.0の新機能紹介
C#6.0の新機能紹介
Kazunori Hamamoto
 
UniRx - Reactive Extensions for Unity
UniRx - Reactive Extensions for UnityUniRx - Reactive Extensions for Unity
UniRx - Reactive Extensions for Unity
Yoshifumi Kawai
 
C# 8.0 非同期ストリーム
C# 8.0 非同期ストリームC# 8.0 非同期ストリーム
C# 8.0 非同期ストリーム
信之 岩永
 
FP習熟度レベルとFSharpxのIteratee
FP習熟度レベルとFSharpxのIterateeFP習熟度レベルとFSharpxのIteratee
FP習熟度レベルとFSharpxのIteratee
pocketberserker
 
C#coding guideline その2_20130325
C#coding guideline その2_20130325C#coding guideline その2_20130325
C#coding guideline その2_20130325
Yoshihisa Ozaki
 
後戻りのある手続き型プログラミング
後戻りのある手続き型プログラミング後戻りのある手続き型プログラミング
後戻りのある手続き型プログラミング
Naohiro Yoshikawa
 
Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.Context
Akira Takahashi
 
Windows 8時代のUXを支える非同期プログラミング
Windows 8時代のUXを支える非同期プログラミングWindows 8時代のUXを支える非同期プログラミング
Windows 8時代のUXを支える非同期プログラミング
Yuya Yamaki
 
yieldとreturnの話
yieldとreturnの話yieldとreturnの話
yieldとreturnの話
bleis tift
 
Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~
CHY72
 
Orange Cube 自社フレームワーク 2015/3
Orange Cube 自社フレームワーク 2015/3Orange Cube 自社フレームワーク 2015/3
Orange Cube 自社フレームワーク 2015/3
信之 岩永
 
Unity の Coroutine は何が便利なのか
Unity の Coroutine は何が便利なのかUnity の Coroutine は何が便利なのか
Unity の Coroutine は何が便利なのか
Yu Takahashi
 
脱UniRx&Croutineから始めるUniTask
脱UniRx&Croutineから始めるUniTask脱UniRx&Croutineから始めるUniTask
脱UniRx&Croutineから始めるUniTask
Euglenaching
 
async/await不要論
async/await不要論async/await不要論
async/await不要論
bleis tift
 
C#勉強会 ~ C#9の新機能 ~
C#勉強会 ~ C#9の新機能 ~C#勉強会 ~ C#9の新機能 ~
C#勉強会 ~ C#9の新機能 ~
Fujio Kojima
 
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)
信之 岩永
 
UniRx - Reactive Extensions for Unity
UniRx - Reactive Extensions for UnityUniRx - Reactive Extensions for Unity
UniRx - Reactive Extensions for Unity
Yoshifumi Kawai
 
C# 8.0 非同期ストリーム
C# 8.0 非同期ストリームC# 8.0 非同期ストリーム
C# 8.0 非同期ストリーム
信之 岩永
 
FP習熟度レベルとFSharpxのIteratee
FP習熟度レベルとFSharpxのIterateeFP習熟度レベルとFSharpxのIteratee
FP習熟度レベルとFSharpxのIteratee
pocketberserker
 
C#coding guideline その2_20130325
C#coding guideline その2_20130325C#coding guideline その2_20130325
C#coding guideline その2_20130325
Yoshihisa Ozaki
 
Ad

Async design with Unity3D

  • 3. ゲーム ループ ● ゲームでは、どこか大元でループが回ってる while (isAlive) { // 固定 FPS なら、所定の時間が来るまで Sleep gameTime = … // ゲーム時間を進める foreach (var obj in gameObjects) { obj.Update (gameTime); } } 1フレームに1回よばれる処理
  • 4. 重たい処理 ● フレームレートよりも時間がかかる処理をし ちゃダメ ダメな例 string Load(string path) { // 30ミリ秒くらいかかるものとする var data = ファイルからバイナリロード(path);     // これも30ミリ秒くらいかかるものとする return デシリアライズ(data);     // 30 FPSだと、このメソッドは33ミリ秒以内に終えないと処理落ち } 2回に分けたい
  • 5. そこで、イテレーター ● イテレーターをコルーチンとして使う ゲーム ループ中で、毎フレーム MoveNextを呼んでもらう IEnumerator Load(string path, Action<string> callback) { var data = ファイルからバイナリロード(path); 1フレーム目     yield return null; callback(デシリアライズ(data)); 2フレーム目     yield return null; } returnの代わりに コールバック呼び出し
  • 6. before/after (1) ● メソッド宣言 before string Load(string path) after IEnumerator Load(string path, Action<string> callback) ● 戻り値はIEnumerator固定 ● 本来の戻り値はcallbackごしに返す
  • 7. before/after (2) ● return before return result; after callback(result); ● returnステートメントの代わりにcallback呼び出し
  • 8. before/after (3) ● 呼び出し側 before var x = Load("path"); …(後続の処理) after StartCoroutine(Load("path", x => { …後続の処理 }); ● 戻り値を直接受け取れない ● 形式上の戻り値(IEnumerator)はコルーチン起動のた めに使う ● 匿名関数を使って後続の処理をつなぐ
  • 10. 問題 ● 複数のコルーチンを扱いにくい StartCoroutine(A); StartCoroutine(B); ● A, B両方が完了するのを待ちたいときはどうする? ● Aの完了後にBを開始したいときはどうする? ● 特に、Aの(本来の)戻り値をBで使いたいときは? ● エラーの伝播を考えるとさらに面倒
  • 11. Taskクラス ● こういうクラスを用意 public class Task<T> : IEnumerator { IEnumerator Routine; public T Result { get; } public Exception Error { get; } public void OnComplete(… callback); public Task<T> ContinueWith<U>(… continuation); } ● 継続処理の登録 ● (本来の)戻り値やエラーの伝播
  • 12. 実例 ● ロードオブナイツのマップ ● 64万要素程度の配列をJSONで受け取ってた ● 通信もコルーチン ● 通信エラーが発生する可能性あり ● デコードもそこそこ高負荷なのでコルーチン化したい ● (行単位でデコード、1フレームに1行ずつとか) ● 通信の結果を使う ● デコード エラーが発生する可能性あり ● ローカル ストレージにキャッシュしたい ● IOエラーが発生する可能性あり ※ 最新バージョンではデータを小分けで受信するように改善され、デコード 処理はコルーチンではなくなっている
  • 13. Task利用例(戻り値) ● (本来の)戻り値の伝播 IEnumerator A(Action<int> callback); IEnumerator B(int x, Action<string> callback); IEnumerator C(string s); A, B, C の順で実行したい A の戻り値(int)を B で、B の戻り値(string)を C で使いたい 同期処理なら C(B(A()); だけで書けるもの var t = new Task<int>(A) .ContinueWith<string>(B) .ContinueWith(C); StartCoroutine(t);
  • 14. 同期処理と比べて ● 同期処理との対比 IEnumerator A(Action<int> callback); IEnumerator B(int x, Action<string> callback); IEnumerator C(string s); int A(); string B(int x); void C(string s); var t = new Task<int>(A) var x = A(); .ContinueWith<string>(B) var s = B(x); .ContinueWith(C); C(s); StartCoroutine(t); あるいは C(B(A()));
  • 15. Task利用例(例外処理) ● コルーチン内で発生した例外も受け取れる var t = new Task<int>(A) .ContinueWith<string>(B) .ContinueWith(C) .OnComplete(t => ● A, B, C のどこかで例外が発 { 生した場合、そこでコルーチン if (t.Error != null) … の実行は中断。 }); ● 発生した例外はErrorプロパ ティにセットされる StartCoroutine(t);