Unity杂谈:Unity中的协程(协同程序)

写于2016-2-7,原创,转载请注明。

另:现在发现一些简单的延时动作可以通过Invoke()的方式实现,没必要全用协程。

以下是正文

…………………………………………………………………………………………………………………………………………………………

(纯干货,无图)

在游戏中,经常会遇到一些计划性的事件,这里举个例子,比如说我们的主角3秒回城后进行5秒的自爆倒计时……
刚开始用Unity的时候总喜欢把这些东西都塞到Update里面去,这样虽然可以达到想要的效果,但是代码量大了就显得紊乱,不好管理。
 

//3秒回城后进行5秒倒计时自爆的代码(Update)
using UnityEngine;
using System.Collections;
public class Test00 : MonoBehaviour {
    float homeTime = 3.0f;
    bool isHome = false;
    float boomTime = 5.0f;
    bool isBoom = false;

    void startHome(){
        // 回城
        Debug.Log("Home");
        isHome=true;
    }

    void startBoom(){
        // 自爆
        Debug.Log("Boom");
        isBoom=true;
    }


    void Update (){
        if (!isHome){
            if (homeTime <= 0){
                startHome();
            }else{
            homeTime -= Time.deltaTime;
        }   

        if (isHome&&!isBoom){
            if (boomTime <= 0){
                startBoom();
            }else{
                boomTime -= Time.deltaTime;
            }
        }
    }
}

 

 

 


若类似的需求很多的话,Update中的代码将凌乱不堪,而且如果要进行添加或者修改,将变得非常麻烦。
而使用协程功能的话,将会变得十分方便:

 

 

 

 

//3秒回城后进行5秒倒计时自爆的代码(协程)

using UnityEngine;
using System.Collections;
public class Test01 : MonoBehaviour {

void Start()
{
HomeBoomHandle();
}

void HomeBoomHandle()
{
StartCoroutine(Home());
}

IEnumerator Home()
{
yield return new WaitForSeconds(9.0f);
Debug.Log("Home");
StartCoroutine(Boom());
}


IEnumerator Boom()
{
yield return new WaitForSeconds(10.0f);
Debug.Log("Boom");
}
}

上面代码中,以IEnumerator作为返回值的函数就是协程,调用StartCoroutine()开始协程,在Start函数中调用HomeBoomHandle(),在HomeBoomHandle()内调用了StartCoroutine(Home());,从而开启3秒回城5秒自爆的计划……
可以发现,这次的代码清晰明了易懂,省去了很多不必要的麻烦。

 

 

需要注意的事情:

 

 

 

①执行顺序问题

若有协程函数B 使用yield return到上一级函数A后,函数A不是协程函数,那么协程函数B中yield return后的代码将会在函数A之后的代码执行后在执行(或等待)。

举个例子:

using UnityEngine;
using System.Collections;
public class Test02 : MonoBehaviour {

void Start () 
{
StartCoroutine(Test());
Debug.Log("333");
Destroy (this.gameObject);
}

IEnumerator Test() 
{
Debug.Log("111");
yield return new WaitForSeconds(3.0f);
Debug.Log("222");
}
}

 

 


由于Debug.Log("222");会在Start()执行完毕后再执行,而Start()中销毁了自身,所以Debug.Log("222");就不会被执行
本地图片,请重新上传 
解决的办法是把Start ()也变成协成函数:

 

 

 

using UnityEngine;
using System.Collections;
public class Test03 : MonoBehaviour {
IEnumerator Start () 
{
yield return StartCoroutine(Test());
Debug.Log("333");
Destroy(this.gameObject);
}


IEnumerator Test() 
{
Debug.Log("111");
yield return new WaitForSeconds(3.0f);
Debug.Log("222");
}
}

这样的话,只有当Test()被彻底执行完毕了,才会接着执行Start()的内容。
网上还有另外一个例子也是说明了这个问题,但是它把执行顺序问题作为一种技巧来使用了,这里摘录一下:

 

//Do等待2秒后执行后面的语句

IEnumerator Do() {
        print("Do now");
        yield return new WaitForSeconds(2);
        print("Do 2 seconds later");
    }
    void Awake() {
        Do();
        print("This is printed immediately");
}

//先执行Do等待都执行结束再执行其他的
IEnumerator Do() {
    print("Do now");
    yield return new WaitForSeconds(2);
    print("Do 2 seconds later");
}

IEnumerator Awake() {
    yield return StartCoroutine("Do");
    print("Also after 2 seconds");
    print("This is after the Do coroutine has finished execution");
}



②Time.Scale的影响

 

WaitForSeconds是受到Time.timeScale影响的,如果将其置为0,那么协程就无法执行下去了。不过yield return null(等到下一帧执行)不会受到影响,因为游戏中每帧都会执行,只是Time.deltaTime为0。

 

 

③停止协程
可以通过使用StopCoroutine()来停止某个协程的运行,以下是从官方里文档里摘的说明和例子:
function StopCoroutine (methodName : string) : void
Description描述
Stops all coroutines named methodName running on this behaviour.
停止这个动作中名为methodName的所有协同程序。
Please note that only StartCoroutine using a string method name can be stopped using StopCoroutine.
请注意只有StartCoroutine使用一个字符串方法名时才能用StopCoroutine停用之.

例子:

using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
IEnumerator Start() {
StartCoroutine("DoSomething", 2.0F);
yield return new WaitForSeconds(1);
StopCoroutine("DoSomething");
}
IEnumerator DoSomething(float someParameter) {
while (true) {
print("DoSomething Loop");
yield return null;
}
}
}

 

 


附:什么是协同程序?
协同程序(coroutine)与多线程情况下的线程比较类似:有自己的堆栈,自己的局部变量,有自己的指令指针(IP,instruction pointer),但与其它协同程序共享全局变量等很多信息。线程和协同程序的主要不同在于:在多处理器情况下,从概念上来讲多线程程序同时运行多个线程;而协同程序是通过协作来完成,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只在必要时才会被挂起。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值