Unity3D协程介绍 以及 使用


作者ChevyRay 2013928日,snaker7译  原文地址:http://unitypatterns.com/introduction-to-coroutines/

Unity中,协程(Coroutines)的形式是我最喜欢的功能之一,几乎在所有的项目中,我都会使用它来控制运动,序列,以及对象的行为。在这个教程中,我将会说明协程是如何工作的,并且会附上一些例子来介绍它的用法。

 

 

 

协程介绍


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

 

倒计时器

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


[csharp]  view plain  copy
  1. using Unity Engine;  
  2. using System.Collections;  
  3.    
  4. public class Countdown : MonoBehaviour  
  5. {  
  6.     public float timer = 3;  
  7.    
  8.     void Update()  
  9.     {  
  10.         timer -= Time.deltaTime;  
  11.         if(timer <= 0)  
  12.             Debug.Log("Timer has finished!");  
  13.     }  
  14. }  


 

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


[csharp]  view plain  copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.    
  4. public class MultiTimer : MonoBehaviour  
  5. {  
  6.     public float firstTimer = 3;  
  7.     public float secondTimer = 2;  
  8.     public float thirdTimer = 1;  
  9.    
  10.     void Update()  
  11.     {  
  12.         firstTimer -= Time.deltaTime;  
  13.         if(firstTimer <= 0)  
  14.             Debug.Log("First timer has finished!");  
  15.    
  16.         secondTimer -= Time.deltaTime;  
  17.         if(secondTimer <= 0)  
  18.             Debug.Log("Second timer has finished!");  
  19.    
  20.         thirdTimer -= Time.deltaTime;  
  21.         if(thirdTimer <= 0)  
  22.             Debug.Log("Third timer has finished!");  
  23.     }  
  24. }  


 

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

 

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


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


 

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

 

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

 

码入你的协程!

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


[csharp]  view plain  copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.    
  4. public class CoroutineCountdown : MonoBehaviour  
  5. {  
  6.     void Start()  
  7.     {  
  8.         StartCoroutine(Countdown());  
  9.     }  
  10.    
  11.     IEnumerator Countdown()  
  12.     {  
  13.         for(floattimer = 3; timer >= 0; timer -= Time.deltaTime)  
  14.             Yield return 0;  
  15.    
  16.         Debug.Log("This message appears after 3 seconds!");  
  17.     }  
  18. }  


 

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

 


[csharp]  view plain  copy
  1. StartCoroutine(Countdown());  


 

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

 

Yield

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

l IEnumerator 的返回值

l For循环中的yield return

 

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

 

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

 

一些例子

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

 

多次输出“Hello

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


[csharp]  view plain  copy
  1. //This will say hello 5 times, once each frame for 5 frames  
  2. IEnumerator SayHelloFiveTimes()  
  3. {  
  4.     Yield return 0;  
  5.     Debug.Log("Hello");  
  6.     Yield return 0;  
  7.     Debug.Log("Hello");  
  8.     Yield return 0;  
  9.     Debug.Log("Hello");  
  10.     Yield return 0;  
  11.     Debug.Log("Hello");  
  12.     Yield return 0;  
  13.     Debug.Log("Hello");  
  14. }  
  15.    
  16. //This will do the exact same thing as the above function!  
  17. IEnumerator SayHello5Times()  
  18. {  
  19.     for(inti = 0; i < 5; i++)  
  20.     {  
  21.         Debug.Log("Hello");  
  22.         Yield return 0;  
  23.     }  
  24. }  


 

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

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


[csharp]  view plain  copy
  1. //Once started, this will run until manually stopped or the object is destroyed  
  2. IEnumerator SayHelloEveryFrame()  
  3. {  
  4.     while(true)  
  5.     {  
  6.         //1. Say hello  
  7.         Debug.Log("Hello");  
  8.    
  9.         //2. Wait until next frame  
  10.         Yield return 0;  
  11.    
  12.     }//3. This is a forever-loop, goto 1  
  13. }  


 

计时

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


[csharp]  view plain  copy
  1. IEnumerator CountSeconds()  
  2. {  
  3.     int seconds = 0;  
  4.    
  5.     while(true)  
  6.     {  
  7.         for(float timer = 0; timer < 1; timer += Time.deltaTime)  
  8.             Yield return 0;  
  9.    
  10.         seconds++;  
  11.         Debug.Log(seconds +" seconds have passed since the Coroutine started.");  
  12.     }  
  13. }  


 

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

 

开始和终止协程

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


[csharp]  view plain  copy
  1. StartCoroutine(Countdown());  


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

 

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


[csharp]  view plain  copy
  1. StartCoroutine(FirstTimer());  
  2. StartCoroutine(SecondTimer());  


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


[csharp]  view plain  copy
  1. //If you start a Coroutine by name...  
  2. StartCoroutine("FirstTimer");  
  3. StartCoroutine("SecondTimer");  
  4.    
  5. //You can stop it anytime by name!  
  6. StopCoroutine("FirstTimer");  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值