Unity里做等待只能用unity协程用法吗

> 李勇2的博客详情
摘要: unity,协程,coroutine,IEnumerator
c#实现的协程样例代码:
/liyonghelpme/a2d4e1f4c9
#### c#迭代器和Unity协程
c# 本身支持迭代器IEnumerator,可以封装函数的执行上下文,但是迭代器不支持嵌套迭代,而为了实现带堆栈支持的协程,需要对c#做扩展。
Unity 中协程的嵌套形式如下:
yield return StartCoroutine(IEnumerator);
为了嵌套协程需要保存协程调用的堆栈信息,并且在IEnumerator结束的时候,继续执行上一层的IEnumerator.
因此Unity 封装了一个类Coroutine, 该类中保存有当前IEnumerator 等待的下一级IEnumerator(waitingFor) 以及当当前IEnumerator 执行结束的时候所要调用的上一级IEnumerator(ContinueWhenFinished).
#### 这样协程的执行流程如下:
1. 调用Unity StartCoroutine 接口
2. Unity 创建一个 Coroutine 协程对象, 并初始化协程中的迭代器
3. Unity 合适时机调用 协程中迭代器的 moveNext 方法
这时候 Unity获取迭代器的Current 返回值;如果返回值为NULL 则Unity将对迭代器的执行加入到下一帧的循环中,否则根据返回值不同做不同的处理。
如果返回值是一个new WaitForSeconds() 对象则将将协程加入到延迟回调队列里面;
如果返回值是另外一个协程对象,则设置当前协程的waitFor 为这个新协程对象,并且设置新协程对象的ContinueWhenFinish 为当前协程。
#### 协程创建
Unity 提供 StartCoroutine 接口用于创建一个协程, 创建完协程之后,将会调用协程Run接口,执行一下协程,若返回NULL,则将协程加入到下一帧执行队列中,否则根据情况来做不同的调度。
#### 协程执行
在初始化StartCoroutine 的时候会执行一次协程;之后根据协程执行的返回结果,来做不同的调度;
返回NULL 调度下一帧继续执行;
返回另外一个协程对象,则配置两个协程的关系,同时停止调度当前协程,将子协程加入到调度队列里面;
返回Wait 则将当前协程加入到等待调度队列里面。
#### 协程销毁
协程生命周期和Monobehavior 相绑定,当GameObject SetActive False的时候,所有协程都会停止。
在创建协程的时候,若协程将会加入到 Monobehavior的ActiveCoroutine 活跃协程列表中,接着执行一次协程;若协程本次执行完之后没有yield调用,则表示协程生命周期结束,之后调用的协程清理函数会清理掉协程。
否则协程会加入到延迟调用队列中。
当协程需要等待其它事件,或者其它协程的时候,协程引用计数+1,这样协程会在其它事件执行完之后,才检测是否执行清理工作。
人打赏支持
参与源创会
领取时间:
“”在线下联结了各位 OSCer,推广开源项目和理念,很荣幸有你的参与~
领取条件:参与过开源中国“源创会”的 OSCer 可以领取
码字总数 59898
支付宝支付
微信扫码支付
打赏金额: ¥
已支付成功
打赏金额: ¥
& 开源中国(OSChina.NET) |
开源中国社区(OSChina.net)是工信部
指定的官方社区Unity3D(97)
写游戏代码,往往最终需要代码为连续的事件.结果会像这样:
[它可以实现将一段程序延迟执行或者将其各个部分分布在一个时间段内连续执行。]
往往使用yield语句更为方便.yield语句是一个特殊的返回类型,它确保函数从yield语句的下一行继续执行.
你也可以传递时间值到yield语句,Update函数会在yield结束后执行下一语句.
你可以入栈并连接协程.
这个例子将执行Do,但是do函数之后的print指令会立刻执行.
这个例子将执行Do,并等待,直到Do完成再执行其他语句.【注:这里的等待是把线程时间交给其他任务,而不是阻塞式等待】
任何事件处理程序都可以是协同程序 。
注意你不能在Update或FixedUpdate函数内使用yield,但是你能使用 StartCoroutine &开始一个函数.
查看 YieldInstruction , WaitForSeconds , WaitForFixedUpdate , Coroutine &and MonoBehaviour.StartCoroutine &可以获得更多使用yield的信息.
yield return可以看做是一种特殊的return,会返回到父类继续执行,但是yield return后面的类型或方法会有一个执行条件,当条件满足时会回调包含yield的子函数,例如下面代码
在执行yield return new WaitForSeconds(waitTime)时暂停的条件没有满足,故返回到start函数中继续执行,直到满足条件后再回调WaitAndPrint,所以输出为:
Starting:0
Before WaiAndPrint:0
WaitAndPrint:2.12291
例2:(协程嵌套使用)
因为start为顶级函数,所以会阻塞在这里,直到StartCoroutine(WaitAndPrint(2.0F))执行完毕,输出为:
starting:0
WaitAndPrint:2.00315
done:2.00315
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:40193次
排名:千里之外
原创:14篇
转载:131篇
评论:31条
(3)(1)(3)(2)(9)(11)(5)(18)(22)(31)(42)【Unity】懂得协程的原理1——实现一个自己的WaitForSeconds - 人工智能当前位置:& &&&【Unity】懂得协程的原理1——实现一个自己的WaitFo【Unity】懂得协程的原理1——实现一个自己的WaitForSeconds&&网友分享于:&&浏览:0次【Unity】理解协程的原理1——实现一个自己的WaitForSeconds协程的所能达到的效果就是在指定的时间点上执行需要执行的代码,Unity中开始一个协程的函数是StartCoroutine,而提供的延迟的类有以下几种分别是
new WaitForEndOfF
//等待一帧
new WaitForFixedU
//等待一个FixedUpdate(固定时间间隔)
new WaitForS
//等待外部资源加载完毕
本文就针对其中的WaitForSeconds实现进行探究。 因为在开发过程中,很多时候会遇到一种情况就是,超时或者是符合某种条件就继续运行,使用系统提供WaitForSeconds已经无法满足要求了,这时候有两种解决方法,一种是使用StopCoroutine来停止协程,但是对于Unity来说,这种行为会造成很大的开销,那么可以采用重写WaitForSeconds,使它能达到我们的要求。以下是我认为的WaitForSeconds的实现:
/// &summary&
/// 任务扩展
/// &/summary&
static class CTaskExtend
static public IEnumerator WaitForSeconds(float second)
DateTime init_dt = DateTime.N
while(true)
time = DateTime.Now - init_
if(time.TotalSeconds &= second)
调用的方法与Unity差不多:
yield return CTaskExtend.WaitForSeconds(delayTime);
看上去似乎非常简单,确实也是非常简单,那么如果遇到之前说的那一种情况(超时或者是符合某种条件就继续运行),这里需要做怎么样的改动呢?如下:
/// &summary&
/// 任务扩展
/// &/summary&
static class CTaskExtend
public delegate bool CondDelegate();
static public IEnumerator WaitForSeconds(float second, CondDelegate cond = null)
DateTime init_dt = DateTime.N
while(true)
time = DateTime.Now - init_
if (time.TotalSeconds &= second && !cond())
加上了一个回调函数,每次都会检查这个函数是否为true,如果为true则停止等待。
12345678910
12345678910
12345678910 上一篇:下一篇:文章评论相关解决方案 1234567891011 Copyright & &&版权所有Unity3D之协程(Coroutines & Yield )
我的图书馆
Unity3D之协程(Coroutines & Yield )
写游戏代码,往往最终需要代码为连续的事件.结果会像这样:
[它可以实现将一段程序延迟执行或者将其各个部分分布在一个时间段内连续执行。]
[csharp] &span&style="font-size:18"&private&int&state&=&0;&&void&Update()&&{&&&&&&&&&&if&(state&==&0)&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&//做步骤0&&&&&&&&&&&&&&&&&&state&=&1;&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&if&(state&==&1)&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&//&做步骤1&&&&&&&&&&&&&&&&&&state&=&2;&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&//&...&&}&&/span&&&
往往使用yield语句更为方便.yield语句是一个特殊的返回类型,它确保函数从yield语句的下一行继续执行.
[csharp] &span&style="font-size:18"&while(true)&{&&&&&&&&&&//&做步骤0&&&&&&&&&&yield&return&0;&&&&&&&&&&&//&等待一帧&&&&&&&&&&//&做步骤1&&&&&&&&&&yield&return&2;&&&&&&&&&&&//&等待两帧&&&&&&&&&&//&...&&}&&/span&&&
你也可以传递时间值到yield语句,Update函数会在yield结束后执行下一语句.
[csharp] &span&style="font-size:18"&&&//&do&something&&&&yield&return&WaitForSeconds&&(5.0);&&&&//等待5秒&&&&//&do&something&more...&&&/span&&&
你可以入栈并连接协程.
这个例子将执行Do,但是do函数之后的print指令会立刻执行.
[csharp] &span&style="font-size:18"&Do&();&&Console.WriteLine("This&is&printed&immediately");&&IEnumerator&&Do&()&&{&&&&&&Console.WriteLine("Do&now");&&&&&&yield&return&new&WaitForSeconds&&(2);&&&&&&&&&&&&Console.WriteLine("Do&2&seconds&later");&&}&&/span&&&
这个例子将执行Do,并等待,直到Do完成再执行其他语句.【注:这里的等待是把线程时间交给其他任务,而不是阻塞式等待】
[csharp] &span&style="font-size:18"&//&启动协程&&yield&return&StartCoroutine("Do");&&Console.WriteLine("Also&after&2&seconds");&&Console.WriteLine&("这个print将在Do协程执行完以后显示。");&&IEnumerator&&Do&()&&{&&&&&&&&Console.WriteLine("Do&now");&&yield&return&new&WaitForSeconds&&(2);&&Console.WriteLine("Do&2&seconds&later");&&}&&&/span&&&
任何事件处理程序都可以是协同程序 。
注意你不能在Update或FixedUpdate函数内使用yield,但是你能使用 StartCoroutine &开始一个函数.
查看 YieldInstruction , WaitForSeconds , WaitForFixedUpdate , Coroutine &and MonoBehaviour.StartCoroutine &可以获得更多使用yield的信息.
yield return可以看做是一种特殊的return,会返回到父类继续执行,但是yield return后面的类型或方法会有一个执行条件,当条件满足时会回调包含yield的子函数,例如下面代码
[csharp] &span&style="font-size:18"&void&Start&()&{&&&&&&&&&&&&&&print("Starting:"&+&Time.time);&&&&&&&&&&&&&&StartCoroutine(WaitAnPrint(2.0F));&&&&&&&&&&&&&&print("Before&WaiAndPrint:"&+&Time.time);&&&&&&&&&&}&&&&&&IEnumerator&WaitAndPrint(float&waitTime)&&&&&&&&&&{&&&&&&&&&&&&&&yield&return&new&WaitForSeconds(waitTime);&&&&&&&&&&&&&&print("WaitAndPrint:"&+&Time.time);&&&&&&&&&&&&&&}&&&/span&&&
在执行yield return new WaitForSeconds(waitTime)时暂停的条件没有满足,故返回到start函数中继续执行,直到满足条件后再回调WaitAndPrint,所以输出为:
Starting:0
Before WaiAndPrint:0
WaitAndPrint:2.12291
[csharp] &span&style="font-size:18"&IEnumerator&Start()&&&&&&&&&&{&&&&&&&&&&&&&&print("starting:"&+&Time.time);&&&&&&&&&&&&&&yield&return&StartCoroutine(WaitAndPrint(2.0F));&&&&&&&&&&&&&&print("done:"&+&Time.time);&&&&&&&&&&}&&&&&&IEnumerator&WaitAndPrint(float&waitTime)&&&&&&&&&&{&&&&&&&&&&&&&&yield&return&new&WaitForSeconds(waitTime);&&&&&&&&&&&&&&print("WaitAndPrint:"&+&Time.time);&&&&&&&&&&&&&&}&/span&&&
因为start为顶级函数,所以会阻塞在这里,直到StartCoroutine(WaitAndPrint(2.0F))执行完毕,输出为:
starting:0
WaitAndPrint:2.00315
done:2.00315
[csharp] &pre&name="code"&class="csharp"&&pre&name="code"&class="csharp"&&pre&name="code"&class="csharp"&&p&&/p&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&&&/pre&&/pre&&/pre& &写游戏代码,往往最终需要代码为连续的事件.结果会像这样:
[它可以实现将一段程序延迟执行或者将其各个部分分布在一个时间段内连续执行。]
[csharp] &span&style="font-size:18"&private&int&state&=&0;&&void&Update()&&{&&&&&&&&&&if&(state&==&0)&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&//做步骤0&&&&&&&&&&&&&&&&&&state&=&1;&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&if&(state&==&1)&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&//&做步骤1&&&&&&&&&&&&&&&&&&state&=&2;&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&//&...&&}&&/span&&&
往往使用yield语句更为方便.yield语句是一个特殊的返回类型,它确保函数从yield语句的下一行继续执行.
[csharp] &span&style="font-size:18"&while(true)&{&&&&&&&&&&//&做步骤0&&&&&&&&&&yield&return&0;&&&&&&&&&&&//&等待一帧&&&&&&&&&&//&做步骤1&&&&&&&&&&yield&return&2;&&&&&&&&&&&//&等待两帧&&&&&&&&&&//&...&&}&&/span&&&
你也可以传递时间值到yield语句,Update函数会在yield结束后执行下一语句.
[csharp] &span&style="font-size:18"&&&//&do&something&&&&yield&return&WaitForSeconds&&(5.0);&&&&//等待5秒&&&&//&do&something&more...&&&/span&&&
你可以入栈并连接协程.
这个例子将执行Do,但是do函数之后的print指令会立刻执行.
[csharp] &span&style="font-size:18"&Do&();&&Console.WriteLine("This&is&printed&immediately");&&IEnumerator&&Do&()&&{&&&&&&Console.WriteLine("Do&now");&&&&&&yield&return&new&WaitForSeconds&&(2);&&&&&&&&&&&&Console.WriteLine("Do&2&seconds&later");&&}&&/span&&&
这个例子将执行Do,并等待,直到Do完成再执行其他语句.【注:这里的等待是把线程时间交给其他任务,而不是阻塞式等待】
[csharp] &span&style="font-size:18"&//&启动协程&&yield&return&StartCoroutine("Do");&&Console.WriteLine("Also&after&2&seconds");&&Console.WriteLine&("这个print将在Do协程执行完以后显示。");&&IEnumerator&&Do&()&&{&&&&&&&&Console.WriteLine("Do&now");&&yield&return&new&WaitForSeconds&&(2);&&Console.WriteLine("Do&2&seconds&later");&&}&&&/span&&&
任何事件处理程序都可以是协同程序 。
注意你不能在Update或FixedUpdate函数内使用yield,但是你能使用 StartCoroutine &开始一个函数.
查看 YieldInstruction , WaitForSeconds , WaitForFixedUpdate , Coroutine &and MonoBehaviour.StartCoroutine &可以获得更多使用yield的信息.
yield return可以看做是一种特殊的return,会返回到父类继续执行,但是yield return后面的类型或方法会有一个执行条件,当条件满足时会回调包含yield的子函数,例如下面代码
[csharp] &span&style="font-size:18"&void&Start&()&{&&&&&&&&&&&&&&print("Starting:"&+&Time.time);&&&&&&&&&&&&&&StartCoroutine(WaitAnPrint(2.0F));&&&&&&&&&&&&&&print("Before&WaiAndPrint:"&+&Time.time);&&&&&&&&&&}&&&&&&IEnumerator&WaitAndPrint(float&waitTime)&&&&&&&&&&{&&&&&&&&&&&&&&yield&return&new&WaitForSeconds(waitTime);&&&&&&&&&&&&&&print("WaitAndPrint:"&+&Time.time);&&&&&&&&&&&&&&}&&&/span&&&
在执行yield return new WaitForSeconds(waitTime)时暂停的条件没有满足,故返回到start函数中继续执行,直到满足条件后再回调WaitAndPrint,所以输出为:
Starting:0
Before WaiAndPrint:0
WaitAndPrint:2.12291
[csharp] &span&style="font-size:18"&IEnumerator&Start()&&&&&&&&&&{&&&&&&&&&&&&&&print("starting:"&+&Time.time);&&&&&&&&&&&&&&yield&return&StartCoroutine(WaitAndPrint(2.0F));&&&&&&&&&&&&&&print("done:"&+&Time.time);&&&&&&&&&&}&&&&&&IEnumerator&WaitAndPrint(float&waitTime)&&&&&&&&&&{&&&&&&&&&&&&&&yield&return&new&WaitForSeconds(waitTime);&&&&&&&&&&&&&&print("WaitAndPrint:"&+&Time.time);&&&&&&&&&&&&&&}&/span&&&
因为start为顶级函数,所以会阻塞在这里,直到StartCoroutine(WaitAndPrint(2.0F))执行完毕,输出为:
starting:0
WaitAndPrint:2.00315
done:2.00315
[csharp] &pre&name="code"&class="csharp"&&pre&name="code"&class="csharp"&&pre&name="code"&class="csharp"&&p&&/p&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&pre&&/pre&&&&&&/pre&&/pre&&/pre& &
发表评论:
TA的最新馆藏[转]&问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
我正在用Unity3D,使用的是C#,我正在尝试着写一个For循环,在一个循环中实例化多个游戏对象,但是我想稍微交错实例化,在下一个游戏对象实例化之前,比如说0.5秒,
我认为这和做一个单循环一样简单 :
for (int i = 0; i &= 3; i++)
Instantiate(gameObject, objectSpawn[i].transform.position, Quaternion.identity);
StartCoroutine(WeaponsCooldown(6));
StartCoroutine(WeaponsCooldown(2));
在协程中,有个切换案例,所以我可以重复多次使用。
IEnumerator WeaponsCooldown(int coolTrigger)
switch (coolTrigger)
mainFire =
yield return new WaitForSeconds(5);
mainFire =
leftFire =
yield return new WaitForSeconds(3);
leftFire =
...down to case 6 (for this project)
我得到的结果是这样的: For循环之外的StartCoroutine能够很好地激发,这是武器的主冷却时间,保证player不断地朝它发射。然而,For循环之内的StartCoroutine似乎并没有减缓For循环,由于我刚接触Unity和C#,所以我不知道如何找出错在哪里。
我假设StartCoroutine是一个单独的线程,除了运行Update线程,那是For循环仍在继续的原因,因为它不会等待线程结束。
如果是这样的话,有什么解决方法呢?
我想在一个脚本内完成的原因是:player可以有25个以上的硬点,这样可以更加简单的用较少的硬点重用多个船只,对于我个人来说也会更加方便一点。
来源: Stack Overflow 相关的热门问题
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
Heisenbug:
For循环中的startcoroutine似乎不会减缓For循环。
你的脚本得到的结果是在同一帧中实例化所有的GameObject,并开始所有的协同程序(不同的冷却持续不同的时间)。
假设除了Update 线程以外,StartCoroutine是一个单独运行的线程。这是For循环继续的原因,因为它不会等待协程来完成。
协程在所有的线程中有用。它们是(iterator blocks)
引用一下 Jon Skeet的话:
在 C# 2, 当你使用迭代器块来执行 IEnumerable 或 IEnumerator(或通用表格)编译器会为你完成所有的复杂工作。它建立一个状态机,在迭代器块中,迭代器有效的获得执行代码,随着它的运行yield值。
另一个描述它的方法是协同式多任务(cooperative multitasking)。
我在尝试写一个For循环,在1 个循环中实例化所有的游戏对象,但是我只想稍微错开它们的实例化,比如0.5秒,在下一个游戏对象实例化之前。
不考虑实现过程中的具体行为,有个简单的方法可以延迟实例化调用(0.5秒):在协程自身中wrap For循环。
public IEnumerator DelayInstantiate()
for (int i = 0; i &= 3; i++)
Instantiate(gameObject, objectSpawn[i].transform.position, Quaternion.identity);
StartCoroutine(WeaponsCooldown(6)); //or do what you want
yield return new WaitForSeconds(0.5f);
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
你的假设不对,协程并非是独立线程,而是在主线程之内执行。具体来说,当你启动一个协程时,他会立即执行,但只是执行到第一个yield return之前,协程下次执行是在yield条件满足时。事实上,通过实验可以发现,如果仅仅是yield return null,协程的代码会在Update()执行完之后被执行,每次执行到下一个yield return null,再去执行Update()。
如果理解协程并非多线程这件事后,你的问题就很好解释了。如果想在for循环中每一次延时,就要插入yield return的延时条件。将for所在方法作为协程启动就OK了。
同步到新浪微博
分享到微博?
你好!看起来你挺喜欢这个内容,但是你还没有注册帐号。 当你创建了帐号,我们能准确地追踪你关注的问题,在有新答案或内容的时候收到网页和邮件通知。还能直接向作者咨询更多细节。如果上面的内容有帮助,记得点赞 (????)? 表示感谢。
明天提醒我
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:
扫扫下载 App
SegmentFault
一起探索更多未知}

我要回帖

更多关于 unity3d 协程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信