모든 코루틴은 이로부터 접근을 해서 호출한다. 예)
EditorCoroutines.EditorCoroutines.StartCoroutine(코루틴 명칭, this);
EditorCoroutines.EditorCoroutines.StopCoroutine(코루틴 명칭, this);
using EditorCoroutine = EditorCoroutines.EditorCoroutines; 맨 위에 추가시 아래와 같이 생략 가능
EditorCoroutine.StartCoroutine(코루틴 명칭, this);
EditorCoroutine.StopCoroutine(코루틴 명칭, this);
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System;
using System.Reflection;
namespace EditorCoroutines
{
public class EditorCoroutines
{
public class EditorCoroutine
{
public ICoroutineYield currentYield = new YieldDefault();
public IEnumerator routine;
public string routineUniqueHash;
public string ownerUniqueHash;
public string MethodName = "";
public int ownerHash;
public string ownerType;
public bool finished = false;
public EditorCoroutine(IEnumerator routine, int ownerHash, string ownerType)
{
this.routine = routine;
this.ownerHash = ownerHash;
this.ownerType = ownerType;
ownerUniqueHash = ownerHash + "_" + ownerType;
if (routine != null)
{
string[] split = routine.ToString().Split('<', '>');
if (split.Length == 3)
{
this.MethodName = split[1];
}
}
routineUniqueHash = ownerHash + "_" + ownerType + "_" + MethodName;
}
public EditorCoroutine(string methodName, int ownerHash, string ownerType)
{
MethodName = methodName;
this.ownerHash = ownerHash;
this.ownerType = ownerType;
ownerUniqueHash = ownerHash + "_" + ownerType;
routineUniqueHash = ownerHash + "_" + ownerType + "_" + MethodName;
}
}
public interface ICoroutineYield
{
bool IsDone(float deltaTime);
}
struct YieldDefault : ICoroutineYield
{
public bool IsDone(float deltaTime)
{
return true;
}
}
struct YieldWaitForSeconds : ICoroutineYield
{
public float timeLeft;
public bool IsDone(float deltaTime)
{
timeLeft -= deltaTime;
return timeLeft < 0;
}
}
struct YieldWWW : ICoroutineYield
{
public WWW Www;
public bool IsDone(float deltaTime)
{
return Www.isDone;
}
}
struct YieldAsync : ICoroutineYield
{
public AsyncOperation asyncOperation;
public bool IsDone(float deltaTime)
{
return asyncOperation.isDone;
}
}
struct YieldNestedCoroutine : ICoroutineYield
{
public EditorCoroutine coroutine;
public bool IsDone(float deltaTime)
{
return coroutine.finished;
}
}
static EditorCoroutines instance = null;
Dictionary<string, List<EditorCoroutine>> coroutineDict = new Dictionary<string, List<EditorCoroutine>>();
List<List<EditorCoroutine>> tempCoroutineList = new List<List<EditorCoroutine>>();
Dictionary<string, Dictionary<string, EditorCoroutine>> coroutineOwnerDict =
new Dictionary<string, Dictionary<string, EditorCoroutine>>();
DateTime previousTimeSinceStartup;
/// <summary>Starts a coroutine.</summary>
/// <param name="routine">The coroutine to start.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static EditorCoroutine StartCoroutine(IEnumerator routine, object thisReference)
{
CreateInstanceIfNeeded();
return instance.GoStartCoroutine(routine, thisReference);
}
/// <summary>Starts a coroutine.</summary>
/// <param name="methodName">The name of the coroutine method to start.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static EditorCoroutine StartCoroutine(string methodName, object thisReference)
{
return StartCoroutine(methodName, null, thisReference);
}
/// <summary>Starts a coroutine.</summary>
/// <param name="methodName">The name of the coroutine method to start.</param>
/// <param name="value">The parameter to pass to the coroutine.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static EditorCoroutine StartCoroutine(string methodName, object value, object thisReference)
{
MethodInfo methodInfo = thisReference.GetType()
.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (methodInfo == null)
{
Debug.LogError("Coroutine '" + methodName + "' couldn't be started, the method doesn't exist!");
}
object returnValue;
if (value == null)
{
returnValue = methodInfo.Invoke(thisReference, null);
}
else
{
returnValue = methodInfo.Invoke(thisReference, new object[] {value});
}
if (returnValue is IEnumerator)
{
CreateInstanceIfNeeded();
return instance.GoStartCoroutine((IEnumerator) returnValue, thisReference);
}
else
{
Debug.LogError("Coroutine '" + methodName + "' couldn't be started, the method doesn't return an IEnumerator!");
}
return null;
}
/// <summary>Stops all coroutines being the routine running on the passed instance.</summary>
/// <param name="routine"> The coroutine to stop.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static void StopCoroutine(IEnumerator routine, object thisReference)
{
CreateInstanceIfNeeded();
instance.GoStopCoroutine(routine, thisReference);
}
/// <summary>
/// Stops all coroutines named methodName running on the passed instance.</summary>
/// <param name="methodName"> The name of the coroutine method to stop.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static void StopCoroutine(string methodName, object thisReference)
{
CreateInstanceIfNeeded();
instance.GoStopCoroutine(methodName, thisReference);
}
/// <summary>
/// Stops all coroutines running on the passed instance.</summary>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static void StopAllCoroutines(object thisReference)
{
CreateInstanceIfNeeded();
instance.GoStopAllCoroutines(thisReference);
}
static void CreateInstanceIfNeeded()
{
if (instance == null)
{
instance = new EditorCoroutines();
instance.Initialize();
}
}
void Initialize()
{
previousTimeSinceStartup = DateTime.Now;
EditorApplication.update += OnUpdate;
}
void GoStopCoroutine(IEnumerator routine, object thisReference)
{
GoStopActualRoutine(CreateCoroutine(routine, thisReference));
}
void GoStopCoroutine(string methodName, object thisReference)
{
GoStopActualRoutine(CreateCoroutineFromString(methodName, thisReference));
}
void GoStopActualRoutine(EditorCoroutine routine)
{
if (coroutineDict.ContainsKey(routine.routineUniqueHash))
{
coroutineOwnerDict[routine.ownerUniqueHash].Remove(routine.routineUniqueHash);
coroutineDict.Remove(routine.routineUniqueHash);
}
}
void GoStopAllCoroutines(object thisReference)
{
EditorCoroutine coroutine = CreateCoroutine(null, thisReference);
if (coroutineOwnerDict.ContainsKey(coroutine.ownerUniqueHash))
{
foreach (var couple in coroutineOwnerDict[coroutine.ownerUniqueHash])
{
coroutineDict.Remove(couple.Value.routineUniqueHash);
}
coroutineOwnerDict.Remove(coroutine.ownerUniqueHash);
}
}
EditorCoroutine GoStartCoroutine(IEnumerator routine, object thisReference)
{
if (routine == null)
{
Debug.LogException(new Exception("IEnumerator is null!"), null);
}
EditorCoroutine coroutine = CreateCoroutine(routine, thisReference);
GoStartCoroutine(coroutine);
return coroutine;
}
void GoStartCoroutine(EditorCoroutine coroutine)
{
if (!coroutineDict.ContainsKey(coroutine.routineUniqueHash))
{
List<EditorCoroutine> newCoroutineList = new List<EditorCoroutine>();
coroutineDict.Add(coroutine.routineUniqueHash, newCoroutineList);
}
coroutineDict[coroutine.routineUniqueHash].Add(coroutine);
if (!coroutineOwnerDict.ContainsKey(coroutine.ownerUniqueHash))
{
Dictionary<string, EditorCoroutine> newCoroutineDict = new Dictionary<string, EditorCoroutine>();
coroutineOwnerDict.Add(coroutine.ownerUniqueHash, newCoroutineDict);
}
// If the method from the same owner has been stored before, it doesn't have to be stored anymore,
// One reference is enough in order for "StopAllCoroutines" to work
if (!coroutineOwnerDict[coroutine.ownerUniqueHash].ContainsKey(coroutine.routineUniqueHash))
{
coroutineOwnerDict[coroutine.ownerUniqueHash].Add(coroutine.routineUniqueHash, coroutine);
}
MoveNext(coroutine);
}
EditorCoroutine CreateCoroutine(IEnumerator routine, object thisReference)
{
return new EditorCoroutine(routine, thisReference.GetHashCode(), thisReference.GetType().ToString());
}
EditorCoroutine CreateCoroutineFromString(string methodName, object thisReference)
{
return new EditorCoroutine(methodName, thisReference.GetHashCode(), thisReference.GetType().ToString());
}
void OnUpdate()
{
float deltaTime = (float) (DateTime.Now.Subtract(previousTimeSinceStartup).TotalMilliseconds / 1000.0f);
previousTimeSinceStartup = DateTime.Now;
if (coroutineDict.Count == 0)
{
return;
}
tempCoroutineList.Clear();
foreach (var pair in coroutineDict)
tempCoroutineList.Add(pair.Value);
for (var i = tempCoroutineList.Count - 1; i >= 0; i--)
{
List<EditorCoroutine> coroutines = tempCoroutineList[i];
for (int j = coroutines.Count - 1; j >= 0; j--)
{
EditorCoroutine coroutine = coroutines[j];
if (!coroutine.currentYield.IsDone(deltaTime))
{
continue;
}
if (!MoveNext(coroutine))
{
coroutines.RemoveAt(j);
coroutine.currentYield = null;
coroutine.finished = true;
}
if (coroutines.Count == 0)
{
coroutineDict.Remove(coroutine.ownerUniqueHash);
}
}
}
}
static bool MoveNext(EditorCoroutine coroutine)
{
if (coroutine.routine.MoveNext())
{
return Process(coroutine);
}
return false;
}
// returns false if no next, returns true if OK
static bool Process(EditorCoroutine coroutine)
{
object current = coroutine.routine.Current;
if (current == null)
{
return false;
}
else if (current is WaitForSeconds)
{
float seconds = float.Parse(GetInstanceField(typeof(WaitForSeconds), current, "m_Seconds").ToString());
coroutine.currentYield = new YieldWaitForSeconds() {timeLeft = (float) seconds};
}
else if (current is WWW)
{
coroutine.currentYield = new YieldWWW {Www = (WWW) current};
}
else if (current is WaitForFixedUpdate)
{
coroutine.currentYield = new YieldDefault();
}
else if (current is AsyncOperation)
{
coroutine.currentYield = new YieldAsync {asyncOperation = (AsyncOperation) current};
}
else if (current is EditorCoroutine)
{
coroutine.currentYield = new YieldNestedCoroutine { coroutine= (EditorCoroutine) current};
}
else
{
Debug.LogException(
new Exception("<" + coroutine.MethodName + "> yielded an unknown or unsupported type! (" + current.GetType() + ")"),
null);
coroutine.currentYield = new YieldDefault();
}
return true;
}
static object GetInstanceField(Type type, object instance, string fieldName)
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
FieldInfo field = type.GetField(fieldName, bindFlags);
return field.GetValue(instance);
}
}
}
추가 확장 (에디터 탭에서 따로 가능하게끔 설정)
using System.Collections;
using UnityEditor;
namespace EditorCoroutines
{
public static class EditorCoroutineExtensions
{
public static EditorCoroutines.EditorCoroutine StartCoroutine(this EditorWindow thisRef, IEnumerator coroutine)
{
return EditorCoroutines.StartCoroutine(coroutine, thisRef);
}
public static EditorCoroutines.EditorCoroutine StartCoroutine(this EditorWindow thisRef, string methodName)
{
return EditorCoroutines.StartCoroutine(methodName, thisRef);
}
public static EditorCoroutines.EditorCoroutine StartCoroutine(this EditorWindow thisRef, string methodName, object value)
{
return EditorCoroutines.StartCoroutine(methodName, value, thisRef);
}
public static void StopCoroutine(this EditorWindow thisRef, IEnumerator coroutine)
{
EditorCoroutines.StopCoroutine(coroutine, thisRef);
}
public static void StopCoroutine(this EditorWindow thisRef, string methodName)
{
EditorCoroutines.StopCoroutine(methodName, thisRef);
}
public static void StopAllCoroutines(this EditorWindow thisRef)
{
EditorCoroutines.StopAllCoroutines(thisRef);
}
}
}
코루틴 사용 예시
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace EditorCoroutines
{
public class CoroutineWindowExample : EditorWindow
{
[MenuItem("Window/Coroutine Example")]
public static void ShowWindow()
{
EditorWindow.GetWindow(typeof(CoroutineWindowExample));
}
void OnGUI()
{
if (GUILayout.Button("Start"))
{
this.StartCoroutine(Example());
}
if (GUILayout.Button("Start WWW"))
{
this.StartCoroutine(ExampleWWW());
}
if (GUILayout.Button("Start Nested"))
{
this.StartCoroutine(ExampleNested());
}
if (GUILayout.Button("Stop"))
{
this.StopCoroutine("Example");
}
if (GUILayout.Button("Stop all"))
{
this.StopAllCoroutines();
}
if (GUILayout.Button("Also"))
{
this.StopAllCoroutines();
}
}
IEnumerator Example()
{
while (true)
{
Debug.Log("Hello EditorCoroutine!");
yield return new WaitForSeconds(2f);
}
}
IEnumerator ExampleWWW()
{
while (true)
{
var www = new WWW("https://unity3d.com/");
yield return www;
Debug.Log("Hello EditorCoroutine!" + www.text);
yield return new WaitForSeconds(2f);
}
}
IEnumerator ExampleNested()
{
while (true)
{
yield return new WaitForSeconds(2f);
Debug.Log("I'm not nested");
yield return this.StartCoroutine(ExampleNestedOneLayer());
}
}
IEnumerator ExampleNestedOneLayer()
{
yield return new WaitForSeconds(2f);
Debug.Log("I'm one layer nested");
yield return this.StartCoroutine(ExampleNestedTwoLayers());
}
IEnumerator ExampleNestedTwoLayers()
{
yield return new WaitForSeconds(2f);
Debug.Log("I'm two layers nested");
}
class NonEditorClass
{
public void DoSomething(bool start, bool stop, bool stopAll)
{
if (start)
{
EditorCoroutines.StartCoroutine(Example(), this);
}
if (stop)
{
EditorCoroutines.StopCoroutine("Example", this);
}
if (stopAll)
{
EditorCoroutines.StopAllCoroutines(this);
}
}
IEnumerator Example()
{
while (true)
{
Debug.Log("Hello EditorCoroutine!");
yield return new WaitForSeconds(2f);
}
}
}
}
}
'게임엔진 > Unity' 카테고리의 다른 글
[Unity] Job 시스템 이해 3, JobHandle (0) | 2023.03.23 |
---|---|
[Unity] SQL 데이터 베이스 연동 SQLite (0) | 2023.03.23 |
[Unity] Range 어트리뷰트를 단위로 설정할 수 있는 방법 (0) | 2023.02.17 |
[Unity] 화면 위치 > 월드 좌표 치환 RectTransformUtility (0) | 2023.02.05 |
[Unity] UI 카메라 설정하기 (screen space - camera) (0) | 2023.01.15 |