Unity中的模式——协同程序介绍

来源:互联网 发布:数据如何做韦恩图 编辑:程序博客网 时间:2024/05/29 16:02




原文:Introduction to Coroutines

Unity的系统程序系统的能力由C#的IEnumerator提供,IEnumerator是一个简单但是强大的接口,这个接口允许你写自己的可数集合类型。但是你不必在意这个,让我们直接跳到一个简单的例子,这个例子展示了协同程序可以做的事情。首先,让我们看一个简单的一小块代码:

The Countdown Timer
这里有一个简单的组件,组件仅仅减少它的timer字段,输出一个消息——timer到达了0。
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!");    }}

不是太坏,非常少的代码达到了它的目标。但是有一个问题,如果我们有更多的组件(像一个Player或者Enemy类)它们有多个计时器,怎么办?我们的代码看起来就像这样:
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!");
这看起来很整齐,因为现在每一个计时的变量仅仅作为循环体的一部分,我不需要重复设置这些变量了。
好的,你可能了解了我大概的想法:协同程序可以将这件事做的很漂亮!

进入协同程序
现在,这里有一个和上边完全相同的例子,但是使用了一个协同程序!我建议你写一个简单的组件并且从现在开始跟着我的步骤,你自己再看看发生的事情。
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传入一个引用,而是调用这个函数自身(有效的传递一个倒计时的值)

Yielding
这个Countdown函数能很好的自行解释他自己,除了两个部分:
*IEnumerator返回值
*for循环中的yield返回值
为了在多个帧中运行这个方法(这里指的是3秒内的帧),Unity通过某些方式已经存储了这个方法的状态。它使用IEnumerator类型代表yield  return返回调用的值。当你yield一个方法的时候,就会说:“现在停止这个方法,在下一帧重新从这里开始执行!”

注意:yield 0或者null会告诉协同程序进入等待,直到下一帧继续之前。但是你也可以yield其他的协同程序,这个我会在一个课程中讲解。

一些例子
协同程序在一开始是相当让人困惑的,我看到新的和有经验的程序员在协同语法上都瞪大了他们的眼镜。所以,我会尽可能的通过例子来说明,这里有几个简单的协同程序的例子:

在一段时间里说“Hello”
记住,yield return 说:“停止这个函数,下一帧在继续执行”,这意味着可以做这些:
//This will say hello 5 times, once each frame for 5 framesIEnumerator 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 (int i = 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 destroyedIEnumerator 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()里,你可以协同程序里做一些花哨的事情,像这样:
IEnumerator CountSeconds(){    int seconds = 0;     while (true)    {        for (float timer = 0; timer < 1; timer += Time.deltaTime)            yield return 0;         seconds++;        Debug.Log(seconds + " seconds have passed since the Coroutine started.");    }}
这个函数的让协同程序亮了:这个函数的状态被存了起来,所以在函数体中任何定义的变量都会保持他们的值,甚至在两个帧之间。还记得在教程一开始的令人讨厌的计时变量吗?使用协同函数,我们不再需要他们,我们只需要在函数体中放置这些变量!

开始&&停止&&协同程序
之前,我们学习了通过调用StartCoroutine()方法启动一个协同程序,像这样:
StartCoroutine(Countdown());
如果我们想要停止所有的协同程序,我们可以使用StopAllCoroutines()函数,它做的就像它的名字中承诺的一样。注意它只会停止这些协同程序——被该物体启动的协同程序,而不是其他MonoBehaviours运行的协同函数。
但是,如果我们有两个协同程序在运行会怎样,像这样:
StartCoroutine(FirstTimer());StartCoroutine(SecondTimer());
...我们想要停止他们中的一个,怎么办?这种情况是不行的。如果我们想要停止一个特定的协同程序,你必须使用一个string为参数的函数,像这样:
//If you start a Coroutine by name...StartCoroutine("FirstTimer");StartCoroutine("SecondTimer"); //You can stop it anytime by name!StopCoroutine("FirstTimer");

额外的链接:
  • Coroutines – Unity Script Referencee





0 0
原创粉丝点击