• yield学习续:yield return迭代块在Unity3D中的应用——协程


    必读好文推荐:

    Unity协程(Coroutine)原理深入剖析

    Unity协程(Coroutine)原理深入剖析再续

    上面的文章说得太透彻,所以这里就记一下自己的学习笔记了。

    首先要说明的是,协程并不是线程,协程是运行在主线程中的,是和主线程同步执行的代码,不同的地方是运行的方法可以被yield return在当前帧进行打断,到下一帧后可以继续从被打断的地方继续运行。

    下面我们看一个示例,场景中有一个空的GameObject对象,其绑定了下面的脚本:

     1 using UnityEngine;
     2 using System.Collections;
     3 
     4 public class Test : MonoBehaviour
     5 {
     6     int frame = 0;
     7 
     8     void Start ()
     9     {
    10         this.StartCoroutine(CountDown());
    11     }
    12     
    13     void Update ()
    14     {
    15         Debug.Log("Now is frame: " + (++frame));
    16     }
    17 
    18     IEnumerator CountDown()
    19     {
    20         Debug.Log("step - 1");
    21         yield return null;
    22         Debug.Log("step - 2");
    23         yield return null;
    24         Debug.Log("step - 3");
    25         yield return null;
    26         Debug.Log("step - 4");
    27     }
    28 }

    下面是执行的结果:

    下面我们看看运行的逻辑是如何的:

    当进入Start方法时开始启动协程,这时候协程开始运行,输出“step1”后遇到第一个yield return后暂停本帧的运行,接下来进入Update方法输出“frame1”,由于协程调用是在Update之后,所以第二帧开始后,先执行了第二个Update输出“frame2”,然后从协程的上次暂停处继续执行,输出“step2”后遇到第二个yield return后暂停本帧的运行,如此反复,当输出“step4”后发现方法已经执行完毕,协程结束。

    下面看看yield break的效果,这个语句会立即中断协程的运行,代码如下:

     1 using UnityEngine;
     2 using System.Collections;
     3 
     4 public class Test : MonoBehaviour
     5 {
     6     int frame = 0;
     7 
     8     void Start ()
     9     {
    10         this.StartCoroutine(CountDown());
    11     }
    12     
    13     void Update ()
    14     {
    15         Debug.Log("Now is frame: " + (++frame));
    16     }
    17 
    18     IEnumerator CountDown()
    19     {
    20         Debug.Log("step - 1");
    21         yield return null;
    22         Debug.Log("step - 2");
    23         yield return null;
    24         Debug.Log("step - 3");
    25         yield break;
    26         Debug.Log("step - 4");
    27     }
    28 }

    下面是运行的结果:

    我们可以发现“step4”已经运行不到了。

    yield的返回值,我们可以返回null或者数字0,效果是一致的,同时还可以返回3个对象,分别如下:

    yield return new WaitForFixedUpdate();

    ·等待直到下一个固定帧速率更新函数。

    yield return new WaitForEndOfFrame();

    ·等待直到所有的摄像机和GUI被渲染完成后,在该帧显示在屏幕之前。

    yield return new WaitForSeconds(1);

    ·在给定的秒数内,暂停协同程序的执行。

    下面我们来看一个例子,修改第一个例子的Test.cs:

     1 using UnityEngine;
     2 using System.Collections;
     3 
     4 public class Test : MonoBehaviour
     5 {
     6     int frame1 = 0;
     7     int frame2 = 0;
     8     int frame3 = 0;
     9 
    10     void Start ()
    11     {
    12         this.StartCoroutine(CountDown());
    13         this.StartCoroutine(CountDown_WaitForFixedUpdate());
    14         this.StartCoroutine(CountDown_WaitForEndOfFrame());
    15         this.StartCoroutine(CountDown_WaitForSeconds());
    16     }
    17     
    18     void Update ()
    19     {
    20         Debug.Log("Update is frame: " + (++frame1));
    21     }
    22 
    23     void FixedUpdate ()
    24     {
    25         Debug.Log("FixedUpdate is frame: " + (++frame2));
    26     }
    27 
    28     void LateUpdate ()
    29     {
    30         Debug.Log("LateUpdate is frame: " + (++frame3));
    31     }
    32 
    33     IEnumerator CountDown()
    34     {
    35         Debug.Log("yield - step - 1");
    36         yield return null;
    37         Debug.Log("yield - step - 2");
    38         yield return null;
    39         Debug.Log("yield - step - 3");
    40     }
    41 
    42     IEnumerator CountDown_WaitForFixedUpdate()
    43     {
    44         Debug.Log("yield WaitForFixedUpdate - step - 1");
    45         yield return new WaitForFixedUpdate();
    46         Debug.Log("yield WaitForFixedUpdate - step - 2");
    47         yield return new WaitForFixedUpdate();
    48         Debug.Log("yield WaitForFixedUpdate - step - 3");
    49     }
    50 
    51     IEnumerator CountDown_WaitForEndOfFrame()
    52     {
    53         Debug.Log("yield WaitForEndOfFrame - step - 1");
    54         yield return new WaitForEndOfFrame();
    55         Debug.Log("yield WaitForEndOfFrame - step - 2");
    56         yield return new WaitForEndOfFrame();
    57         Debug.Log("yield WaitForEndOfFrame - step - 3");
    58     }
    59 
    60     IEnumerator CountDown_WaitForSeconds()
    61     {
    62         Debug.Log("yield WaitForSeconds - step - 1");
    63         yield return new WaitForSeconds(1 / 60 * 3);//大概是三帧的时间
    64         Debug.Log("yield WaitForSeconds - step - 2");
    65         yield return new WaitForSeconds(1 / 60 * 3);
    66         Debug.Log("yield WaitForSeconds - step - 3");
    67     }
    68 }

    运行的结果如下,有点长,我就弄成两张图了:

    通过输出我们可以得出下面的结果:

    1. 当帧数波动时,FixedUpdate会进行多次补帧处理,我们可以发现两张图之间FixedUpdate从3一直补帧到15;
    2. WaitForFixedUpdate表示协程是跟在FixedUpdate之后执行的;
    3. WaitForEndOfFrame表示协程是跟在LateUpdate之后执行的;
    4. WaitForSeconds额。。。不用多说了,你指定多久后执行就多久后执行,当然由于是基于帧运算的,所以可能会不准确;

    最后补一张开头博客的运行顺序图:

  • 相关阅读:
    UI和3D物体的堆叠响应
    UI中的事件系统EventSystem
    UI的管理
    学习笔记--2020年12月30日
    Java集合框架
    Java基本数据类型
    数据存储
    intent和手势探测
    即时消息Toast和对话框
    Android事件处理的三种方法
  • 原文地址:https://www.cnblogs.com/hammerc/p/4432969.html
Copyright © 2020-2023  润新知