Unity 协程等待对象的生命周期
Unity系统提供的协程等待有下面几个 :
yield return new WaitForFixedUpdate(); yield return new WaitForEndOfFrame(); yield return new WaitForSeconds(1.0f); yield return new WaitForSecondsRealtime(1.0f);
一般情况下我们对协程的生命周期不是很关心, 因为是异步模式的 ( 虽然它不是异步的 ), 不过对于一个需要返回的等待对象, Unity 提供的是一个 class 对象, 比如一个循环等待的协程, 它就会产生很多的 GC 对象, 像下面这样 :
IEnumerator TestCoroutine1() { while(true) { // ...... yield return new WaitForEndOfFrame(); // ...... } }
而它又只是一个对象, 没有附带其它信息, 那么这个东西其实是可以复用的, 直接给它一个静态对象也是一样的 :
public static readonly WaitForEndOfFrame WaitForEndOfFrame = new WaitForEndOfFrame(); IEnumerator TestCoroutine1() { while(true) { yield return WaitForEndOfFrame; Debug.Log(Time.frameCount); } }
然后很多人发现对于 WaitForEndOfFrame 其实返回的对象是空时 ( null ), 得到的结果也一样, 可是其实他们执行代码所在的生命周期是不同的, 可以看看下面的测试结果 :
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class Test : MonoBehaviour { int id = 0; void Awake() { StartCoroutine(TestCoroutine()); } void Update() { Debug.Log("Update :: " + id++); } void LateUpdate() { Debug.Log("LateUpdate :: " + id++); } private void OnPostRender() { Debug.Log("OnPostRender :: " + id++); } IEnumerator TestCoroutine() { Debug.Log("Coroutine Start :: " + id++); int i = 0; while(i++ < 3) { Debug.Log("Coroutine Tick : " + i + " :: " + id++); yield return null; Debug.Log("Coroutine End : " + i + " :: " + id++); } this.enabled = false; } }
这里使用 null 作为协程返回, 添加了 Update, LateUpdate, OnPostRender 等生命周期, 得到的结果如下 :
可见每次 yield 等待之后的代码调用都是在Update之后, LateUpdate之前调用的, 它插入的生命周期就在这里, 而 WaitForEndOfFrame 的就不同了, 就像描述的一样, 是在"一帧"之后才会调用 :
IEnumerator TestCoroutine() { Debug.Log("Coroutine Start :: " + id++); int i = 0; while(i++ < 3) { Debug.Log("Coroutine Tick : " + i + " :: " + id++); yield return new WaitForEndOfFrame(); Debug.Log("Coroutine End : " + i + " :: " + id++); } this.enabled = false; }
可以看到插入的生命周期在相机完成渲染之后, 其实是在所有生命周期之后, 所以需要根据实际需求选择正确的返回, 比如你要获取当前帧相机的正确渲染结果, 就需要使用 WaitForEndOfFrame, 而你如果想要改变当前帧的渲染结果, 你就要使用 null 返回, 否则就变成获取到上一帧的渲染结果和改变下一帧的渲染了, 虽然影响不大...
而 WaitForFixedUpdate 这个在物理帧里的显然使用的就很少了, 它跟逻辑跟渲染都不搭嘎.
而 WaitForSeconds 跟 WaitForSecondsRealtime 有个决定性的不同, WaitForSeconds 跟 WaitForEndOfFrame 一样只是个简单对象, 而 WaitForSecondsRealtime 有自己的计时器 :
所以测试结果 WaitForSecondsRealtime 是无法使用全局对象的 :
IEnumerator TestCoroutine() { var WaitForSecondsRealtime = new WaitForSecondsRealtime(1.0f); Debug.Log("Coroutine Start :: " + id++); int i = 0; while(i++ < 3) { Debug.Log("Coroutine Tick : " + i + " :: " + id++); yield return WaitForSecondsRealtime; Debug.Log(Time.realtimeSinceStartup); Debug.Log("Coroutine End : " + i + " :: " + id++); } this.enabled = false; }
它在第一次成功返回之后, 每帧都是成功返回, 没有意义...
而 WaitForSeconds 还是正常的 :
IEnumerator TestCoroutine() { var WaitForSeconds = new WaitForSeconds(1.0f); Debug.Log("Coroutine Start :: " + id++); int i = 0; while(i++ < 3) { Debug.Log("Coroutine Tick : " + i + " :: " + id++); yield return WaitForSeconds; Debug.Log(Time.realtimeSinceStartup); Debug.Log("Coroutine End : " + i + " :: " + id++); } this.enabled = false; }