LoginSignup
31
15

More than 3 years have passed since last update.

UnityのEditorCoroutineUtilityを使い、何か重要なバッチを走らせている風なダイアログを作る

Posted at
  • 使用Unityバージョン: 2019.3.1f1

Editor拡張はお好きでしょうか?
Editor拡張はとても便利ですが、拡張内でコルーチンが使えないという問題が昔からあると思います。
そんな中、UnityのPackageManagerにEditorCoroutineという機能がUnity2018.1ごろから追加されています。
今回はそのEditorCoroutineを使って、「何かわからないけど重要なことしてる風」なプログレスバーを出すEditor拡張を書きます。

この記事で得られるもの

DummyProgress.png
このような、時間を指定するとその時間だけプログレスバーをUnityEditor上で出す事が出来ます。
ちょっとした離席時などに検査走らせているからUnity内での作業が進められない。と言ったアリバイ作りが出来ます

EditorCoroutineの何が嬉しいの?

前述した通り、Editor拡張は公式ではコルーチンの機能が使えませんでした。
(UniRxのFromCoroutineを使ったりする必要がありました)
なので、Editor拡張中に1秒待ちたいと言ったような実装が必要な場合、手間がかかってしまっていました。
EditorCoroutineはPackageManagerから入れる事ができるので、手間としては大分楽になったと思います。

EditorCoroutineの導入方法

UnityのPackageManagerから導入が出来ます。2020年3月11日現在、プレビュー版です。
editorコルーチン.png

EditorCoroutineの使い方

まずEditorフォルダ内にEditorWindowを継承したEditor拡張用のクラスを作ります。
そうしたらusingに
using Unity.EditorCoroutines.Editor;
を追加します。
スクリーンショット 2020-03-11 3.04.52.png
ここから先は通常のEditor拡張の分野ですので、割愛しますがEditorWindowの処理とGUIを作ります。

GUIを設定したら、コルーチンの処理を記述します。 関数は通常のコルーチンと同じく、IEnumeratorにします。

private IEnumerator ShowProgressBar(string progressTime)
    {
        float loopTime = 100f;
        float.TryParse(progressTime, out loopTime);

        for (int i = 0; i < 100; i++)
        {
            float progress = (float)i / 100;
            EditorUtility.DisplayProgressBar (
                "検査中", 
                (progress * 100).ToString("F2") + "%", 
                progress
            );
            yield return new EditorWaitForSeconds(loopTime / 100f);
        }
        EditorUtility.DisplayProgressBar (
            "検査が完了しました", 
            (100).ToString("F2") + "%)", 
            100
        );
        if (EditorUtility.DisplayDialog("検査が完了しました", "異常は見つかりませんでした", "OK"))
        {
            EditorUtility.ClearProgressBar();
        }
    }

ここではEditorUtility.DisplayProgressBarでプログレスバーを表示しています。
注目するべきは
yield return new EditorWaitForSeconds(loopTime / 100f);
の部分です。
こちらは通常のWaitForSecoundsでは無く、EditorWaitForSecondsを指定する事でEditor拡張の中で指定秒数待機します。
WaitForSecoundsではコンパイルエラーにはなりませんが、正しく動かないので注意しましょう。

ここで作成したコルーチンを呼び出すには、以下のようなEditorCoroutine型の変数とボタンを用意します。

private EditorCoroutine m_coroutine;
        using (new GUILayout.HorizontalScope())
        {
            if (GUILayout.Button("表示"))
            {
                m_coroutine = EditorCoroutineUtility.StartCoroutine(ShowProgressBar(progressTime), this);
            }
            if (GUILayout.Button("Stop"))
            {
                if (m_coroutine != null)
                {
                    EditorCoroutineUtility.StopCoroutine(m_coroutine);
                    EditorUtility.ClearProgressBar();
                }
            }
        }

この処理をOnGUIに書く事で、表示とStopのボタンが出てくると思います。

そして先ほど作成したShowProgressBarを呼ぶ際には、
EditorCoroutineUtility.StartCoroutine(ShowProgressBar(progressTime), this);
と言った指定が必要です。
基本的には通常のStartCoroutineと同じですが、第二引数のコルーチンのオーナーを指定する必要があります。
(StartCoroutineOwnerlessと言うのもあります)
ここはthisを指定してあげればほぼほぼ間違いはないと思います。

Stopの処理も同様に
EditorCoroutineUtility.StopCoroutine
を呼び、EditorUtility.ClearProgressBar();でプログレスバーを閉じています。
(ProgressBarは必ず閉じる際にEditorUtility.ClearProgressBar();を呼びましょう)

ここまでやると開始ボタンを押す事で、以下のような挙動になります。指定した秒数でプログレスバーが100%になれば成功です。
(指定した値が1秒など小さすぎると1秒以上かかってしまったりします。数字の処理は厳密ではありません)

DummyProgress.png

DummyComplete.png

今回のサンプルのソースコード

今回作成したサンプルのソースコードは以下になります。

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

public class ShowDummyProgress : EditorWindow
{
    private string progressTime = "100";

    private EditorCoroutine m_coroutine;

    [MenuItem("Editor/DummyProgress")]
    private static void Create()
    {
        GetWindow<ShowDummyProgress>("ダミープログレスバー");
    }

    private void OnGUI()
    {
        using (new GUILayout.HorizontalScope())
        {
            GUILayout.Label("時間");
            progressTime = GUILayout.TextField(progressTime);
        }

        using (new GUILayout.HorizontalScope())
        {
            if (GUILayout.Button("表示"))
            {
                m_coroutine = EditorCoroutineUtility.StartCoroutine(ShowProgressBar(progressTime), this);
            }
            if (GUILayout.Button("Stop"))
            {
                if (m_coroutine != null)
                {
                    EditorCoroutineUtility.StopCoroutine(m_coroutine);
                    EditorUtility.ClearProgressBar();
                }
            }
        }
    }

    private IEnumerator ShowProgressBar(string progressTime)
    {
        float loopTime = 100f;
        float.TryParse(progressTime, out loopTime);

        for (int i = 0; i < 100; i++)
        {

            float progress = (float)i / 100;
            EditorUtility.DisplayProgressBar (
                "検査中", 
                (progress * 100).ToString("F2") + "%", 
                progress
            );
            yield return new EditorWaitForSeconds(loopTime / 100f);
        }
        EditorUtility.DisplayProgressBar (
            "検査が完了しました", 
            (100).ToString("F2") + "%", 
            100
        );
        if (EditorUtility.DisplayDialog("検査が完了しました", "異常は見つかりませんでした", "OK"))
        {
            EditorUtility.ClearProgressBar();
        }
    }
}
31
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
15