在unity中,协程(Coroutines)的形式是我最喜欢的功能之一,几乎在所有的项目中,我都会使用它来控制运动,序列,以及对象的行为。在这个教程中,我将会说明协程是如何工作的,并且会附上一些例子来介绍它的用法。 协程介绍 Unity的协程系统是基于C#的一个简单而强大的接口 ,IEnumerator,它允许你为自己的集合类型编写枚举器。这一点你不必关注太多,我们直接进入一个简单的例子来看看协程到底能干什么。首先,我们来看一下这段简单的代码... 倒计时器 这是一个简单的脚本组件,只做了倒计时,并且在到达0的时候log一个信息。
- 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!");
- }
- }
复制代码
还不错,代码简短实用,但问题是,如果我们需要复杂的脚本组件(像一个角色或者敌人的类),拥有多个计时器呢?刚开始的时候,我们的代码也许会是这样的:
- 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循环来做这些,看上去是否会好很多?
- for(float timer = 3; timer >= 0; timer -= Time.deltaTime)
- {
- //Just do nothing...
- }
- Debug.Log("This happens after 5 seconds!");
复制代码
现在每一个计时器变量都成为for循环的一部分了,这看上去好多了,而且我不需要去单独设置每一个跌倒变量。 好的,你可能现在明白我的意思:协程可以做的正是这一点! 码入你的协程! 现在,这里提供了上面例子运用协程的版本!我建议你从这里开始跟着我来写一个简单的脚本组件,这样你可以在你自己的程序中看到它是如何工作的。
- 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!");
- }
- }
复制代码
这看上去有点不一样,没关系,接下来我会解释这里到底发生了什么。
- 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是“停止执行方法,并且在下一帧从这里重新开始”,这意味着你可以这样做:
- //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()循环等同。。。
- //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
- }
复制代码
开始和终止协程 之前,我们已经学过了通过 StartCoroutine()方法来开始一个协程,就像这样:
- StartCoroutine(Countdown());
复制代码
如果我们想要终止所有的协程,可以通过StopAllCoroutines()方法来实现,它的所要做的就跟它的名字所表达的一样。注意,这只会终止在调用该方法的对象中(应该是指调用这个方法的类吧)开始的协程,对于其他的MonoBehavior类中运行的协程不起作用。 如果我们有以下这样两条协程语句:
- StartCoroutine(FirstTimer());
- StartCoroutine(SecondTimer());
复制代码
那我们怎么终止其中的一个协程呢?在这个例子里,这是不可能的,如果你想要终止某一个特定的协程,那么你必须得在开始协程的时候将它的方法名作为字符串,就像这样:
- //If you start a Coroutine by name...
- StartCoroutine("FirstTimer");
- StartCoroutine("SecondTimer");
-
- //You can stop it anytime by name!
- StopCoroutine("FirstTimer");
|