Hi, Figured this might be useful to some people. It's coroutines for editor scripting. It's a little different from regular coroutines. Things like WaitForSeconds don't work, it always waits just one frame. But you can "cascade" coroutines by just yielding the call to the second coroutine. You can also specify an (optional) OnUpdate method, which gets called every time the coroutine gets updated. I use this for splitting up a lengthy operation over multiple frames, so I can use EditorUtility.DisplayProgressBar to show progress (which I update in the OnUpdate callback). Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; namespace MogulTech.Utilities { public static class EditorCoroutines { public class Coroutine { public IEnumerator enumerator; public System.Action<bool> OnUpdate; public List<IEnumerator> history = new List<IEnumerator> (); } static readonly List<Coroutine> coroutines = new List<Coroutine> (); public static void Execute (IEnumerator enumerator, System.Action<bool> OnUpdate = null) { if (coroutines.Count == 0) { EditorApplication.update += Update; } var coroutine = new Coroutine { enumerator = enumerator, OnUpdate = OnUpdate }; coroutines.Add (coroutine); } static void Update () { for (int i = 0; i < coroutines.Count; i++) { var coroutine = coroutines[i]; bool done = !coroutine.enumerator.MoveNext (); if (done) { if (coroutine.history.Count == 0) { coroutines.RemoveAt (i); i--; } else { done = false; coroutine.enumerator = coroutine.history[coroutine.history.Count - 1]; coroutine.history.RemoveAt (coroutine.history.Count - 1); } } else { if (coroutine.enumerator.Current is IEnumerator) { coroutine.history.Add (coroutine.enumerator); coroutine.enumerator = (IEnumerator)coroutine.enumerator.Current; } } if (coroutine.OnUpdate != null) coroutine.OnUpdate (done); } if (coroutines.Count == 0) EditorApplication.update -= Update; } internal static void StopAll () { coroutines.Clear (); EditorApplication.update -= Update; } } } And here's a little tester class : Code (CSharp): [InitializeOnLoad] public static class CoroutineTester { static CoroutineTester () { EditorCoroutines.Execute (Test ()); } static IEnumerator Test () { Debug.Log ("Test"); yield return Test2 (); Debug.Log ("Test done"); } static IEnumerator Test2 () { Debug.Log ("Test2"); yield return Test3 (); Debug.Log ("Test2 done"); } static IEnumerator Test3 () { Debug.Log ("Test3"); yield return 0; Debug.Log ("Test3 done"); } } Enjoy!
I've used code recently in a project to pull data in the editor, and it works much faster now! Thank you for the excellent code! -Phil
For the future: There is now this Unity Package for Editor Coroutines https://docs.unity3d.com/Packages/com.unity.editorcoroutines@0.0/manual/index.html (I actually don't know if there is still someone at Unity working on this since it has almost been a full year since the latest release 0.0.2-preview1. Classic Unity)
The package while promising doesn't function as one would expect though. Despite using it per the documentation it only ticks while editor changes are occurring. Stop moving something in the editor and it pauses the coroutine. Code (CSharp): using Unity.EditorCoroutines.Editor; using UnityEditor; [ExecuteInEditMode] public class MessageBox : MonoBehaviour { #if (UNITY_EDITOR) public void Awake() { StartCoroutine(TickTock()); } private IEnumerator TickTock() { Debug.Log("Tick"); //EditorWaitForSeconds is not implemented correctly, behaves like WaitForEndOfFrame //yield return new EditorWaitForSeconds(1f); yield return new WaitForSeconds(1f); Debug.Log("Tock"); } #endif } //Console reads one Tick, then a second later, one Tock and pauses until the next change to the editor window.
So 1.0 is released, but it doesn't support WaitForSeconds etc. see: https://docs.unity3d.com/Packages/com.unity.editorcoroutines@1.0/manual/index.html That was the whole appeal for me :/ Guess I'll have to look into using .net tasks and async await instead.
I was just noticing that, although it appears as though Code (CSharp): yield return null; will let it wait a frame at least. Don't forget to place a Code (CSharp): Repaint(); after the work is done or the editor will only sporadically update it's visuals.
It doesn't use the same classes, but it has direct support for some of this, e.g. WaitForSeconds is replaced by EditorWaitForSeconds: https://docs.unity3d.com/Packages/c...orCoroutines.Editor.EditorWaitForSeconds.html