Unity Coroutine

Coroutine的参数都是IEnumerator类型的。
它是个interface,有Current属性、MoveNext方法 和 Reset方法。
实际操作类似foreach,即每次 MoveNext 之后得到 Current,执行 Current,然后等下一帧被调用,如此往复,直到 MoveNext 返回 false(表示执行完毕),释放掉IEnumerator即可。

yield 语句可以暂停协同程序的执行,yield 的返回值指定在什么时候 resume 协同程序

  • 暂停协同程序,下一帧再继续往下执行
    yield return null
  • 暂停协同程序,等到下一次调用 FixedUpdate 方法时再继续往下执行
yield return new WaitForFixedUpdate();
yield return new WaitForEndOfFrame();
  • 暂停协同程序,等到 n 秒之后再继续往下执行
yield return new WaitForSeconds(n);
yield return new WaitForSecondsRealtime(n);
  • 暂停协同程序,开启其它协同程序(OtherCoroutineMethod),直到 OtherCoroutineMethod 执行完毕再继续往下执行
    yield return StartCoroutine("OtherCoroutineMethod")

  • 暂停协同程序,直到条件满足

static bool condition = true;
yield return new WaitUntil(() => condition == true);
  • 当条件满足时,暂停协同程序
static bool condition = true;
yield return new WaitWhile(()=> condition == true);

游戏中可以用得到yield的场景:

  • 游戏结算分数时,分数从0逐渐上涨,而不是直接显示最终分数
  • 人物对话时,文字一个一个很快的出现,而不是一下突然出现
  • 10、9、8……0的倒计时
  • 某些游戏(如拳皇)掉血时血条UI逐渐减少,而不是突然降低到当前血量
using UnityEngine;
using System.Collections;

public class dialog_yield : MonoBehaviour {
    public string dialogStr = "0123456789012345678901234567890123456789";
    public float speed = 5.0f;

    // Use this for initialization
    void Start () {
        StartCoroutine(ShowDialog());
    }

    // Update is called once per frame
    void Update () {
    }

    IEnumerator ShowDialog(){
        float timeSum = 0.0f;
        while(guiText.text.Length < dialogStr.Length){
            timeSum += speed * Time.deltaTime;
            guiText.text = dialogStr.Substring(0, System.Convert.ToInt32(timeSum));
            yield return null;
        }
    }
}

注意:协程不是线程,也不是异步执行的。协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的。使用协程你不用考虑同步和锁的问题。

UnityGems.com给出了协程的定义:

  A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done.

  协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码。Unity在每一帧(Frame)都会去处理对象上的协程。Unity主要是在Update后去处理协程(检查协程的条件是否满足):

Unity Coroutine_第1张图片

从上图的剖析就明白,协程跟Update()其实一样的,都是Unity每帧对会去处理的函数(如果有的话)。如果MonoBehaviour 是处于激活(active)状态的而且yield的条件满足,就会协程方法的后面代码。还可以发现:如果在一个对象的前期调用协程,协程会立即运行到第一个 yield return 语句处,如果是 yield return null ,就会在同一帧再次被唤醒。如果没有考虑这个细节就会出现一些奇怪的问题。

  • 协程和Update()一样更新,自然可以使用Time.deltaTime了,而且这个Time.deltaTime和在Update()当中使用是一样的效果(使用yield return null的情况下)
  • 协程并不是多线程,它和Update()一样是在主线程中执行的,所以不需要处理线程的同步与互斥问题
  • yield return null其实没什么神奇的,只是unity3d封装以后,这个协程在下一帧就被自动调用了
  • 可以理解为ShowDialog()构造了一个自己的Update(),因为yield return null让这个函数每帧都被调用了
void Start () {
    Debug.Log("start1");
    StartCoroutine(Test());
    Debug.Log("start2");
}

IEnumerator Test()
{
    Debug.Log("test1");
    yield return StartCoroutine(DoSomething());
    Debug.Log("test2");
}

IEnumerator DoSomething()
{
    Debug.Log("load 1");
    yield return null;
    Debug.Log("load 2");
}

执行结果:

start1
test1
load1
start2
load2
test2

在 GameObject 中 StartCoroutine,在这个 Coroutine 还未执行完毕之前 Destroy 掉这个 GameObject,可以发现 Coroutine 虽然还没执行完,但也没了,因为没人再去调用了。

你可能感兴趣的:(Unity,unity,coroutine,yield)