• unity-协程详解


    什么是协程

    协程,即Coroutine,顾名思义,协助程序的意思。我们在进行主任务的同时,需要一些分支任务来配合工作,这就是协程的用处。协程不是进程或线程,它是一个特殊的函数,可以认为它是一个返回值是IEnumerator(不知道也没关系,后面会说)的函数。协程依然是在主线程上进行的,是一种异步多任务处理的方式,相比于线程,开辟多个协程开销不大,适合对某任务进行分时处理。

    我们只要知道协程是一个可以暂停执行,暂停后回到主函数,执行主函数剩余的部分,直到中断指令完成后,从中断指令的下一行继续执行协程剩余的函数就行。

    前置知识

    首先我们要知道协程是通过迭代器实现的。什么是迭代器?迭代器是一种设计模式,可以让开发人员无需关心容器对象的底层架构,就可以遍访这个容器对象。简单来说,迭代器就是用来遍历一个序列中的所有对象。

    在C#中可以使用foreach关键字就可以枚举一个序列

    1. foreach (var item in collection)
    2. {
    3. Console.WriteLine(item?.ToString());
    4. }

    但foreach语句并非完美无缺,它依赖于.NET Core库中的两个接口:IEnumerable和IEnumerator

    IEnumerable是可枚举的意思,IEnumerator是枚举器的意思

    IEnumerable接口

    1. public interface IEnumerable
    2. {
    3. IEnumerator GetEnumerator();
    4. }

    继承这个接口需要实现暴露出来的GetEnumerator方法,返回一个IEnumerator对象

    IEnumerator接口 

    1. public interface IEnumerator
    2. {
    3. object Current { get; }
    4. bool MoveNext();
    5. void Reset();
    6. }

    IEnumerator接口有三个东西,current返回当前序列的元素,方法MoveNext()移动到下一个元素,Reset方法重置,所以继承这个接口需要实现这三个东西

    从这个两个接口对比就可以发现,对于枚举一个容器,起真正作用是IEnumerator

    所以一个对象只要实现IEnumerator接口就能遍历

    下面来看一个实例

    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Threading.Tasks;
    7. namespace Csharp_study.Day1
    8. {
    9. //枚举对象
    10. public class Anim
    11. {
    12. public string name;//动物的名字
    13. //构造方法,对name赋值
    14. public Anim(string name)
    15. {
    16. this.name = name;
    17. }
    18. }
    19. //实现IEnumerator接口
    20. public class MIEnumerator:IEnumerator
    21. {
    22. Anim[] anim;
    23. int idx = -1;
    24. //构造方法,对t赋值
    25. public MIEnumerator(Anim[] t)
    26. {
    27. anim = t;
    28. }
    29. //实现IEnumerator接口的Current方法,获取当前元素的值
    30. public object Current
    31. {
    32. get
    33. {
    34. if (idx == -1)
    35. return new IndexOutOfRangeException();
    36. return anim[idx];
    37. }
    38. }
    39. //实现IEnumerator接口的MoveNext方法,向下一个元素移动
    40. public bool MoveNext()
    41. {
    42. idx++;
    43. return anim.Length > idx;
    44. }
    45. //实现IEnumerator接口的Reset方法,重置迭代器状态
    46. public void Reset()
    47. {
    48. idx = -1;
    49. }
    50. }
    51. class Class1
    52. {
    53. static void Main(string[] args)
    54. {
    55. //初始化一个Anim序列,用来遍历
    56. Anim[] anims = new Anim[] { new Anim("老虎"),new Anim("大象"),new Anim("河马")};
    57. MIEnumerator enumerator = new MIEnumerator(anims);
    58. while(enumerator.MoveNext())
    59. {
    60. Anim test = enumerator.Current as Anim;
    61. show(test);
    62. }
    63. void show(Anim p)
    64. {
    65. Console.WriteLine("这个小动物的名字是:" + p.name);
    66. }
    67. Console.ReadLine();
    68. }
    69. }
    70. }

    输出结果

    1. 这个小动物的名字是:老虎
    2. 这个小动物的名字是:大象
    3. 这个小动物的名字是:河马

    从这个例子中就可以看出来,我们通过继承这个IEnumerator接口,然后实现它的Current,MoveNext和Reset方法就可以遍历这个Anim对象了。

    所以不难看出,foreach关键字就是主要依靠IEnumerator接口实现,这个就不深入讲了,我们只要知道IEnumerator就行

    此外在迭代器中还有一个关键字需要我们掌握-yield。yield是一个语法糖,是为了简化迭代器的实现语法才产生的,从上面的讲解不难发现,实际起作用的就是MoveNext和Current方法。所以C#2提供一个处理方法:yield语句。

    这里就不细讲了,详细请看这里:C#迭代器的详细用法_真的没事鸭的博客-CSDN博客

    协程的实现

    格式

    1. IEnumrator 函数名(形参表) //最多只能有一个形参
    2. {
    3. yield return xxx; //恢复执行条件
    4. //方法体
    5. }

    在IEnumerator类型的方法中写入需要执行的操作,遇到yield会暂时挂起,yield return后条件满足才继续执行后面的内容

    yield return表示在迭代中下一个迭代时返回的数据,其中还有yield break表示跳出迭代

    协程的开启

    开启协程需要使用StartCoroutine()方法:

    • 开启无参数的协程:StartCoroutine(协程名());或StartCoroutine("协程名")
    • 开启单参数的协程:StartCoroutine(协程名(参数));或StartCoroutine("协程名",参数)
    • 开启多参数的协程StartCoroutine(协程名(参数1,......))

    协程的关闭

    结束协程有两种情况:

    • 当协程的方法体执行完毕将会自动结束
    • 调用StopCoroutine();方法中止协程执行

    终止协程有两种情况

    • 中止所有协程:StopAllCoroutines();
    • 使用对象实例中止指定协程

    yield回复条件语句 

     协程的顺序

    比如我们在start函数定义了一个协程,首先第一帧我们在start函数开启协程,从上到下执行协程里面的操作,遇到yield return XXX,主函数是不受影响,主函数一直在执行。yield return后的条件满足后先挂起,在下一帧再继续执行后面的操作

    下名看一个案例:实现一个秒表的效果,没过一秒数字增加1

    我们在Hierarchy界面添加一个Text,注意这个是旧的Text,不是TextMeshPro

    调整一下位置

     然后建立一个C#脚本,下面编写一个这个脚本

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using TMPro;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6. public class test : MonoBehaviour
    7. {
    8. public Text text;
    9. void Start()
    10. {
    11. StartCoroutine(Timer(1));//开启协程
    12. }
    13. IEnumerator Timer(float second)
    14. {
    15. int count = 0;
    16. while (true)
    17. {
    18. yield return new WaitForSeconds(second);//等待一秒钟执行后续代码
    19. count++;
    20. Debug.Log("输出");
    21. text.text = count.ToString();
    22. }
    23. }
    24. }

     然后我们将这个代码挂载Text所属的canvas,因为上面脚本获取的Text是pulbic,所以需要我们拖一个Text过去,所以把这个Text拖给canvas上的脚本

    执行就可以发现,Text上的数字会自动加1了

    如有错漏之处,敬请指正!

  • 相关阅读:
    文件包含漏洞详解
    在java的继承中你是否有这样的疑惑?
    [Web Server]Tomcat调优之SpringBoot内嵌Tomcat源码分析
    UE4 自带体积云应用
    软考高项第四版教材整合管理(第8章)重点内容
    程序内hook键盘
    C++实测无锁队列concurrentqueue、boost.spinlock 和 std::mutex 在多线程情况下的性能表现
    【LeetCode-中等题】18. 四数之和
    第四站:数组
    .NET下数据库的负载均衡(有趣实验)
  • 原文地址:https://blog.csdn.net/qq_52905520/article/details/126440135