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)

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

Similar to Async design with Unity3D (20)

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

Recently uploaded (8)

PDF
Hyperledger Fabric公式サンプル fabric-samples徹底解説
LFDT Tokyo Meetup
 
PDF
[Hardening Designers Confernece 2025]ランサムウェアでの見えざるログ・見えるログ
kataware
 
PDF
20250710_Devinで切り拓くDB革命_〜価値創出に集中せよ〜.pdf
Masaki Yamakawa
 
PDF
Hyperledger Fabric最新v3.x系での機能強化、変更点にキャッチアップ!
LFDT Tokyo Meetup
 
PDF
PostgreSQL18新機能紹介(db tech showcase 2025 発表資料)
NTT DATA Technology & Innovation
 
PDF
20250711_日本IBM ミドルウエア・ユーザー研究会(JIMUC)総会_中村会長資料.pdf
ChikakoInami1
 
PDF
プライバシ保護のためのインターネットアーキテクチャの進化 (2025-07-11)
Jun Kurihara
 
PDF
人気ブロックチェーン基盤「Hyperledger Fabric」最新版を動かしてみた!
LFDT Tokyo Meetup
 
Hyperledger Fabric公式サンプル fabric-samples徹底解説
LFDT Tokyo Meetup
 
[Hardening Designers Confernece 2025]ランサムウェアでの見えざるログ・見えるログ
kataware
 
20250710_Devinで切り拓くDB革命_〜価値創出に集中せよ〜.pdf
Masaki Yamakawa
 
Hyperledger Fabric最新v3.x系での機能強化、変更点にキャッチアップ!
LFDT Tokyo Meetup
 
PostgreSQL18新機能紹介(db tech showcase 2025 発表資料)
NTT DATA Technology & Innovation
 
20250711_日本IBM ミドルウエア・ユーザー研究会(JIMUC)総会_中村会長資料.pdf
ChikakoInami1
 
プライバシ保護のためのインターネットアーキテクチャの進化 (2025-07-11)
Jun Kurihara
 
人気ブロックチェーン基盤「Hyperledger Fabric」最新版を動かしてみた!
LFDT Tokyo Meetup
 
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);