- 使用Unityバージョン: 2019.3.1f1
Editor拡張はお好きでしょうか?
Editor拡張はとても便利ですが、拡張内でコルーチンが使えないという問題が昔からあると思います。
そんな中、UnityのPackageManagerにEditorCoroutineという機能がUnity2018.1ごろから追加されています。
今回はそのEditorCoroutineを使って、「何かわからないけど重要なことしてる風」なプログレスバーを出すEditor拡張を書きます。
この記事で得られるもの
このような、時間を指定するとその時間だけプログレスバーをUnityEditor上で出す事が出来ます。
ちょっとした離席時などに検査走らせているからUnity内での作業が進められない。と言ったアリバイ作りが出来ます
EditorCoroutineの何が嬉しいの?
前述した通り、Editor拡張は公式ではコルーチンの機能が使えませんでした。
(UniRxのFromCoroutineを使ったりする必要がありました)
なので、Editor拡張中に1秒待ちたいと言ったような実装が必要な場合、手間がかかってしまっていました。
EditorCoroutineはPackageManagerから入れる事ができるので、手間としては大分楽になったと思います。
EditorCoroutineの導入方法
UnityのPackageManagerから導入が出来ます。2020年3月11日現在、プレビュー版です。
EditorCoroutineの使い方
まずEditorフォルダ内にEditorWindow
を継承したEditor拡張用のクラスを作ります。
そうしたらusingに
using Unity.EditorCoroutines.Editor;
を追加します。
ここから先は通常の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秒以上かかってしまったりします。数字の処理は厳密ではありません)
今回のサンプルのソースコード
今回作成したサンプルのソースコードは以下になります。
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();
}
}
}