うにてぃブログ

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

【Unity】コルーチンを MonoBehaviour を使わずに呼び出す

コルーチンを使う場合 MonoBehaviour クラスを継承している必要があり
またオブジェクトがアクティブである必要がある

色々制限があって面倒だったので、GlobalCoroutine を作成しました

これは内部で CoroutineManager を生成しており、特に意識することなく利用できる

使い方

private void Test()
{
    // コルーチン開始
    GlobalCoroutine.Play(C());
 
    // コールバックを利用して終わりの検知もできる
    GlobalCoroutine.Play(C(), () => Debug.Log("end"));
 
    // 内部でコルーチンをキャッシュしているので key を指定すれば途中で停止もできる
    GlobalCoroutine.Play(C(), id: "key");
 
    // コルーチンの停止
    GlobalCoroutine.Stop("key");
}


private IEnumerator C()
{
    Debug.Log("begin");
    yield return new WaitForSeconds(1f);

    yield return new WaitForSeconds(1f);

    Debug.Log("end");
}

コード

using System;
using System.Collections;
using UnityEngine;
 
public static class GlobalCoroutine
{
    private static readonly CoroutineManager _i;
 
    static GlobalCoroutine()
    {
        var obj = new GameObject("CoroutineManager");
        _i = obj.AddComponent<CoroutineManager>();
        GameObject.DontDestroyOnLoad(_i);
    }
 
    /// <summary>
    /// Coroutine を開始する
    /// </summary>
    public static void Play(IEnumerator e, Action end = null, string key = "")
    {
        _i.Play(e, end, key);
    }
 
    public static void Stop(string key)
    {
        _i.Stop(key);
    }
 
    public static void StopAll()
    {
        _i.StopAll();
    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
internal class CoroutineManager : MonoBehaviour
{
    private Dictionary<string, Coroutine> _dic = new Dictionary<string, Coroutine>();
 
    internal void Stop(string key)
    {
        if (!_dic.ContainsKey(key))
        {
            Debug.LogWarning($"{key} not found");
            return;
        }
 
        StopCoroutine(_dic[key]);
        _dic[key] = null;
        _dic.Remove(key);
    }
 
    internal void StopAll()
    {
        StopAllCoroutines();
    }
 
    internal void Play(IEnumerator e, Action end = null, string key = "")
    {
        var isCache = !string.IsNullOrEmpty(key) && !_dic.ContainsKey(key);
        if (isCache)
        {
            end += () =>
            {
                if (_dic.ContainsKey(key))
                    _dic.Remove(key);
            };
        }
        var c = StartCoroutine(Coroutine(e, end));
        if (isCache)
        {
            _dic.Add(key, c);
        }
    }
 
    private IEnumerator Coroutine(IEnumerator e, Action end = null)
    {
        yield return e;

        end?.Invoke();
    }
}