zoukankan      html  css  js  c++  java
  • Unity 3D中不得不说的yield协程与消息传递

      1. 协程

      在Unity 3D中,我们刚开始写脚本的时候肯定会遇到类似下面这样的需求:每隔3秒发射一个烟花、怪物死亡后20秒再复活之类的。刚开始的时候喜欢把这些东西都塞到Update里面去,就像下面这样写。

     1 float nowTime = 3.0f;
     2 bool isDead = true;
     3 float deadTime = 20.0f;
     4     
     5 void startFireworks()
     6 {
     7     // 放烟花
     8 }
     9 
    10 void revival()
    11 {
    12     // 复活
    13 }
    14 
    15 void Update () 
    16 {
    17     if (nowTime <= 0)
    18     {
    19         startFireworks();
    20         nowTime = Random.Range(2.5f, 3.5f);
    21     }
    22     nowTime -= Time.deltaTime;
    23     if (isDead)
    24     {
    25         if (deadTime <= 0)
    26         {
    27             revival();
    28             isDead = false;
    29             deadTime = 30.0f;
    30         }
    31         deadTime -= Time.deltaTime;
    32     }
    33 }

      当这样的需求多起来时,Update中凌乱不堪,如果有需求需要添加或者修改,将显得非常麻烦。尤其是类似死亡后复活这种需求,只是在死亡后等待30秒重新复活,其他时间根本不需要去执行,这样放在Update里面还需要每一帧去判断,显得很累赘。

    好在Unity 3D支持yield协程,不懂没关系,先看看下面用协程实现上面的功能。

     1 void Start()
     2 {
     3     StartCoroutine(Fireworks());
     4 }
     5     
     6 void deadHandle()
     7 {
     8     StartCoroutine(Revival());
     9 }
    10 
    11 IEnumerator Fireworks()
    12 {
    13     while (true)
    14     {
    15         startFireworks();
    16         yield return new WaitForSeconds(Random.Range(2.5f, 3.5f));
    17     }
    18 }
    19 
    20 IEnumerator Revival()
    21 {
    22     yield return new WaitForSeconds(30.0f);
    23     revival();
    24 }

      上面代码中,以IEnumerator作为返回值的函数就是协程,调用StartCoroutine()开始协程,在Start函数中调用StartCoroutine(Fireworks());,说明在开始时就开始执行协程Fireworks(),在deadHandle()中调用StartCoroutine(Revival());说明是在怪物死亡时开始执行协程。

      现在再来看看协程Fireworks()和Revival()中带有yield return的语句,yield return new WaitForSeconds(30.0f);表示现在从return这个语句处中断执行,在30秒后继续执行后面的代码。

      如果想在下一帧继续执行,就应该这样写 yield return null,这样语句就会在return这里中断,等待下一帧继续执行后面的代码。就像在Fireworks()里面写的,return之后,继续进行while判断,为true则继续循环,遇到yield return中断执行,等待,反复这样运行,就像Update一样。当然while中的判断条件可以自己指定,在需要中断的时候,在外面将while中的判断条件置为false即可。

     1 bool isContinue = true;
     2 void stopFireworks()
     3 {
     4     isContinue = false;
     5 }
     6 IEnumerator Fireworks()
     7 {
     8     while (isContinue)
     9     {
    10         startFireworks();
    11         yield return new WaitForSeconds(Random.Range(2.5f, 3.5f));
    12     }
    13 }

      当然也可以通过有StopCoroutine来中止协程的执行,不过这个函数是有条件的,具体可以去查阅unity文档或者网上搜索一下,有很多资料,这里只是告诉大家有这么个东西可以用。

      有了协程,写起脚本来真是方便了很多。协程和Update一样,也是系统在每帧会去检测调用,因此在协程中也是可以使用Time.deltaTime的。关于协程与Update之类的执行顺序,没有测试过,网上也有一些资料,大家可以参考,不同的Unity 3D版本具体的实现可能有出入,如果某些功能确实需要知道执行顺序,那么到时候可以亲测一下。

      需要注意的一点是:WaitForSeconds是受到Time.timeScale影响的,如果将其置为0,那么协程就无法执行下去了。不过yield return null不会受到影响,因为每帧会执行,只是Time.deltaTime为0了。

      2. 消息传递

      在游戏开发中,消息传递必不可少。通常有三种方式:保存别的对象的引用、Unity自带的SendMessage和C#中的事件。

      例如一个暂停,我需要通知玩家,暂停了,不要响应键盘鼠标操作了;通知UI,显示一个暂停面板;通过所有怪物,不要动了,暂停了,休息一下。 

      第一种方式是刚开始写脚本时常用的,保存所有对象的引用,这是很麻烦的事情,我需要获取玩家、UI和所有的怪物对象,然后调用其相应的暂停函数,这在程序规模变大之后,添加、修改和删除是一个很大的工作量。而且很多对象之间相互引用,耦合对也很高,用起来比较麻烦。

      第二种方式是Unity 3D提供的Messages消息机制,不过网上说这种方法有很大的缺陷,而且只能通知一个父子关系的对象,不同对象之间的消息无法传递。没有用过这个机制,所以也不是很清楚是不是像上面说的那样。

      第三种方式是C#中的委托和事件,这个方法对于消息传递来说非常好用,从设计模式的角度上来说,就是一个典型的观察者模式。如果你用过EasyTouch摇杆,那你就应该知道在OnEnable()中使用EasyJoystick.On_JoystickMove += OnJoystickMove;注册自己的Move函数,在这里就是OnJoystickMove。其实EasyTouch这个就用到了C#事件,使用+=添加自己的响应函数,当发生摇杆移动时,就会调用你自己指定的OnJoystickMove函数。具体可以参考下面给出的参考资料的链接。

      今天就写到这里,这些都是简介性质的,详细资料网上都有很多,我这些只是告诉初学者Unity 3D中有这些东西,很可能是你需要的,可以少走一些弯路。

      关于协程和C#事件,是Unity 3D中强力推荐的两个机制,它们真的非常重要,一定要善用,大家可以体会一下。

      参考资料1:【吐血推荐】简要分析unity3d中剪不断理还乱的yield

      参考资料2:C# 事件和Unity3D

  • 相关阅读:
    Database
    模式匹配
    About Unicode
    Dive into python学习笔记
    Python基本概念及零碎知识点
    Djnago的一些零碎知识点
    python项目练习3:万能的XML
    Django的第一个web程序及深入学习
    Poj1195&tyvj1474二维线段树
    zoj2319Beautiful People Dp
  • 原文地址:https://www.cnblogs.com/qinruijie/p/3916189.html
Copyright © 2011-2022 走看看