Unity Coroutine

本文深入解析Unity中的Coroutine机制,包括其基本概念、实现原理及常见应用场景。通过具体示例介绍了如何利用yield语句来控制协程的执行流程,并探讨了协程与Update函数的区别与联系。

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后去处理协程(检查协程的条件是否满足):

这里写图片描述

从上图的剖析就明白,协程跟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 虽然还没执行完,但也没了,因为没人再去调用了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值