static IEnumerator DemoCoroutiune() { // You can code editor coroutines exactly like you would a normal unity coroutine Debug.Log("Step: 0"); yield return(null); // all the normal return types that work with regular Unity coroutines should work here! for example lets wait for a second Debug.Log("Step: 1"); yield return(new WaitForSeconds(1)); // We can also yeild any type that extends Unitys CustomYieldInstruction class. here we are going to use EditorStatusUpdate. this allows us to yield and update the // editor coroutine UI at the same time! yield return(new EditorStatusUpdate("coroutine is running", 0.2f)); // We can also yield to nested coroutines Debug.Log("Step: 2"); yield return(EditorCoroutineRunner.StartCoroutine(DemoTwo())); EditorCoroutineRunner.UpdateUIProgressBar(0.35f); // we can use the UpdateUI helper methods to update the UI whenever, without yielding a EditorStatusUpdate yield return(DemoTwo()); // it shouldnt matter how we start the nested coroutine, the editor runner can hadle it // we can even yield a UnityWebRequest object if we want to grab data from the internets! Debug.Log("Step: 3"); // for example, lets as random.org to generate us a list of random numbers and shove it into the console var www = new UnityWebRequest("https://www.random.org/integers/?num=100&min=1&max=1000&col=1&base=10&col=5&format=plain&rnd=new"); yield return(www); Debug.Log(www.ToString()); EditorCoroutineRunner.UpdateUI("Half way!", 0.5f); yield return(new WaitForSeconds(1)); // Finally lets do a long runnig task and split its updates over many frames to keep the editor responsive Debug.Log("Step: 4"); var test = 1000; yield return(new WaitUntil(() => { test--; EditorCoroutineRunner.UpdateUI("Crunching Numbers: " + test, 0.5f + (((1000 - test) / 1000f) * 0.5f)); return (test <= 0); })); Debug.Log("Done!!"); }
/// <summary> /// Send a Coroutine to be run on the main thread. See EditorDispatchActions for some common usecases. /// </summary> /// <param name="task">A coroutine to run on the main thread</param> /// <param name="showUI">if the Editor Corotine runner should run a progress UI</param> /// <returns>An AsyncDispatch that can be used to track if the coroutine has been dispatched & completed.</returns> public static AsyncDispatch Dispatch(IEnumerator task, bool showUI = false) { // you need this system for this to work! https://gist.github.com/LotteMakesStuff/16b5f2fc108f9a0201950c797d53cfbf lock (dispatchQueue) { AsyncDispatch dispatch = new AsyncDispatch(); dispatchQueue.Enqueue(() => { if (showUI) { EditorCoroutineRunner.StartCoroutineWithUI(DispatchCorotine(task, dispatch), "Dispatcher task", false); } else { EditorCoroutineRunner.StartCoroutine(task); } }); return(dispatch); } }
public void Tick() { if (coroutine != null) { // First check if we have been canceled by the UI. If so, we need to stop before doing any wait processing if (canceled) { Stop(); return; } // Did the last Yield want us to wait? bool isWaiting = false; var now = DateTime.Now; if (current != null) { if (currentType == typeof(WaitForSeconds)) { // last yield was a WaitForSeconds. Lets update the timer. var delta = now - lastUpdateTime; timer -= (float)delta.TotalSeconds; if (timer > 0.0f) { isWaiting = true; } } else if (currentType == typeof(WaitForEndOfFrame) || currentType == typeof(WaitForFixedUpdate)) { // These dont make sense in editor, so we will treat them the same as a null return... isWaiting = false; } else if (currentType == typeof(UnityWebRequest)) { // Web download request, lets see if its done! var www = current as UnityWebRequest; if (!www.isDone) { isWaiting = true; } } else if (currentType.IsSubclassOf(typeof(CustomYieldInstruction))) { // last yield was a custom yield type, lets check its keepWaiting property and react to that var yieldInstruction = current as CustomYieldInstruction; if (yieldInstruction.keepWaiting) { isWaiting = true; } } else if (currentType == typeof(EditorCoroutine)) { // Were waiting on another coroutine to finish var editorCoroutine = current as EditorCoroutine; if (!editorCoroutine.HasFinished) { isWaiting = true; } } else if (typeof(IEnumerator).IsAssignableFrom(currentType)) { // if were just seeing an enumerator lets assume that were seeing a nested coroutine that has been passed in without calling start.. were start it properly here if we need to if (nestedCoroutine == null) { nestedCoroutine = EditorCoroutineRunner.StartCoroutine(current as IEnumerator); isWaiting = true; } else { isWaiting = !nestedCoroutine.HasFinished; } } else if (currentType == typeof(Coroutine)) { // UNSUPPORTED UnityEngine.Debug.LogError("Nested Coroutines started by Unity's defaut StartCoroutine method are not supported in editor! please use EditorCoroutineRunner.Start instead. Canceling."); canceled = true; } else { // UNSUPPORTED Debug.LogError("Unsupported yield (" + currentType + ") in editor coroutine!! Canceling."); canceled = true; } } lastUpdateTime = now; // have we been canceled? if (canceled) { Stop(); return; } if (!isWaiting) { // nope were good! tick the coroutine! bool update = coroutine.MoveNext(); if (update) { // yup the coroutine returned true so its been ticked... // lets see what it actually yielded current = coroutine.Current; if (current != null) { // is it a type we have to do extra processing on? currentType = current.GetType(); if (currentType == typeof(WaitForSeconds)) { // its a WaitForSeconds... lets use reflection to pull out how long the actual wait is for so we can process the wait var wait = current as WaitForSeconds; FieldInfo m_Seconds = typeof(WaitForSeconds).GetField("m_Seconds", BindingFlags.NonPublic | BindingFlags.Instance); if (m_Seconds != null) { timer = (float)m_Seconds.GetValue(wait); } } else if (currentType == typeof(EditorStatusUpdate)) { // Special case yield that wants to update the UI! var updateInfo = current as EditorStatusUpdate; if (updateInfo.HasLabelUpdate) { Label = updateInfo.Label; } if (updateInfo.HasPercentUpdate) { PercentComplete = updateInfo.PercentComplete; } } } } else { // Coroutine returned false so its finally finished!! Stop(); } } } }