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;
    }

 

posted @ 2020-07-27 10:23  tiancaiKG  阅读(2158)  评论(0编辑  收藏  举报