位置: 编程技术 - 正文

Unity3D协程学习(unity协程的工作原理)

编辑:rootadmin

推荐整理分享Unity3D协程学习(unity协程的工作原理),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:unity3d协程的作用,unity3d的协程和线程的区别,unity协程用法,unity协程用法,unity中协程,unity3d协程的作用,unity协程的工作原理,unity协程用法,内容如对您有帮助,希望把文章链接给更多的朋友!

转载自: ,年9月日,snaker7译 原文地址:

协程介绍

Unity的协程系统是基于C#的一个简单而强大的接口 ,IEnumerator,它允许你为自己的集合类型编写枚举器。这一点你不必关注太多,我们直接进入一个简单的例子来看看协程到底能干什么。首先,我们来看一下这段简单的代码...

倒计时器

这是一个简单的脚本组件,只做了倒计时,并且在到达0的时候log一个信息。

[csharp] view plaincopyusing Unity Engine; using System.Collections; public class Countdown : MonoBehaviour { public float timer = 3; void Update() { timer -= Time.deltaTime; if(timer <= 0) Debug.Log("Timer has finished!"); } }

还不错,代码简短实用,但问题是,如果我们需要复杂的脚本组件(像一个角色或者敌人的类),拥有多个计时器呢?刚开始的时候,我们的代码也许会是这样的:

[csharp] view plaincopyusing UnityEngine; using System.Collections; public class MultiTimer : MonoBehaviour { public float firstTimer = 3; public float secondTimer = 2; public float thirdTimer = 1; void Update() { firstTimer -= Time.deltaTime; if(firstTimer <= 0) Debug.Log("First timer has finished!"); secondTimer -= Time.deltaTime; if(secondTimer <= 0) Debug.Log("Second timer has finished!"); thirdTimer -= Time.deltaTime; if(thirdTimer <= 0) Debug.Log("Third timer has finished!"); } }

尽管不是太糟糕,但是我个人不是很喜欢自己的代码中充斥着这些计时器变量,它们看上去很乱,而且当我需要重新开始计时的时候还得记得去重置它们(这活我经常忘记做)。

如果我只用一个for循环来做这些,看上去是否会好很多?

[csharp] view plaincopyfor(float timer = 3; timer >= 0; timer -= Time.deltaTime) { //Just do nothing... } Debug.Log("This happens after 5 seconds!");

现在每一个计时器变量都成为for循环的一部分了,这看上去好多了,而且我不需要去单独设置每一个跌倒变量。

好的,你可能现在明白我的意思:协程可以做的正是这一点!

码入你的协程!

现在,这里提供了上面例子运用协程的版本!我建议你从这里开始跟着我来写一个简单的脚本组件,这样你可以在你自己的程序中看到它是如何工作的。

[csharp] view plaincopyusing UnityEngine; using System.Collections; public class CoroutineCountdown : MonoBehaviour { void Start() { StartCoroutine(Countdown()); } IEnumerator Countdown() { for(floattimer = 3; timer >= 0; timer -= Time.deltaTime) Yield return 0; Debug.Log("This message appears after 3 seconds!"); } }

这看上去有点不一样,没关系,接下来我会解释这里到底发生了什么。

[csharp] view plaincopyStartCoroutine(Countdown());

这一行用来开始我们的Countdown程序,注意,我并没有给它传入参数,但是这个方法调用了它自己(这是通过传递Countdown的return返回&#;来实现的)。

Yield

在Countdown方法中其他的都很好理解,除了两个部分:

l IEnumerator 的返回&#;

l For循环中的yield return

为了能在连续的多帧中(在这个例子中,3秒钟等同于很多帧)调用该方法,Unity必须通过某种方式来存储这个方法的状态,这是通过IEnumerator 中使用yield return语句得到的返回&#;,当你“yield”一个方法时,你相当于说了,“现在停止这个方法,然后在下一帧中从这里重新开始!”。

注意:用0或者null来yield的意思是告诉协程等待下一帧,直到继续执行为止。当然,同样的你可以继续yield其他协程,我会在下一个教程中讲到这些。

一些例子

协程在刚开始接触的时候是非常难以理解的,无论是新手还是经验丰富的程序员我都见过他们对于协程语句一筹莫展的时候。因此我认为通过例子来理解它是最好的方法,这里有一些简单的协程例子:

多次输出“Hello”

记住,yield return是“停止执行方法,并且在下一帧从这里重新开始”,这意味着你可以这样做:

[csharp] view plaincopy//This will say hello 5 times, once each frame for 5 frames IEnumerator SayHelloFiveTimes() { Yield return 0; Debug.Log("Hello"); Yield return 0; Debug.Log("Hello"); Yield return 0; Debug.Log("Hello"); Yield return 0; Debug.Log("Hello"); Yield return 0; Debug.Log("Hello"); } //This will do the exact same thing as the above function! IEnumerator SayHello5Times() { for(inti = 0; i < 5; i&#;&#;) { Debug.Log("Hello"); Yield return 0; } }

每一帧输出“Hello”,无限循环。。。

通过在一个while循环中使用yield,你可以得到一个无限循环的协程,这几乎就跟一个Update()循环等同。。。

[csharp] view plaincopy//Once started, this will run until manually stopped or the object is destroyed IEnumerator SayHelloEveryFrame() { while(true) { //1. Say hello Debug.Log("Hello"); //2. Wait until next frame Yield return 0; }//3. This is a forever-loop, goto 1 } Unity3D协程学习(unity协程的工作原理)

计时

...不过跟Update()不一样的是,你可以在协程中做一些更有趣的事:

[csharp] view plaincopyIEnumerator CountSeconds() { int seconds = 0; while(true) { for(float timer = 0; timer < 1; timer &#;= Time.deltaTime) Yield return 0; seconds&#;&#;; Debug.Log(seconds &#;" seconds have passed since the Coroutine started."); } }

这个方法突出了协程一个非常酷的地方:方法的状态被存储了,这使得方法中定义的这些变量都会保存它们的&#;,即使是在不同的帧中。还记得这个教程开始时那些烦人的计时器变量吗?通过协程,我们再也不需要担心它们了,只需要把变量直接放到方法里面!

开始和终止协程

之前,我们已经学过了通过 StartCoroutine()方法来开始一个协程,就像这样:

[csharp] view plaincopyStartCoroutine(Countdown());

如果我们想要终止所有的协程,可以通过StopAllCoroutines()方法来实现,它的所要做的就跟它的名字所表达的一样。注意,这只会终止在调用该方法的对象中(应该是指调用这个方法的类吧)开始的协程,对于其他的MonoBehavior类中运行的协程不起作用。

如果我们有以下这样两条协程语句:

[csharp] view plaincopyStartCoroutine(FirstTimer()); StartCoroutine(SecondTimer());

。。。那我们怎么终止其中的一个协程呢?在这个例子里,这是不可能的,如果你想要终止某一个特定的协程,那么你必须得在开始协程的时候将它的方法名作为字符串,就像这样:

[csharp] view plaincopy//If you start a Coroutine by name... StartCoroutine("FirstTimer"); StartCoroutine("SecondTimer"); //You can stop it anytime by name! StopCoroutine("FirstTimer");

更多关于协程的学习

即将为你带来:“Scripting with Coroutines”,一个更深入的介绍,关于如何使用协程以及如何通过协程编写对象行为。

扩展链接

l Coroutines – Unity Script Reference

如果你知道其他很棒的关于协程的Unity教程,或者相关的主题,请在回复中分享链接!当然,如果在教程有什么问题,比如链接无效或者其他一些问题,欢迎给我发邮件。

作者ChevyRay ,年9月日,snaker7译 原文地址: view plaincopyUsing UnityEngine; Using System.Collections; Public class TimerExample : MonoBehaviour { Void Start() { //Log "Hello!" 5 times with 1 second between each log StartCoroutine(RepeatMessage(5, 1.0f,"Hello!")); } IEnumerator RepeatMessage(int count,float frequency,string message) { for(int i = 0; i < count; i&#;&#;) { Debug.Log(message); for(float timer = 0; timer < frequency; timer &#;= Time.deltaTime) Yield return 0; } } }

嵌套的协程

在此之前,我们yield的时候总是用0(或者null),仅仅告诉程序在继续执行前等待下一帧。协程最强大的一个功能就是它们可以通过使用yield语句来相互嵌套。

&#;见为实,我们先来创建一个简单的Wait()程序,不需要它做任何事,只需要在运行的时候等待一段时间就结束。

[csharp] view plaincopyIEnumerator Wait(float duration) { for(float timer = 0; timer < duration; timer &#;= Time.deltaTime) Yield return 0; }

接下来我们要编写另一个协程,如下:

[csharp] view plaincopyUsing UnityEngine; Using System.Collections; Public class TimerExample : MonoBehaviour { voidStart() { StartCoroutine(SaySomeThings()); } //Say some messages separated by time IEnumerator SaySomeThings() { Debug.Log("The routine has started"); Yield return StartCoroutine(Wait(1.0f)); Debug.Log("1 second has passed since the last message"); Yield return StartCoroutine(Wait(2.5f)); Debug.Log("2.5 seconds have passed since the last message"); } //Our wait function IEnumerator Wait(float duration) { for(float timer = 0; timer < duration; timer &#;= Time.deltaTime) Yield return 0; } }

第二个方法用了yield,但它并没有用0或者null,而是用了Wait()来yield,这相当于是说,“不再继续执行本程序,直到Wait程序结束”。

现在,协程在程序设计方面的能力要开始展现了。

控制对象行为的例子

在最后一个例子中,我们就来看看协程如何像创建方便的计时器一样来控制对象行为。协程不仅仅可以使用可计数的时间来yield,它还能很巧妙地利用任何条件。将它与嵌套结合使用,你会得到控制游戏对象状态的最强大工具。

运动到某一位置

对于下面这个简单脚本组件,我们可以在Inspector面板中给targetPosition和moveSpeed变量赋&#;,程序运行的时候,该对象就会在协程的作用下,以我们给定的速度运动到给定的位置。

[csharp] view plaincopyusingUnityEngine; Using System.Collections; Public class MoveExample : MonoBehaviour { ublic Vector3 targetPosition; ublic float moveSpeed; Void Start() { StartCoroutine(MoveToPosition(targetPosition)); } IEnumerator MoveToPosition(Vector3 target) { while(transform.position != target) { transform.position = Vector3.MoveTowards(transform.position, target, moveSpeed * Time.deltaTime); Yield return 0; } } }

这样,这个程序并没有通过一个计时器或者无限循环,而是根据对象是否到达指定位置来yield。

按指定路径前进

我们可以让运动到某一位置的程序做更多,不仅仅是一个指定位置,我们还可以通过数组来给它赋&#;更多的位置,通过MoveToPosition() ,我们可以让它在这些点之间持续运动。

[csharp] view plaincopyUsing UnityEngine; Using System.Collections; Public class MoveExample : MonoBehaviour { ublic Vector3[] path; ublic float moveSpeed; Void Start() { StartCoroutine(MoveOnPath(true)); } IEnumerator MoveOnPath(bool loop) { do { foreach(var point in path) Yield return StartCoroutine(MoveToPosition(point)); } while(loop); } IEnumerator MoveToPosition(Vector3 target) { while(transform.position != target) { transform.position = Vector3.MoveTowards(transform.position, target, moveSpeed * Time.deltaTime); Yield return 0; } } }

我还加了一个布尔变量,你可以控制在对象运动到最后一个点时是否要进行循环。

把Wait()程序加进来,这样就能让我们的对象在某个点就可以选择是否暂停下来,就像一个正在巡逻的AI守卫一样,这真是锦上添花啊!

注意:

如果你刚接触协程,我希望这两个教程能帮助你了解它们是如何工作的,以及如何来使用它们。以下是一些在使用协程时须谨记的其他注意事项:

l 在程序中调用StopCoroutine()方法只能终止以字符串形式启动(开始)的协程;l 多个协程可以同时运行,它们会根据各自的启动顺序来更新;l 协程可以嵌套任意多层(在这个例子中我们只嵌套了一层);l 如果你想让多个脚本访问一个协程,那么你可以定义静态的协程;l 协程不是多线程(尽管它们看上去是这样的),它们运行在同一线程中,跟普通的脚本一样;l 如果你的程序需要进行大量的计算,那么可以考虑在一个随时间进行的协程中处理它们;l IEnumerator类型的方法不能带ref或者out型的参数,但可以带被传递的引用;l 目前在Unity中没有简便的方法来检测作用于对象的协程数量以及具体是哪些协程作用在对象上。

如果您发现教程中存在问题和错误的信息,或者有任何建议又或者您想要在这里看到其他需要的教程,可以发邮件或者在评论中留言。

NGUI优化方法 转载自:

NGUI优化方法总结 转载自:

Unity优化 转载自:

标签: unity协程的工作原理

本文链接地址:https://www.jiuchutong.com/biancheng/380089.html 转载请保留说明!

上一篇:Unity3D协程(unity协程会阻塞主线程吗)

下一篇:NGUI优化方法(优化lr)

  • 应该如何
  • 缴纳印花税的会计凭证
  • 保险公司收车船使用税吗
  • 劳务费专票需要备注吗
  • 两地拿工资怎么扣个税
  • 应付职工薪酬包括哪些内容
  • 小企业销售费用多少算高
  • 甲供材料税金如何结算
  • 在建工程领用原材料的账务处理
  • 商贸公司可以用加工厂的商标委托加工生产吗
  • 企业接到银行通知,借入长期借款的应付利息为15000
  • 个人去税务局能开什么类型的发票
  • 免税的投资收益需要调整利润表吗
  • 发放股票股利要考虑时间权重吗
  • 公司股权转1元转让还涉及哪些税务风险?
  • 详解劳务报酬个人所得税应该怎么计算?
  • 实收资本印花税申报期限
  • 银行承兑汇票上的承兑日期
  • 新办企业需要做什么
  • 交通卡定额发票购买
  • 公司开业期间的费用入管理费用其他行吗
  • 怎么查以前申报的财务报表
  • 发票查询发票代码有误
  • 1697510021
  • 一般纳税人转让固定资产增值税税率
  • 个人垫付汽车修理怎么办
  • 非正常损失为什么不赔偿
  • 未开票收入跨年度如何申报冲回
  • 隐藏分区怎么打开
  • win10图片密码怎么全屏显示
  • 苹果充电宝怎么验真假
  • 固定资产出租需交什么税
  • 定额备用金的核算可分为哪几类
  • 绿萝 用什么土
  • 购进商品发生溢余的核算
  • 车船税单子什么样
  • php框架实例
  • yolo4损失函数
  • 图像风格迁移网站
  • 个税手续费会计分录
  • 机械租赁费税率是多少
  • 同一控制下企业合并发生的审计费用计入
  • 增值税是指哪些税种
  • 厂房无偿使用交税吗
  • 公司购买银行理财产品账务处理
  • 小规模纳税人适用什么会计准则
  • 会计法中单位负责人均指法定代表人
  • 成本计算的方法定额法
  • 交易性金融资产属于流动资产
  • 应交税费期初数调整
  • 出租周转材料的租金会计科目
  • 交易费用计入何种科目
  • 广告费和业务宣传费税前扣除基数
  • 去年的招待费做什么科目
  • 金税盘技术维护费可以抵扣吗
  • 生产成本是费用要素吗
  • 职工教育经费的差旅费可以抵扣吗
  • sql触发器使用存储过程
  • xp系统为什么不能用了
  • win7无法访问局域网电脑
  • drivemgr.exe 病毒介绍
  • win7指令代码
  • VS2012中C++,#include无法打开源文件
  • unity团队
  • 简单的jquery插件实例
  • node.js和go
  • python输出unicode编码
  • 怎么用批处理显示文字
  • 电子词典笔哪个品牌好
  • android 动画分类
  • js文件设置编码
  • 原生js
  • 浅谈插入排序算法在Python程序中的实现及简单改进
  • android混淆后怎么破解
  • 房产原值包括哪些内容
  • 建筑业小规模纳税人工程结算增值税
  • 接受虚开转出滞纳金
  • 四川税务网上报税
  • 根据消费税暂行条例的规定,纳税人自产自用
  • 云南烟草税收是多少
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设