SlideShare a Scribd company logo
1 of 16
Download to read offline
ロードオブナイツで使ってる
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

関東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
 
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
Yoshifumi Kawai
 
配管流路の多目的最適化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
 
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
Yoshifumi Kawai
 

What's hot (20)

きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回
 
関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU
 
Boost.Coroutine
Boost.CoroutineBoost.Coroutine
Boost.Coroutine
 
C#次世代非同期処理概観 - Task vs Reactive Extensions
C#次世代非同期処理概観 - Task vs Reactive ExtensionsC#次世代非同期処理概観 - Task vs Reactive Extensions
C#次世代非同期処理概観 - Task vs Reactive Extensions
 
TensorFlow XLA 「XLAとは、から、最近の利用事例について」
TensorFlow XLA 「XLAとは、から、最近の利用事例について」TensorFlow XLA 「XLAとは、から、最近の利用事例について」
TensorFlow XLA 「XLAとは、から、最近の利用事例について」
 
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)
 
LINQ in Unity
LINQ in UnityLINQ in Unity
LINQ in Unity
 
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
 
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の歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
 
C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミング
 
Introduction to cython
Introduction to cythonIntroduction to cython
Introduction to cython
 
UniRxことはじめ
UniRxことはじめUniRxことはじめ
UniRxことはじめ
 
Visual C++コード分析を支えるSAL
Visual C++コード分析を支えるSALVisual C++コード分析を支えるSAL
Visual C++コード分析を支えるSAL
 
配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)
配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)
配管流路の多目的最適化OpenFOAM+OpenMDAO(第28回オープンCAE勉強会@関西)
 
More C++11
More C++11More C++11
More C++11
 
フラグを愛でる
フラグを愛でるフラグを愛でる
フラグを愛でる
 
Python で munin plugin を書いてみる
Python で munin plugin を書いてみるPython で munin plugin を書いてみる
Python で munin plugin を書いてみる
 
SEH on mingw32
SEH on mingw32SEH on mingw32
SEH on mingw32
 
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
 

Similar to Async design with Unity3D

Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.Context
Akira Takahashi
 
Javaセキュアコーディングセミナー東京第2回演習の解説
Javaセキュアコーディングセミナー東京第2回演習の解説Javaセキュアコーディングセミナー東京第2回演習の解説
Javaセキュアコーディングセミナー東京第2回演習の解説
JPCERT Coordination Center
 

Similar to Async design with Unity3D (20)

Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.Context
 
新しい並列for構文のご提案
新しい並列for構文のご提案新しい並列for構文のご提案
新しい並列for構文のご提案
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 
Rの高速化
Rの高速化Rの高速化
Rの高速化
 
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_cccJEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
 
x86とコンテキストスイッチ
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチ
 
Scalaの限定継続の応用と基本
Scalaの限定継続の応用と基本Scalaの限定継続の応用と基本
Scalaの限定継続の応用と基本
 
Scalaの限定継続の応用と基本(改訂版)
Scalaの限定継続の応用と基本(改訂版)Scalaの限定継続の応用と基本(改訂版)
Scalaの限定継続の応用と基本(改訂版)
 
ji-5. 繰り返し計算
ji-5. 繰り返し計算ji-5. 繰り返し計算
ji-5. 繰り返し計算
 
Junit4
Junit4Junit4
Junit4
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門
 
Javaセキュアコーディングセミナー東京第2回演習の解説
Javaセキュアコーディングセミナー東京第2回演習の解説Javaセキュアコーディングセミナー東京第2回演習の解説
Javaセキュアコーディングセミナー東京第2回演習の解説
 
C++14 Overview
C++14 OverviewC++14 Overview
C++14 Overview
 
ji-3. 条件分岐と場合分け
ji-3. 条件分岐と場合分けji-3. 条件分岐と場合分け
ji-3. 条件分岐と場合分け
 
20190625 OpenACC 講習会 第3部
20190625 OpenACC 講習会 第3部20190625 OpenACC 講習会 第3部
20190625 OpenACC 講習会 第3部
 
Boost Tour 1.50.0 All
Boost Tour 1.50.0 AllBoost Tour 1.50.0 All
Boost Tour 1.50.0 All
 
boost tour 1.48.0 all
boost tour 1.48.0 allboost tour 1.48.0 all
boost tour 1.48.0 all
 
Brief introduction of Boost.ICL
Brief introduction of Boost.ICLBrief introduction of Boost.ICL
Brief introduction of Boost.ICL
 
コルーチンを使おう
コルーチンを使おうコルーチンを使おう
コルーチンを使おう
 
cp-9. 再帰関数
cp-9. 再帰関数cp-9. 再帰関数
cp-9. 再帰関数
 

Recently uploaded

Recently uploaded (7)

新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 

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