Unity 协程(Coroutine)

转自http://gypsumlabs.github.io/2015/12/08/unity-tips-coroutine/ 

协程?

  当然我早就听说了协程这个东西,可能你也听过。但是我和之前学C#学delegate时一样,一开始没有很好的理解这个东西,文章看了一些,还是Get不到它正确的应用场景,而一直不愿意去用。其实很多时候就是要去放开了试才能真的理解这些技巧的意义,其瓶颈不过就是一层纸,这次终于把这层纸捅破罢了。

  协程(Coroutine)不同于线程(Thread),他目标解决的不是线程阻塞一类的问题。以我个人的理解,他主要解决有某些必须在游戏循环中运行的代码,而你又不想把他写到Update()中的情况。

  游戏与一般应用程序的区别在于,一般应用程序是事件驱动的,而游戏则拥有游戏循环这一特性,在Unity中体现为MonoBehaviour的Update()方法。游戏中自然也会有”事件”,基本都可以写在Update()中,但是由于游戏循环的存在,Update()会风雨无阻的在每一帧调用,为了让代码适时的执行,一般都会设置几个bool或int/float来控制Update()中的代码。

  这时你会发现,如果你的Update()中有太多这样的代码时,代码的可读性会越来越糟。

解决问题

  来看一个比较典型的情况:一个事件会在游戏过程中被触发,触发后执行一些动作A,等待5秒后,执行一些动作B,事件结束

  我原来会这么写:

public class SimpleEvent : MonoBehaviour
{
    private bool event_Start;
    private float event_Timer;
    private bool A_Done;
    private bool B_Done;

    //打开事件开关,同时重置该事件的计时器和A,B动作的标记
    public void StartEvent()
    {
        event_Start = true;
        event_Timer = 0;
        A_Done = false;
        B_Done = false;
    }

    void Update()
    {
        if(event_Start)
        {
            if(!A_Done)
            {
                DoSomething_A();
                A_Done = true;
            }

            if(event_Timer<5)
            {
                event_Timer += Time.deltaTime;
            }
            else
            {
                if(!B_Done)
                {
                    DoSomething_B();
                    B_Done = true;
                }

                //关掉事件开关,结束事件
                event_Start = false;
            }
        }
    }
}

     需要触发事件时,我只要调用一下这个脚本的StartEvent()即可,看起来也没有什么问题。

  可是如果一个Update()中有很多个这样的事件,又或是一个事件中有很多等待用的计时器,而每多一个计时器,你的事件逻辑就又多了一个if( )else( )嵌套。如此一来,Update()会越来越难以阅读。

  所以你知道的,协程该出现了:

public class SimpleEvent : MonoBehaviour
{
    IEnumerator Event()
    {
        DoSomething_A();

        yield return new WaitForSeconds(5);

        DoSomething_B();
    }
}

触发这个事件我只要:

StartCoroutine(Event());

  IEnumerator是C#的一种迭代器,协程实现的具体原理可以找到很多文章分析。但是就算没有彻底理解其原理也没有关系,只要会应用就可以。总之,协程以 IEnumerator 协程名(形参){ } 申明,和一般的方法申明类似。

  协程中必须要有 yield return 值 这样的返回值,例子中的 yield return new WaitForSeconds(5) 就表示等待5秒钟

  一句代码就能完成等待功能,且不需要计时器变量和if()else()判断!这已经很神奇了,当然协程不止于此,他还有另外一个重要的特性:

yield return null;

  这个表示等待下一帧。可能一下子无法理解这个等待的意义,回头再看一下我对协程的理解:他可以解决有某些必须在游戏循环中运行的代码,而你又不想把他写到 Update() 中的情况。

  所以说我认为协程应该可以替代Update(),让代码不写在Update()中又能跑在游戏循环中的能力。如何实现?请看:

IEnumerator CoroutineUpdate()
{
    while(true)
    {
        //TODO:你的游戏更新

        yield return null;
    }
}

  while(true){ }是一个无限循环,在循环结尾处的yield return null;则意味着这个无限循环的代码,每执行一次则会等到下一帧再继续循环,这不就是一个Update()嘛!

  他有一个相比Update()额外的好处,就是你可以在想结束循环时,break即可。或者直接使用for进行有限次的循环,很灵活。

其他值得一提的事

启动一个协程的方法为:

  • StartCoroutine( 协程() , 参数 ) ;
  • StartCoroutine( “协程名” , 参数 ) ; //以字符串为参数

停止协程的方法:

  • StopAllCoroutine(); 停止所有协程,使用上面第一个重载运行的协程只能以这种方式停止。

  • StopCoroutine(“协程名”); 停止单个协程只能用这个,且只能停止同样以字符串为参数启动的协程。

  另外,协程中yield return可以使用其他协程做为返回值,比如yield return OtherCoroutine(); 作用就是等待OtherCoroutine()的代码全部运行完毕后继续运行,这个特性也是极其好用的,可以实现很多不限时等待的功能。

以上

  协程是一种更加灵活的Update()替代品,避免过多的游戏逻辑堆砌在一个Update()中导致最终的可读性下降。同时还提供了yield return new WaitForSeconds(float)这样方便的等待功能,不需要在外部申明一堆计时器变量然后在逻辑中使用if()else()判断时间这样繁琐的流程。
  另外,协程也存在一些问题,就是启用多个协程之后,务必注意代码执行顺序的问题,这个问题在以前可以使用Update()和LateUpdate()缓解,但是在协程里就不太好解决,所以尽量避免多个协程之间的耦合,在一个协程中完成一个独立的功能是一个良好的思路。


转自https://blog.csdn.net/huang9012/article/details/29595747#comments_13359762

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

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

协程和Update()一样更新,可以使用Time.deltaTime,而且这个Time.deltaTime和在Update()当中使用是一样的效果(使用yield return null的情况下)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值