Help us understand the problem. What is going on with this article?

Unity のコルーチンで結果を受け取る

More than 5 years have passed since last update.

Unity では非同期的な処理をコルーチンを使って書くことができます。
たとえば、

IEnumerator Hoge(){
    yield return StartCoroutine(Async());
}

IEnumerator Async(){
    Something.Run();
    while(!Something.Finished){
      yield return null;
    }
}

でもこれだと結果が受け取れません。結果を受け取るための方法には私が思いつくだけで3つあります。

  1. どちらのメソッドからも見える変数に値を代入する
  2. コールバック関数を渡して結果を返してもらう
  3. IEnumerator#Current を使用する

1. どちらのメソッドからも見える変数に値を代入する

int Result;

IEnumerator Hoge(){
    yield return StartCoroutine(Async());
    print(Result);
}

IEnumerator Async(){
    Something.Run();
    while(!Something.Finished){
      yield return null;
    }
    Result = 3535;
}

問題点

  • Result の寿命が Result を参照しているクラスだけではなく、このクラスの寿命にも影響されてしまうこと
  • Result のスコープが必要以上に広くなってしまう可能性があること
  • Async 結果を受け取るために Result を使用することを知らないといけない
  • Async を並列に走らせることができないこと

2. コールバック関数を渡して結果を返してもらう

IEnumerator Hoge(){
    int? result = null;
    yield return StartCoroutine(Async(r => result = r));
    print(result.Value);
}

IEnumerator Async(Action<int> callback){
    Something.Run();
    while(!Something.Finished){
      yield return null;
    }
    callback(3535);
}

問題点

  • ネストが増えるので読みにくくなる
  • 書き方によっては上から下に読み下せない

3. IEnumerator#Current を使用する

IEnumerator Hoge(){
    var coroutine = Async();
    yield return StartCoroutine(coroutine);
    var result = (int)coroutine.Current;
    print(result);
}

IEnumerator Async(){
    Something.Run();
    while(!Something.Finished){
      yield return null;
    }
    yield return 3535;
}

問題点

  • 型安全じゃない(generic の IEnumerator にしてもいいけれど、そうすると、 yield return [Coroutine Object] ができなくなるので、中で StartCoroutine できない)

いまのプロジェクトでは、間にラッパーを挟んで3の方法を使っています。なぜかというとコードがシーケンシャルになるからです。また、C# が採用している async/await に比較的近い見た目のコードになるからというのもあります。

おまけ

自前で IEnumerator を回した時に Unity の WaitForSeconds みたいな Coroutine を返すものを待ってくれないっていうことに躓くことがあります。

たとえば、こういうの

IEnumerator Hoge(){
    var coroutine = Wait();
    while(coroutine.MoveNext())
      yield return null;
    print("hoge");
}

IEnumerator Wait(){
    yield return new WaitForSeconds(1.0f);
}

Unity の Coroutine を待つためには、Coroutine オブジェクトを Current で返してあげないといけないのです。なので、

IEnumerator Hoge(){
    var coroutine = Wait();
    while(coroutine.MoveNext())
      yield return coroutine.Current; // null じゃなくて Coroutine を返す
    print("hoge");
}

IEnumerator Wait(){
    yield return new WaitForSeconds(1.0f);
}

にすれば動きます。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした