[转载]Unity3d官方工程AngryBots中的通用信号传递机制

原文地址:http://www.narkii.com/club/thread-283590-1.html

  最近分析了一下Unity自带的AngryBots中的代码,发现了一个相当强大的信号传递框架,并且代码并不复杂。它之所以有用是因为此框架维护起来非常方便,就像我之前介绍的那个状态机框架的道理是一样的。这个框架仅仅只有一个脚本,你可能对此产生不屑,不过,当你看到了它的魔术般的效果时,你一定会对你之前的藐视而感到惭愧的。好了,话多无益,实践才是硬道理。
        首先,我们来思考一个问题:假如在某一时刻,我们需要做一连串动作,且执行该行为的接收者也各不相同,甚至是同一个接收者在此条件下得执行不同脚本中的某些函数,此时我们的代码该怎样写?例如:当我按下攻击键,我自身必须播放射击的动画,角色自身是此攻击信号的第一个接收者;然后角色的枪口要克隆出一个子弹,并且产生火花,还要播放子弹出膛的音频,此时枪口可以设计成第二个攻击信号的接收者,且要连续执行三个不同的行为,如果主角此时拿的是火箭筒,那么此时的接收者主角还要向后挪动一小段距离,也就是说此接收者还要多执行一个动作。通常情况下,我们会事先在枪口的某一个位置安放一个子弹出膛点,然后绑定一个音频源,再挂接相应的脚本,当我们按下开枪按键时,会获取有关的脚本,然后调用里面的相应的函数,执行该动作。可是我们会发现,这样做的话,代码会变得异常混乱,并不利于我们今后对代码的维护,所以我们得尽量想其他的办法。这就是我今天介绍这个框架的原因。
    我将AngryBots里面的那个代码给翻译成了C#,途中出了一些挫折,原因出在类的序列化,先看代码:

[转载]Unity3d官方工程AngryBots中的通用信号传递机制
using UnityEngine;
using System.Collections;

[System.Serializable]
public class ReceiverItem{    
  public GameObject receiver;//信号接收者    
  public string action = "OnSignal";//默认执行的函数名,可以在Inspector面板上面修改    
  public float delay;//执行在执行该行为之前的等待时间,默认是不必等待的    
  
  public IEnumerator SendWithDelay(MonoBehaviour sender)    {        
    yield return new WaitForSeconds(delay);//等待delay秒        
    if (receiver)//如果接收者存在            
      receiver.SendMessage(action,SendMessageOptions.DontRequireReceiver);//就执行该接收者附着的脚本中的名为action的函数        
    else            
      Debug.LogWarning("No receiver of signal "" + action + "" on object " + sender.name + " (" + sender.GetType().Name + ")", sender);    
  }
}

[System.Serializable]
public class SignalSends {    
  public ReceiverItem[] receives;
  
public void SendSignal(MonoBehaviour sender){     for (int i = 0; i < receives.Length; i++){       sender.StartCoroutine(receives.SendWithDelay(sender));//执行协同函数     }   } }
[转载]Unity3d官方工程AngryBots中的通用信号传递机制

  这就是此框架的核心,你也许会说,这么短的代码,能起多大作用?好吧,我们还是以一个例子来说明这一切吧!为了节省时间,我还是以我的上一篇帖子中的工程作为基础,修改一下里面的几个脚本。

  第一步,往工程里面添加一个Audio文件夹,并导入两个音频片(我自己找的两个音频片),如下:

      [转载]Unity3d官方工程AngryBots中的通用信号传递机制
  
第二步,修改代码及Inspector面板中的信号变量的拖拽:SpawnBullet.cs修改如下:

SpawnBulletPoint.cs的修改:

 

[转载]Unity3d官方工程AngryBots中的通用信号传递机制
using UnityEngine;
using System.Collections;

public class SpawnBulletPoint : MonoBehaviour {    

  public float radius = 2f;
  public GameObject bullet;
  
  void OnDrawGizmos(){        
    Gizmos.color = Color.green;        
    Gizmos.DrawSphere(transform.position, radius);    
   }  

  void InstantiateBullet(){        
    Instantiate(bullet, transform.position, Quaternion.identity);    
  }    

  void PlayAudio(){        
    if(audio){
      audio.Play();     }   
  } }
[转载]Unity3d官方工程AngryBots中的通用信号传递机制

 

  下面我们开始最关键的一步:

  选中MainCamera,然后在Inspector面板上将连个信号接收实例定义为一个,且里面的接收者元素有两个,如下:

    [转载]Unity3d官方工程AngryBots中的通用信号传递机制
  我们看SpawnSignal,此时该变量下元素类型为Receives的实例有两个,但都为SpawnPoint,只是Action不同,留心观察一下,这两个Action是可以在Inspector面板中临时填写的,当前填写的名字正好是SpawnBulletPoint .cs脚本中的两个新添加的函数名。这就是这个信号发射系统的关键所在。接下来我们修改BulletController.cs脚本:

[转载]Unity3d官方工程AngryBots中的通用信号传递机制
using UnityEngine;
using System.Collections;
public class BulletController : MonoBehaviour {
    public float speed = 1f;   
    public float distance = 0.1f;
    public SignalSends explosionSignal;    
    public AudioClip explosion;
    private LayerMask mask;

    // Use this for initializationvoid 
    void Start () {        
        mask = 1 << LayerMask.NameToLayer("wall1");
    }
    // Update is called once per framevoid 
    void Update () {        
        transform.Translate(transform.forward * speed * Time.deltaTime );  
        RaycastHit hit;  
        if(Physics.Raycast(transform.position,transform.forward,out hit,distance,mask))
        {
            explosionSignal.SendSignal(this);  
        }
    }
    void cuteExplosion()    
    {     
        if(explosion)     
        {         
            AudioSource.PlayClipAtPoint(explosion,transform.position);  
        }     
        Destroy(this.gameObject);   
    }
}
[转载]Unity3d官方工程AngryBots中的通用信号传递机制

 

  修改的目的是:当子弹撞击第二块挡板时播放爆炸的音频。但用的是信号发射框架,具体修改部分我还是将工程上传上去大家下载下来自行研究吧!运行之后,功能全都实现了。我们再回头看看这个信号发射框架中的后一个类的代码:

[转载]Unity3d官方工程AngryBots中的通用信号传递机制
[System.Serializable]
public class SignalSends
{
    public ReceiverItem[] receives;
    public void SendSignal(MonoBehaviour sender){        
        for (int i = 0; i < receives.Length; i++)       
        {
            sender.StartCoroutine(receives.SendWithDelay(sender));    
        } 
    }
}
[转载]Unity3d官方工程AngryBots中的通用信号传递机制

  整个流程丝毫不拖泥带水,我们理论上可以在Inspector面板中的SignalSends实例中定义无数个接收者,并执行相应的名为Action函数。我们只需调用SendSignal(this)函数,就不用在当前脚本中编写纷繁复杂的行为代码了,只需在特定的脚本中编写Action函数就行,然后再Inspector面板中将此Action函数名添加进去就行了。如此一来,我们就省去了获取GameObject实例,获取脚本实例,然后才能调用特定函数且有可能传递一些参数的这些步骤了,我们就能更方便的编写行为代码,且当行为增加时,我们只需增加SignalSends实例中的ReceiverItem元素个数并编写相应的行为函数然后将该行为函数替换进去就行了。认真体会一下吧!我想,这个短小精悍的代码应该会在某些时刻发生一些意想不到的作用的!

[转载]Unity3d官方工程AngryBots中的通用信号传递机制

上一篇:xml学习语法


下一篇:三、Kubernetes集群的命令行工具kubectl