はんなりと、ゆるやかに

アジャイル、スクラムが好きが日々から学んだことをアウトプット

Unity コルーチンの一時停止と終了を調べる

前回、Unityのコルーチンとは何なのか調べてみました。
iucstscui.hatenablog.com

前回のコードを思い出すと、StartCoroutineが何度も呼べる状態になっており、コルーチンを複数スタートできるようになっていました。動作確認した結果が以下です。

ボタンを2回クリックすると2個のコルーチンが同時にテキストを更新してカウントアップが異常な状態です。

スタート後のクリックは一時停止させる

そこで、スタート後のクリックは一時停止にしようと決めました。コルーチンを一時停止させる方法はStopCoroutineがあると知ります。そこで書いてみたコードは以下です。

    public GameObject text = null;
    Coroutine coroutine = null;
    bool isStart = false;

    public void OnClick()
    {
        if (isStart == false)
        {
            isStart = true;
            coroutine = StartCoroutine(Count());
        }
        else 
        {
            StopCoroutine(coroutine);
            isStart = false;
        }
    }

    IEnumerator Count()
    {
        for(int i = 1; i <= 10; ++i)
        {
            text.GetComponent<Text>().text = i.ToString();
            yield return new WaitForSeconds(.5f);
        }

        yield break;
    }

一時停止ではなく再度先頭から再開されています。ミスです。StartCoroutineの引数に渡している「IEnumerator型」がStopで停止したコルーチンと一致しないため、新しくコルーチンが開始されているようです。

ということで、「IEnumerator型」をメンバ変数で定義して、Start関数で代入します。クリックされた際はメンバ変数を渡すことで、一時停止が完成しました。

   public GameObject text = null;
    IEnumerator enumerator = null;
    bool isStart = false;

    // Start is called before the first frame update
    void Start()
    {
        enumerator = Count();
    }

    public void OnClick()
    {
        if (isStart == false)
        {
            isStart = true;
            StartCoroutine(enumerator);
        }
        else
        {
            StopCoroutine(enumerator);
            isStart = false;
        }
    }

    IEnumerator Count()
    {
        for (int i = 1; i <= 10; ++i)
        {
            text.GetComponent<Text>().text = i.ToString();
            yield return new WaitForSeconds(.5f);
        }

        yield break;
    }

目的を達成しましたが、ここで欲が出てきます。10までカウントアップした後は1から再開したい。そのためにはコルーチンが終了したことを知りたいのですが、終了を知る直接的な方法はなさそうです。常套手段はActionもしくはUnityActionを使用するようです。

コルーチン終了後、再開させる

「IEnumerator型」オブジェクトのインスタンスがStopCoroutineと違う場合、先頭から開始されます。その仕様とUnityActionを組み合わせて、実現してみましょう。コルーチン終了後にメンバ変数のenumerator はnullにします。クリック時にenumeratorがnullの場合は新しいインスタンスを作る方法でコルーチン終了後、再開させる仕組みを実現しました。

   public GameObject text = null;
    IEnumerator enumerator = null;
    bool isStart = false;

    public void OnClick()
    {
        if (enumerator == null)
        {
            isStart = false;
            enumerator = Count((result) => enumerator = result);
        }

        if (isStart == false)
        {
            isStart = true;
            StartCoroutine(enumerator);
        }
        else 
        {
            StopCoroutine(enumerator);
            isStart = false;
        }
    }

    IEnumerator Count(UnityEngine.Events.UnityAction<IEnumerator> callback)
    {
        for(int i = 1; i <= 10; ++i)
        {
            text.GetComponent<Text>().text = i.ToString();
            yield return new WaitForSeconds(.5f);
        }

        callback(null);
        yield break;
    }


まとめ

  • StopCoroutine でコルーチンを一時停止できる
  • StopCoroutineと同じ引数でStartCoroutineを呼ぶと停止位置から再開できる
  • Coroutineの終了を知るにはActionもしくはUnityActionを使用する。