【Unity】Unity Editorでコルーチンが使える「Editor Coroutines」の使い方まとめ

Unity Editorでコルーチンが使える「Editor Coroutines」の使い方をまとめました。

Unity2020.2.7f1

Editor Coroutines?

Editor Coroutinesはエディタでコルーチンを使うための機能です。
Unityの公式パッケージとして配布されています。

docs.unity3d.com

ランタイムで使うコルーチンはMonoBehaviourが必要ですが、Editor CoroutinesはMonoBehaviourに依存しない仕組みになっています。

要件はUnity2018.1以上で、Unity2020.2でパッケージが正式リリースになりました。

インストール

Editor CoroutinesはPackage Managerからインストールできます。

f:id:halya_11:20210602210753p:plain
インストール

Package Managerの使い方は以下の記事にまとめていますので、必要に応じて参照してください。

light11.hatenadiary.com

EditorWindowでコルーチンを使う

それでは実際にEditor Coroutinesを使ってみます。
まずEditorWindowで使うには以下のようにEditorWindowの拡張メソッドとして定義されているStartCoroutineStopCoroutineを使います。

using System.Collections;
using Unity.EditorCoroutines.Editor;
using UnityEditor;
using UnityEngine;

public class ExampleWindow : EditorWindow
{
    private EditorCoroutine _coroutine;

    private void OnEnable()
    {
        // コルーチンを開始
        _coroutine = this.StartCoroutine(LogTimeSinceStartup());
    }

    private void OnDisable()
    {
        // コルーチンを停止
        this.StopCoroutine(_coroutine);
    }

    [MenuItem("Window/Example")]
    private static void Open()
    {
        GetWindow<ExampleWindow>();
    }

    private IEnumerator LogTimeSinceStartup()
    {
        while (true)
        {
            Debug.Log(Time.realtimeSinceStartup);
            yield return null;
        }
    }
}

使い勝手はランタイムのコルーチンと同様です。

EditorCoroutineUtilityを直接使う

さて前節ではEditorWindowの拡張メソッドを使いました。
この拡張メソッドのStopCoroutine()の中ではEditorCoroutineUtilityが使用されており、以下のようにこれを直接使うこともできます。

using System.Collections;
using Unity.EditorCoroutines.Editor;
using UnityEditor;
using UnityEngine;

public class ExampleWindow : EditorWindow
{
    private EditorCoroutine _coroutine;

    private void OnEnable()
    {
        // Ownerを指定してコルーチンを開始
        // OwnerがGCで回収された際にコルーチンが走ってたら止まる
        _coroutine = EditorCoroutineUtility.StartCoroutine(LogTimeSinceStartup(), this);
    }

    private void OnDisable()
    {
        // 本当はEditorWindowCoroutineExtension.StopCoroutine()を参考に制御するべき
        EditorCoroutineUtility.StopCoroutine(_coroutine);
    }

    [MenuItem("Window/Example")]
    private static void Open()
    {
        GetWindow<ExampleWindow>();
    }

    private IEnumerator LogTimeSinceStartup()
    {
        while (true)
        {
            Debug.Log(Time.realtimeSinceStartup);
            yield return null;
        }
    }
}

注意点として、EditorCoroutineUtility.StartCoroutine()の第二引数には何かしらのobjectを与えます。
これがGCで回収されたときにもしコルーチンが走ったままだったら、その時点でコルーチンが止まります。
しかしGCのタイミングは制御しづらいので実際には上の例のようにEditorCoroutineUtility.StopCoroutine()で止めたほうがよさそうです。

またコメントにも書きましたが、EditorCoroutineUtility.StopCoroutine()の部分はもう少し異常系の対応を入れるべきです。
前節の拡張メソッドEditorWindowCoroutineExtension.StopCoroutine()ではこのあたりの制御がされているので参考にして実装するとよさそうです。

オーナーを持たないEditiorCoroutineUtility.StartCoroutineOwnerless

さて前節のEditorCoroutineUtility.StartCoroutine()は第二引数に何かしらのobjectを与えました。
EditiorCoroutineUtility.StartCoroutineOwnerlessを使うと、これを指定しないでコルーチンを開始できます。
この場合、コルーチンが勝手に停止することはないので自身で停止する必要があります。

using System.Collections;
using Unity.EditorCoroutines.Editor;
using UnityEditor;
using UnityEngine;

public class ExampleWindow : EditorWindow
{
    private EditorCoroutine _coroutine;

    private void OnEnable()
    {
        // Ownerなしでコルーチンを開始
        _coroutine = EditorCoroutineUtility.StartCoroutineOwnerless(LogTimeSinceStartup());
    }

    private void OnDisable()
    {
        EditorCoroutineUtility.StopCoroutine(_coroutine);
    }

    [MenuItem("Window/Example")]
    private static void Open()
    {
        GetWindow<ExampleWindow>();
    }

    private IEnumerator LogTimeSinceStartup()
    {
        while (true)
        {
            Debug.Log(Time.realtimeSinceStartup);
            yield return null;
        }
    }
}

参考

docs.unity3d.com