计时器例子
在
第一个教程中,我们已经了解了协程如何让一个方法“暂停”下来,并且让它yield直到某些值到达我们给定的数值;并且利用它,我们还创建了一个很棒的计时器系统。协程一个很重要的内容是,它可以让普通的程序(比方说一个计时器)很容易地被抽象化并且被复用。
协程的参数
抽象化一个协程的第一个方法是给它传递参数,协程作为一个函数方法来说,它自然能够传递参数。这里有一个协程的例子,它在特定的地方输出了特定的信息。
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
Using 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()程序,不需要它做任何事,只需要在运行的时候等待一段时间就结束。
[C#]
纯文本查看
复制代码
1
2
3
4
5
|
IEnumerator Wait(
float
duration)
{
for
(
float
timer = 0; timer < duration; timer += Time.deltaTime)
Yield
return
0;
}
|
接下来我们要编写另一个协程,如下:
[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
25
26
27
|
Using 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变量赋值,程序运行的时候,该对象就会在协程的作用下,以我们给定的速度运动到给定的位置。
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
using
UnityEngine;
Using System.Collections;
Public
class
MoveExample : MonoBehaviour
{
Public Vector3 targetPosition;
Public
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() ,我们可以让它在这些点之间持续运动。
[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
25
26
27
28
29
30
31
32
|
Using UnityEngine;
Using System.Collections;
Public
class
MoveExample : MonoBehaviour
{
Public Vector3[] path;
Public
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中没有简便的方法来检测作用于对象的协程数量以及具体是哪些协程作用在对象上。
如果您发现教程中存在问题和错误的信息,或者有任何建议又或者您想要在这里看到其他需要的教程,可以发
邮件或者在评论中留言。
作者ChevyRay ,2013年9月28日,snaker7译 原文地址:http://unitypatterns.com/scripting-with-coroutines/