解决Unity协程无法同步返回的问题
Unity的协程是轻量的异步解决方案,但是每调用一次yield就必须等下一帧才能继续,这一点带来了很多约束。
比如如下代码:
void OnEnable() { StartCoroutine(_Do()); } IEnumerator _Do() { Debug.Log("[A]Frame " + Time.frameCount); yield return null; Debug.Log("[B]Frame " + Time.frameCount); }
当然,也会想到用一些Trick欺骗过去
IEnumerator Start() { Debug.Log("[0]frame: " + Time.frameCount); yield return Foo1(); yield return Foo2(); } IEnumerator Foo1() { Debug.Log("[1]frame: " + Time.frameCount); if (Time.time < 0)//always false yield return null; Debug.Log("[2]frame: " + Time.frameCount); } IEnumerator Foo2() { Debug.Log("[3]frame: " + Time.frameCount); yield return null; }
可是编译器并不吃这一套
那么解决方法也很简单,就是用迭代器再封装一层。
并把yield return true作为非异步返回的标记:
using UnityEngine; using System.Collections; using System.Collections.Generic; using System; public class CoroutineTest : MonoBehaviour { void OnEnable() { StartCoroutine(ToFixedCoroutine(_Do())); } IEnumerator _Do() { Debug.Log("[A]Frame " + Time.frameCount); yield return true; Debug.Log("[B]Frame " + Time.frameCount); } public static IEnumerator ToFixedCoroutine(IEnumerator enumerator) { var parentsStack = new Stack<IEnumerator>(); var currentEnumerator = enumerator; parentsStack.Push(currentEnumerator); while (parentsStack.Count > 0) { currentEnumerator = parentsStack.Pop(); while (currentEnumerator.MoveNext()) { var subEnumerator = currentEnumerator.Current as IEnumerator; if (subEnumerator != null) { parentsStack.Push(currentEnumerator); currentEnumerator = subEnumerator; } else { if (currentEnumerator.Current is bool && (bool)currentEnumerator.Current) continue; yield return currentEnumerator.Current; } } } } }
这样就可以同步返回了
ToFixedCoroutine函数经过一些嵌套的测试,使用起来还算稳定。