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

Unity のコルーチンの動作をエスパーして再現してみた

More than 3 years have passed since last update.

はじめに

Unity 5.3 で待望の カスタムコルーチン(CustomYieldInstruction) が使えるようになります。今までよりコルーチンが便利になるのですが、そもそもコルーチンの動作がよくわからないから何がうれしいのかわからないという声もよく聞きます。

Unity のコルーチンの動作は初心者でも他の言語の経験者であっても詰まることがあります。特に内部的な実装が見えない分何が起こっているかわからず、エスパー能力を試されている気がします。今回はそういう人のために基本的なコルーチンの動作を再現するスクリプトを書いてみました。

コードは読めるけどコルーチンで何が起こっているかわからん!というかたはなにかの足がかりになるかもしれません。もちろん私も内部の実装みたわけではないので、こんな感じのことが起こっているという雰囲気で読んでもらえればうれしいです。

コード

// MyMonoBehaviour.cs
using System.Collections;
using System.Collections.Generic;

public abstract class YieldInstruction
{
    public abstract bool KeepWaiting { get; }
}

public class Coroutine : YieldInstruction
{
    readonly IEnumerator enumerator;
    YieldInstruction currentYieldInstruction;

    public Coroutine(IEnumerator enumerator)
    {
        this.enumerator = enumerator;
    }

    public override bool KeepWaiting
    {
        get
        {
            if (currentYieldInstruction != null)
            {
                if (currentYieldInstruction.KeepWaiting)
                {
                    return true;
                }

                currentYieldInstruction = null;
            }

            if (enumerator.MoveNext())
            {
                var maybeYieldInstruction = enumerator.Current as YieldInstruction;
                if (maybeYieldInstruction != null && maybeYieldInstruction.KeepWaiting)
                {
                    currentYieldInstruction = maybeYieldInstruction;
                }

                return true;
            }

            return false;
        }
    }
}

public class WaitForSeconds : YieldInstruction
{
    readonly float secondsToWait;
    float elapsedSec;

    public override bool KeepWaiting
    {
        get
        {
            elapsedSec += UnityEngine.Time.deltaTime;
            return elapsedSec < secondsToWait;
        }
    }

    public WaitForSeconds(float secondsToWait)
    {
        this.secondsToWait = secondsToWait;
    }
}


public class MonoBehaviour
{
    readonly List<YieldInstruction> yieldInstructions = new List<YieldInstruction>();

    public Coroutine StartCoroutine(IEnumerator enumerator)
    {
        var coroutine = new Coroutine(enumerator);
        yieldInstructions.Add(coroutine);
        return coroutine;
    }

    // たぶん内部的にこういうのがいる
    public void UpdateYieldInstructions()
    {
        foreach (var yieldInstruction in new List<YieldInstruction>(yieldInstructions))
        {
            if (!yieldInstruction.KeepWaiting)
            {
                yieldInstructions.Remove(yieldInstruction);
            }
        }
    }
}

public class CoroutineTestBehaviour : MonoBehaviour
{
    CoroutineTestBehaviour2 coroutineTest2;

    public void Awake()
    {
        coroutineTest2 = new CoroutineTestBehaviour2();
        StartCoroutine(AsyncMethod());
    }

    IEnumerator AsyncMethod()
    {
        UnityEngine.Debug.Log("a");
        yield return new WaitForSeconds(1f);
        UnityEngine.Debug.Log("b");
        yield return new WaitForSeconds(2f);
        UnityEngine.Debug.Log("c");
        yield return StartCoroutine(coroutineTest2.AsyncMethod2());
    }
}

public class CoroutineTestBehaviour2 : MonoBehaviour
{
    public IEnumerator AsyncMethod2()
    {
        UnityEngine.Debug.Log("a2");
        yield return new WaitForSeconds(1f);
        UnityEngine.Debug.Log("b2");
        yield return new WaitForSeconds(2f);
        UnityEngine.Debug.Log("c2");
    }
}

呼び出し元。これを何らかの GameObject に貼り付けてください。

// MyMonoBehaviourRunner.cs
public class MyMonoBehaviourRunner : UnityEngine.MonoBehaviour
{
    CoroutineTestBehaviour coroutineTest;

    void Awake()
    {
        UnityEngine.Debug.Log("awake runner");
        coroutineTest = new CoroutineTestBehaviour();
        coroutineTest.Awake();
    }

    void Update()
    {
        coroutineTest.UpdateYieldInstructions();
    }
}

おわりに

そのうち解説書けるといいな。
この記事は @chiepomme Advent Calender 2015 8日目です。

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
ユーザーは見つかりませんでした