协程介绍

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



协程介绍


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



倒计时器


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

[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
using UnityEngine;
using System.Collections;
  
Public class Countdown : MonoBehaviour
{
     Public float timer = 3;
  
     void Update()
     {
         timer -= Time.deltaTime;
         if (timer <= 0)
             Debug.Log( "Timer has finished!" );
     }
}


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

[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using 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循环来做这些,看上去是否会好很多?

[C#]  纯文本查看  复制代码
?
1
2
3
4
5
for ( float timer = 3; timer >= 0; timer -= Time.deltaTime)
{
     //Just do nothing...
}
Debug.Log( "This happens after 5 seconds!" );


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


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



码入你的协程!


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

[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
using UnityEngine;
using System.Collections;
  
public class CoroutineCountdown : MonoBehaviour
{
     void Start()
     {
         StartCoroutine(Countdown());
     }
  
     IEnumerator Countdown()
     {
         for ( float timer = 3; timer >= 0; timer -= Time.deltaTime)
             Yield return 0;
  
         Debug.Log( "This message appears after 3 seconds!" );
     }
}




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

[C#]  纯文本查看  复制代码
?
1
StartCoroutine(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是“停止执行方法,并且在下一帧从这里重新开始”,这意味着你可以这样做:
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//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()循环等同。。。
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
//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
}

计时
...不过跟Update()不一样的是,你可以在协程中做一些更有趣的事:
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
IEnumerator CountSeconds()
{
     intseconds = 0;
  
     while ( true )
     {
         for (floattimer = 0; timer < 1; timer += Time.deltaTime)
             Yield return 0;
  
         seconds++;
         Debug.Log(seconds + " seconds have passed since the Coroutine started." );
     }
}



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


开始和终止协程

之前,我们已经学过了通过 StartCoroutine()方法来开始一个协程,就像这样:
[C#]  纯文本查看  复制代码
?
1
StartCoroutine(Countdown());


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

如果我们有以下这样两条协程语句:
[C#]  纯文本查看  复制代码
?
1
2
StartCoroutine(FirstTimer());
StartCoroutine(SecondTimer());


。。。那我们怎么终止其中的一个协程呢?在这个例子里,这是不可能的,如果你想要终止某一个特定的协程,那么你必须得在开始协程的时候将它的方法名作为字符串,就像这样:
[C#]  纯文本查看  复制代码
?
1
2
3
4
5
6
//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 ,2013年9月28日,snaker7译  原文地址:http://unitypatterns.com/introduction-to-coroutines/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值