うにてぃブログ

UnityやUnreal Engineの記事を書いていきます

【Unity】複数の IEnumerator もしくは YieldInstruction の終了を待つ

複数の IEnumerator を待つ場合は以下のような処理を書く必要があり面倒だったので
手軽に複数の IEnumerator を待てないかと思い作成しました

   private void WaitAll(Action action)
    {
        var end = 0;
        void End()
        {
            if (++end >= 2)
            {
                action.Invoke();
                // End Call
            }
        }

        StartCoroutine(Wait1(End));
        StartCoroutine(Wait2(End));
    }

    private IEnumerator Wait1(Action end)
    {
        yield return new WaitForSeconds(3f);
        end.Invoke();
    }

    private IEnumerator Wait2(Action end)
    {
        yield return new WaitForSeconds(1f);
        end.Invoke();
    }

複数を待つコード

引数の IEnumerator を内部で StartCoroutine してすべての終了をチェックすることで複数を待てるようにしました

また、 よく使う YieldInstruction に関しても利用できるように対応してみました

params に両方利用できれば良かったのですが、厳しかったので別々で利用する形になりました

   public IEnumerator WaitAll(params YieldInstruction[] yis)
    {
        var endCount = 0;
        foreach (var yi in yis)
            StartCoroutine(PlayCoroutine(yi, () => endCount++));

        yield return new WaitUntil(() => endCount >= yis.Length);
    }

    public IEnumerator WaitAll(params IEnumerator[] enumerators)
    {
        var endCount = 0;
        foreach (var enumerator in enumerators)
            StartCoroutine(PlayCoroutine(enumerator, () => endCount++));

        yield return new WaitUntil(() => endCount >= enumerators.Length);
    }

    public void WaitAll(Action end, params YieldInstruction[] yis)
    {
        var endCount = 0;
        foreach (var yi in yis)
        {
            StartCoroutine(PlayCoroutine(yi, () =>
            {
                if (++endCount >= yis.Length)
                    end?.Invoke();
            }));
        }
    }

    public void WaitAll(Action end, params IEnumerator[] enumerators)
    {
        var endCount = 0;
        foreach (var enumerator in enumerators)
        {
            StartCoroutine(PlayCoroutine(enumerator, () =>
            {
                if (++endCount >= enumerators.Length)
                    end?.Invoke();
            }));
        }
    }

    private IEnumerator PlayCoroutine(YieldInstruction yi, Action end)
    {
        yield return yi;
        end?.Invoke();
    }

    private IEnumerator PlayCoroutine(IEnumerator enumerator, Action end)
    {
        yield return enumerator;
        end?.Invoke();
    }

サンプル

IEnumerator を複数利用した場合

   private IEnumerator Test()
    {
        yield return WaitAll(A(5f), B(3f));
    }

    private IEnumerator A(float seconds)
    {
        yield return new WaitForSeconds(seconds);
    }

    private IEnumerator B(float seconds)
    {
        yield return new WaitForSeconds(seconds);
    }

YieldInstruction を複数利用した場合

   private IEnumerator Test()
    {
        var a = new WaitForSeconds(1f);
        var b = new WaitForSeconds(3f);

        yield return WaitAll(a, b);
    }

IEnumeratorYieldInstruction を利用した場合

WaitAll の戻り値が IEnumerator なので、それを利用して一緒に呼び出します

見た目が少し悪いのは仕方なし

   private IEnumerator Test()
    {
        var a = new WaitForSeconds(1f);
        var b = new WaitForSeconds(3f);

        yield return WaitAll(WaitAll(a, b), A(5f));
    }

    private IEnumerator A(float seconds)
    {
        yield return new WaitForSeconds(seconds);
    }