Unity
Unity拡張

UnityEditor上でStartContinueっぽいのを動かす

More than 3 years have passed since last update.

UnityはEditor上でStartContinueでコルーチンが使えないのが結構不便だったので、作ってみた。

yield return 0; //1フレーム待つ

とか

yield return new EditorCoroutine.WaitForSeconds(1f); //1秒待つ

とか



WWW www = new WWW ("http:// 〜〜〜〜");

yield return www //ダウンロード完了まで待つ;



といった、StartContinueでできることはだいたいできる。

使い方は

1, 以下のコードを書いたc#スクリプトをEditorフォルダにいれる

2, StartContinue(hoge());の代わりに、EditorCoroutine.Start(hoge());を呼ぶ( hoge()はIEnumrator )

要するにStartCorutineとほとんど同じです。


using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// エディター上でStartCorutineのような処理を使用可能にするクラスです
/// </summary>
[UnityEditor.InitializeOnLoad]
public sealed class EditorCoroutine {

static EditorCoroutine () {
EditorApplication.update += Update;
Debug.Log("EditorCoroutine SetUp");
}

static Dictionary<IEnumerator, EditorCoroutine.Coroutine> asyncList = new Dictionary<IEnumerator, Coroutine>();
static List<EditorCoroutine.WaitForSeconds> waitForSecondsList = new List<EditorCoroutine.WaitForSeconds>();

static void Update () {

CheackIEnumerator ();
CheackWaitForSeconds();
}

static void CheackIEnumerator () {
List<IEnumerator> removeList = new List<IEnumerator>();
foreach(KeyValuePair<IEnumerator, EditorCoroutine.Coroutine> pair in asyncList){
if(pair.Key != null){

//IEnumratorのCurrentがCoroutineを返しているかどうか
EditorCoroutine.Coroutine c = pair.Key.Current as EditorCoroutine.Coroutine;
if(c != null){
if(c.isActive) continue;
}
//wwwクラスのダウンロードが終わっていなければ進まない
WWW www = pair.Key.Current as WWW;
if(www != null){
if(!www.isDone) continue;
}
//これ以上MoveNextできなければ終了
if(!pair.Key.MoveNext()){
if(pair.Value != null){
pair.Value.isActive = false;
}
removeList.Add(pair.Key);
}
}else {
removeList.Add(pair.Key);
}
}

foreach(IEnumerator async in removeList) {
asyncList.Remove(async);
}
}

static void CheackWaitForSeconds () {
for(int i=0; i<waitForSecondsList.Count; i++){
if(waitForSecondsList[i] != null){
if(EditorApplication.timeSinceStartup - waitForSecondsList[i].InitTime > waitForSecondsList[i].Time){
waitForSecondsList[i].isActive = false;
waitForSecondsList.RemoveAt(i);
}
}else {
Debug.LogError("rem");
waitForSecondsList.RemoveAt(i);
}
}
}

//=====================================================================================
//関数

/// <summary>
/// コルーチンを起動します
/// </summary>
static public EditorCoroutine.Coroutine Start (IEnumerator iEnumerator) {
if(Application.isEditor && !Application.isPlaying){
EditorCoroutine.Coroutine c = new Coroutine();
if(!asyncList.Keys.Contains(iEnumerator)) asyncList.Add(iEnumerator, c);
iEnumerator.MoveNext();
return c;
}else {
Debug.LogError("EditorCoroutine.Startはゲーム起動中に使うことはできません");
return null;
}
}

/// <summary>
/// コルーチンを停止します
/// </summary>
static public void Stop (IEnumerator iEnumerator) {
if(Application.isEditor){
if(asyncList.Keys.Contains(iEnumerator)){
asyncList.Remove(iEnumerator);
}
}else {
Debug.LogError("EditorCoroutine.Startはゲーム中に使うことはできません");
}
}

/// <summary>
/// 使用不可
/// WaitForSecondsのインスタンスを登録します
/// </summary>
static public void AddWaitForSecondsList (EditorCoroutine.WaitForSeconds coroutine){
if(waitForSecondsList.Contains(coroutine) == false){
waitForSecondsList.Add(coroutine);
}
}

//=====================================================================================
//待機処理用クラス

public class Coroutine {
//trueなら待機させる
public bool isActive;

public Coroutine() {
isActive = true;
}
}

public sealed class WaitForSeconds : EditorCoroutine.Coroutine {
private float time;
private double initTime;

public float Time {
get{return time;}
}
public double InitTime {
get{return initTime;}
}

public WaitForSeconds(float time) : base(){
this.time = time;
this.initTime = EditorApplication.timeSinceStartup;
EditorCoroutine.AddWaitForSecondsList(this);
}
}
}

仕組みとしては、Start関数でIEnumratorの関数を登録しておいて、EditorApplication.updateのデリゲート登録したUpdate関数のループのなかで、IEnumratorのMoveNext関数を呼ぶかどうかをチェックしている感じです。

コルーチンが動いている最中ににスクリプトのコンパイルが入ったり、予期せぬ割り込みが入ってしまうとよくないかも…

注) AddWaitForSecondsListは使わないこと C#にはfriendとかないので仕方なくpublicになってます