深入探索Unity协程:揭开CSharp迭代器背后的神秘面纱

在这里插入图片描述

协程是一种特殊类型的迭代器方法,允许你在多个帧之间分段执行代码。可以用来处理时间延迟、异步操作和顺序执行的任务,而不阻塞主线程。Unity协程的实现依赖于C#语言提供的迭代器相关的语言特性,所以想要弄清楚Unity协程的底层原理,必须先了解C#的迭代器的基本功能。

深入探索Unity协程文章首发

C#迭代器

迭代器的基本概念

  1. 迭代器是什么? 迭代器是一种简化遍历集合或序列的工具。你可以用它来逐个访问集合中的每个元素,而不需要自己编写复杂的循环逻辑。迭代器通过生成一个可枚举的序列,让你逐个取出元素。

  2. yield 关键字 。在 C# 中,yield 关键字是迭代器的核心。它帮助你创建一个可以暂停和恢复的迭代过程。使用 yield 关键字,你可以逐步生成序列中的每个元素,而不是一次性生成所有元素。

    • yield return:用于返回序列中的一个元素,并暂停迭代器的执行,直到下一次请求。
    • yield break:用于结束序列的生成,不再返回更多的元素。

C#迭代器的作用

C#迭代器Enumerator提供了一种可以通过foreach遍历任何一个自定义类型的手段。对于任何一个实现了IEnumerable接口和IEnumerator接口的类型来说,都可以通过foreach语句来像遍历一个集合一样遍历一个对象。

定义一个班级类,由若干学生组成:

public class Student
{public string Name { get; set; }public override string ToString(){return Name;}
}public class ClassRoom : IEnumerable
{private List<Student> students;public ClassRoom(){students = new List<Student>();}public void Add(Student student){if (!students.Contains(student)){students.Add(student);}}public void Remvoe(Student student){if (students.Contains(student)){students.Remove(student);}}public IEnumerator GetEnumerator(){return new StudentEnumerator(students);}
}public class StudentEnumerator : IEnumerator{public StudentEnumerator(List<Student> students){this.students = students;}private List<Student> students;private int currentIndex = -1;public object Current{get{if(0 <= currentIndex && currentIndex<students.Count){return students[currentIndex];}return null;}}public bool MoveNext(){currentIndex++;return currentIndex<students.Count;}public void Reset(){currentIndex = -1;}}

当需要能够编写代码遍历ClassRoom类中的Student对象,如果不借助迭代器,就只能将ClassRoom内部的students集合暴露出来供调用方使用,这样就暴露了ClassRoom内部有关Student对象的存储细节,以后如果Student对象的存储结构变了(比如由List结构变成了数组或者字典等等),对应的调用方的所有代码也得跟着变更。除了直接将students成员暴露出来以外,还有一种方法就是可以让ClassRoom实现IEnumerable接口,这样就可以通过foreach语句来遍历其中的Student对象。

验证代码:

    ClassRoom c = new ClassRoom();c.Add(new Student() { Name = "zzz"});c.Add(new Student() { Name = "yyy"});foreach (Student s in c){Debug.Log(s.ToString());}Debug.Log("......等价输出........");//foreach的等价写法IEnumerator enumerator = c.GetEnumerator();while (enumerator.MoveNext()){Debug.Log(((Student)(enumerator.Current)).ToString());}

控制台输出:
image.png

Unity协程

通常情况下,我们写的每一段代码,都会在Unity的更新逻辑中在同一帧全部执行完毕。如果我们需要将某一段代码包含的逻辑拆分到不同的帧来分段执行,除了自己手写状态机来实现该流程外,更简单方便的方法就是使用Unity协程。总的来说,Unity协程允许我们在保证整个应用在单线程模式不变的情况下通过编写协程函数并调用开启协程的方法(StartCoroutine)将一个任务分到不同的时间段异步执行。

Unity针对开关协程均提供了三个重载方法,以下表格中的方法均是一一对应的开关协程的用法,不能混用。

开启协程方法停止协程方法
StartCoroutine(string methodName)/StartCoroutine(string methodName, object value)StopCoroutine(string methodName)和StopCoroutine(Coroutine)
StartCoroutine(IEnumerator routine)/StartCoroutine(IEnumerator routine)StopCoroutine(Coroutine routine)和StopCoroutine(IEnumerator routine)

Yield Return延迟函数

Uniyt协程中的协程函数通过yield return后面的WaitForSeconds、WaitForEndOfFrame等可以控制延迟多少秒、多少帧之后再执行,诸如此类效果是如何实现的呢?关键点在于yield return语句后面的对象类型。我们知道,Unity协程中常见的yield return有这么几种:

  yield return new WaitForSeconds(1);yield return new WaitForEndOfFrame();yield return new WaitForFixedUpdate();

转到上诉三个函数定义源码处,不难看出它们均继承于YieldInstruction。Unity就是根据yield return返回的对象类型来判断到底应该延迟多长时间来执行下一段代码的。

总结

Unity的协程的实现原理是基于C#语言的迭代器特性,通过定义一个协程函数(通过yield return返回),将协程函数缓存为一个IEnumerator的对象,然后根据该对象的Current(是一个YieldInstruction对象或者null) 来判断下一次执行需要间隔的时间,等到间隔时间结束后执行MoveNext执行下一阶段的任务,并继续根据新的Current确定下一次等待的时间间隔,直到MoveNext返回false标志着协程终止。

大致可以用以下流程图来表示Unity协程的执行过程:
image.png

自定义实现一个有趣的协程方法

理解了Unity协程的实现原理之后,我们完全可以自己写代码来实现类似Unity中的StartCoroutine的效果。比如,我们编写一个自己的开启携程的方法:此方法规定能够接受一个返回IEnumerator的协程函数,并且可以根据yield return后面返回的字符串的长度来等待相应的秒数,比如yield return “1234”,那么就等待4秒之后再执行后面的代码,如果yield return “100”, 那么就等待3秒之后再执行后面的代码,如果yield return后面的对象不是string,则默认等待一帧之后再执行。有了前文的基础,我们很容易写出如下代码:

	/// <summary>/// 用来存储创建的迭代器对象/// </summary>private IEnumerator taskEnumerator = null;/// <summary>/// 用来记录任务是否完成的标记/// </summary>private bool isDone = false;private float currentDelayTime = 0f;private float currentPassedTime = 0f;private int delayFrameCount = 1;private bool delayFrame = false;private bool isCoroutineStarted = false;private void MyStartCoroutine(IEnumerator enumerator){if (enumerator == null) return;isCoroutineStarted = true;taskEnumerator = enumerator;PushTaskToNextStep();}private void Start(){MyStartCoroutine(YieldFunction());}private IEnumerator YieldFunction(){//第一段代码Debug.Log("first step......");yield return 1;//第二段代码Debug.Log("second tep......");yield return 2;//第三段代码Debug.Log("third step......");yield return 3;//第四段代码Debug.Log("forth step......");yield return 4;}private void PushTaskToNextStep(){isDone = !taskEnumerator.MoveNext();if (!isDone){if (taskEnumerator.Current is string){currentDelayTime = (taskEnumerator.Current as string).Length;currentPassedTime = 0f;delayFrame = false;}else{delayFrame = true;delayFrameCount = 1;}}else{isCoroutineStarted = false;}}private void Update(){if (isCoroutineStarted){if (delayFrame){delayFrameCount--;if (delayFrameCount == 0){Debug.Log(string.Format("第{0}帧(运行数:{1})结果:阶段任务已完成!", Time.frameCount, Time.time));PushTaskToNextStep();}}else{currentPassedTime += Time.deltaTime;if (currentPassedTime >= currentDelayTime){Debug.Log(string.Format("第{0}帧(运行数:{1})结果:阶段任务已完成!", Time.frameCount, Time.time));PushTaskToNextStep();}}}}

控制台输出与预期一致:
image.png

挺有趣哈!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/419649.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单例的饿汉式,懒汉式的线程安全问题

1 单例的饿汉式 对象在类加载的时候就创建了&#xff0c;线程安全&#xff0c;速度块&#xff0c;但是浪费空间&#xff0c; public class Hungry {//唯一对象private static final Hungry HUNGRY new Hungry();byte byte1[]new byte[1024];byte byte2[]new byte[1024];byte…

Java的时间复杂度和空间复杂度和常见排序

目录 一丶时间复杂度 二丶空间复杂度 三丶Java常见排序 1. 冒泡排序&#xff08;Bubble Sort&#xff09; 2.插入排序&#xff08;Insertion Sort&#xff09; 3.希尔排序&#xff08;Shell Sort&#xff09; 4.选择排序&#xff08;Selection Sort&#xff09; 5.堆排序&am…

qmt量化交易策略小白学习笔记第61期【qmt编程之期权行情数据--get_market_data_ex函数】

qmt编程之获取期权数据 期权行情数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 获取期权行情数据 获取期权最新数据&#xff0c;首先需要进行数据订阅。完成合约订阅后&#xff0c;用g…

【OpenCV】灰度化处理

文章目录 1. 图像灰度化处理对比2. 代码示例3. 二值化处理 1. 图像灰度化处理对比 2. 代码示例 #include <opencv2/opencv.hpp> using namespace cv;int main() {Mat currentImage imread("path_to_image.jpg"); // 读取彩色图像Mat grayImage;// 将彩色图像…

Rust的常数、作用域与所有权

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust到底值不值得学&#xff0c;之一 -CSDN博客 Rust到底值不值得学&#xff0c;之二-CSDN博客 Rust的数据类型-CSDN博客 3.7 常…

HTTP 二、进阶

四、安全 1、TLS是什么 &#xff08;1&#xff09;为什么要有HTTPS ​ 简单的回答是“因为 HTTP 不安全”。由于 HTTP 天生“明文”的特点&#xff0c;整个传输过程完全透明&#xff0c;任何人都能够在链路中截获、修改或者伪造请求 / 响应报文&#xff0c;数据不具有可…

阿里不认命

​ 转载&#xff1a;新熵 原创 作者丨萧维 编辑丨影蕨 国家定调了&#xff01;一系列积极信号为平台经济注入一剂强心针&#xff0c;阿里迎来新生。 最近&#xff0c;阿里捷报频传&#xff01; 先是8月28日&#xff0c;阿里巴巴完成香港双重主要上市。紧接着&#xff0c;8月…

基于聚类与LSTM对比特币价格深度分析与预测

1.项目背景 比特币作为全球最具影响力的加密货币之一&#xff0c;其价格受到多种复杂因素的共同作用&#xff0c;包括市场情绪、政策变化、大型机构的投资行为等&#xff0c;这些因素在不同的市场阶段对比特币价格波动产生直接或间接的影响。通过对比特币市场的深入分析&#…

66城代表齐聚!蓝卓分享“全国经验”,批量复制推动中小企业数字化转型

9月6日下午&#xff0c;2024中小企业数字化转型现场交流活动在浙江宁波隆重举行。 全国66个中小企业试点城市500多名中小企业主管部门及专家学者&#xff0c;制造业企业、数字化转型服务商等重点企业代表齐聚宁波&#xff0c;共同探讨中小企业数字化转型的模式和路径。 工业和…

酒店智能轻触开关:智慧化的创新实践

在追求高品质住宿体验的今天&#xff0c;酒店智能轻触开关作为智慧酒店建设的关键一环&#xff0c;正逐步成为提升酒店服务品质、优化运营效率、增强顾客满意度的有力工具。本文将深入探讨酒店智能轻触开关如何助力酒店实现智慧化管理&#xff0c;以及它所带来的多重变革。 一、…

VSCode连接docker

1.启动ssh服务 vim /root/.bashrc 或者 vim ~/.bashrc /usr/sbin/sshd #启动ssh服务~代表主目录&#xff0c;cd ~会返回root目录 cd / 返回最根上的目录 为了防止每次打开容器都要输入此指令&#xff0c;我们直接在 ~/.bashrc文件最后一行添加sshd启动命令即可。 打开终端…

【JAVA开源】基于Vue和SpringBoot的图书个性化推荐系统

本文项目编号 T 015 &#xff0c;文末自助获取源码 \color{red}{T015&#xff0c;文末自助获取源码} T015&#xff0c;文末自助获取源码 目录 一、系统介绍1.1 业务分析1.2 用例设计1.3 时序设计 二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究…

掌握ChatGPT写论文六步提问法,会提问才能写出优质好论文

大家好,感谢关注。我是七哥,一个在高校里不务正业,折腾学术科研AI实操的学术人。关于使用ChatGPT等AI学术科研的相关问题都可以分享,相互成就,共同进步,为大家带来最酷最有效的智能AI学术科研写作攻略。 今天给大家分享的是借助GPT一年发两篇SCI的学术大拿总结的ChatGPT六…

IPS和IDS有啥区别?

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330&scene21#wechat_redirect 《网安面试指南》…

ChatGPT付费创作系统V3.0.6独立版 WEB+H5+小程序端 (新增AI全网搜索+文档解析+豆包AI通道)安装部署教程

播播资源GPT付费体验系统最新版系统是一款基于ThinkPHP框架开发的AI问答小程序&#xff0c;是基于国外很火的ChatGPT进行开发的Ai智能问答小程序。这是一种基于人工智能技术的问答系统&#xff0c;可以实现智能回答用户提出的问题。相比传统的问答系统&#xff0c;ChatGPT可以更…

认识Linux及Linux的环境搭建

目录 1、什么是Linux2、Linux环境搭建2.1 下载安装 Xshell2.2 下载安装 VMware Workstation Pro2.3 选择适合自己系统 1、什么是Linux Linux&#xff0c;一般指GNU/Linux&#xff08;单独的Linux内核并不可直接使用&#xff0c;一般搭配GNU套件&#xff0c;故得此称呼&#xff…

ARM基础知识---CPU---处理器

目录 一、ARM架构 1.1.RAM---随机存储器 1.2.ROM---只读存储器 1.3.flash---闪存存储器 1.4.时钟&#xff08;振晶&#xff09; 1.5.复位 二、CPU---ARM920T 2.1.R0~R12---通用寄存器 2.2.PC程序计数器 2.3.LR连接寄存器 2.4.SP栈指针寄存器 2.5.CPSR当前程序状态寄存…

java,php,go,nodejs,Python开发web项目优缺点对比

Java 优点:java 是一门广泛应用于企业级开发的语言,丰富且庞大的开发框架和库。有较高的性能和可伸缩性。生态系统庞大且成熟,拥有大量的开源框架和工具,可以加速开发过程。 内置对多线程的支持,适合处理高并发的 Web 项目。 缺点:相比其他语言,Java 的语法相对冗长繁琐…

【H2O2|全栈】关于Photoshop | PS(4)

PS的一些杂谈&#xff08;亖&#xff09; 目录 PS的一些杂谈&#xff08;亖&#xff09; 前言 准备工作 图形工具 基本属性 混合选项 形状图层 文字工具 基本属性 进一步变化文字 组和图层 UI设计案例 预告和回顾 后话 前言 这一篇博客我将会写一下图形工具和…

【C++】STL学习——priority_queue(了解仿函数)

目录 priority_queue介绍迭代器种类priority_queue实现仿函数仿函数的使用 priority_queue介绍 优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它的第一个元素总是它所包含的元素中最大的。此上下文类似于堆&#xff0c;在堆中可以随时插入元素&#x…