• Unity3D之通过C#使用Advanced CSharp Messenger


    Advanced CSharp Messenger 属于C#事件的一种。 维基百科中由详细的说明http://wiki.unity3d.com/index.php?title=Advanced_CSharp_Messenger

    Advanced CSharp Messenger的特点可以将游戏对象做为参数发送。到底Advanced CSharp Messenger有什么用呢?先创建一个立方体对象,然后把Script脚本绑定在这个对象中。脚本中有一个方法叫DoSomething()。写一段简单的代码,通常我们在调用方法的时候需要这样来写。

     1 private Script script;
     2 void Awake()
     3 {
     4     GameObject cube = GameObject.Find("Cube");
     5     script = cube.GetComponent<Script>();
     6 }
     7  
     8 void Update()
     9 {
    10     if(Input.GetMouseButtonDown(0))
    11     {
    12         script.DoSomething();
    13     }
    14 }

    代码比较简单,我就不注释了。 原理就是先获取游戏对象,接着获取脚本组件对象,最后通过脚本组件对象去调用对应脚本中的方法,这样的调用方法我们称之为直接调用。

    这个例子中我只调用了一个对象的方法,如果说有成千上万个对象,那么这样调用是不是感觉自己的代码非常的丑?因为你需要一个一个的获取对象然后获取脚本组件然后在调用方法。。。。。 (想想都恐怖!!)

    下面我们在用Advanced CSharp Messenger来实现事件的调用。按照维基百科中首先把Message.cs 和Callback.cs拷贝在你的工程中。

    CallBack.cs

    1 public delegate void Callback();
    2 public delegate void Callback<T>(T arg1);
    3 public delegate void Callback<T, U>(T arg1, U arg2);
    4 public delegate void Callback<T, U, V>(T arg1, U arg2, V arg3);

     Message.cs

      1 /*
      2  * Advanced C# messenger by Ilya Suzdalnitski. V1.0
      3  * 
      4  * Based on Rod Hyde's "CSharpMessenger" and Magnus Wolffelt's "CSharpMessenger Extended".
      5  * 
      6  * Features:
      7      * Prevents a MissingReferenceException because of a reference to a destroyed message handler.
      8      * Option to log all messages
      9      * Extensive error detection, preventing silent bugs
     10  * 
     11  * Usage examples:
     12      1. Messenger.AddListener<GameObject>("prop collected", PropCollected);
     13         Messenger.Broadcast<GameObject>("prop collected", prop);
     14      2. Messenger.AddListener<float>("speed changed", SpeedChanged);
     15         Messenger.Broadcast<float>("speed changed", 0.5f);
     16  * 
     17  * Messenger cleans up its evenTable automatically upon loading of a new level.
     18  * 
     19  * Don't forget that the messages that should survive the cleanup, should be marked with Messenger.MarkAsPermanent(string)
     20  * 
     21  */
     22  
     23 //#define LOG_ALL_MESSAGES
     24 //#define LOG_ADD_LISTENER
     25 //#define LOG_BROADCAST_MESSAGE
     26 #define REQUIRE_LISTENER
     27  
     28 using System;
     29 using System.Collections.Generic;
     30 using UnityEngine;
     31  
     32 static internal class Messenger {
     33     #region Internal variables
     34  
     35     //Disable the unused variable warning
     36 #pragma warning disable 0414
     37     //Ensures that the MessengerHelper will be created automatically upon start of the game.
     38     static private MessengerHelper messengerHelper = ( new GameObject("MessengerHelper") ).AddComponent< MessengerHelper >();
     39 #pragma warning restore 0414
     40  
     41     static public Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();
     42  
     43     //Message handlers that should never be removed, regardless of calling Cleanup
     44     static public List< string > permanentMessages = new List< string > ();
     45     #endregion
     46     #region Helper methods
     47     //Marks a certain message as permanent.
     48     static public void MarkAsPermanent(string eventType) {
     49 #if LOG_ALL_MESSAGES
     50         Debug.Log("Messenger MarkAsPermanent 	"" + eventType + """);
     51 #endif
     52  
     53         permanentMessages.Add( eventType );
     54     }
     55  
     56     static public void Cleanup()
     57     {
     58 #if LOG_ALL_MESSAGES
     59         Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed.");
     60 #endif
     61  
     62         List< string > messagesToRemove = new List<string>();
     63  
     64         foreach (KeyValuePair<string, Delegate> pair in eventTable) {
     65             bool wasFound = false;
     66  
     67             foreach (string message in permanentMessages) {
     68                 if (pair.Key == message) {
     69                     wasFound = true;
     70                     break;
     71                 }
     72             }
     73  
     74             if (!wasFound)
     75                 messagesToRemove.Add( pair.Key );
     76         }
     77  
     78         foreach (string message in messagesToRemove) {
     79             eventTable.Remove( message );
     80         }
     81     }
     82  
     83     static public void PrintEventTable()
     84     {
     85         Debug.Log("			=== MESSENGER PrintEventTable ===");
     86  
     87         foreach (KeyValuePair<string, Delegate> pair in eventTable) {
     88             Debug.Log("			" + pair.Key + "		" + pair.Value);
     89         }
     90  
     91         Debug.Log("
    ");
     92     }
     93     #endregion
     94  
     95     #region Message logging and exception throwing
     96     static public void OnListenerAdding(string eventType, Delegate listenerBeingAdded) {
     97 #if LOG_ALL_MESSAGES || LOG_ADD_LISTENER
     98         Debug.Log("MESSENGER OnListenerAdding 	"" + eventType + ""	{" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}");
     99 #endif
    100  
    101         if (!eventTable.ContainsKey(eventType)) {
    102             eventTable.Add(eventType, null );
    103         }
    104  
    105         Delegate d = eventTable[eventType];
    106         if (d != null && d.GetType() != listenerBeingAdded.GetType()) {
    107             throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));
    108         }
    109     }
    110  
    111     static public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved) {
    112 #if LOG_ALL_MESSAGES
    113         Debug.Log("MESSENGER OnListenerRemoving 	"" + eventType + ""	{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}");
    114 #endif
    115  
    116         if (eventTable.ContainsKey(eventType)) {
    117             Delegate d = eventTable[eventType];
    118  
    119             if (d == null) {
    120                 throw new ListenerException(string.Format("Attempting to remove listener with for event type "{0}" but current listener is null.", eventType));
    121             } else if (d.GetType() != listenerBeingRemoved.GetType()) {
    122                 throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
    123             }
    124         } else {
    125             throw new ListenerException(string.Format("Attempting to remove listener for type "{0}" but Messenger doesn't know about this event type.", eventType));
    126         }
    127     }
    128  
    129     static public void OnListenerRemoved(string eventType) {
    130         if (eventTable[eventType] == null) {
    131             eventTable.Remove(eventType);
    132         }
    133     }
    134  
    135     static public void OnBroadcasting(string eventType) {
    136 #if REQUIRE_LISTENER
    137         if (!eventTable.ContainsKey(eventType)) {
    138             throw new BroadcastException(string.Format("Broadcasting message "{0}" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", eventType));
    139         }
    140 #endif
    141     }
    142  
    143     static public BroadcastException CreateBroadcastSignatureException(string eventType) {
    144         return new BroadcastException(string.Format("Broadcasting message "{0}" but listeners have a different signature than the broadcaster.", eventType));
    145     }
    146  
    147     public class BroadcastException : Exception {
    148         public BroadcastException(string msg)
    149             : base(msg) {
    150         }
    151     }
    152  
    153     public class ListenerException : Exception {
    154         public ListenerException(string msg)
    155             : base(msg) {
    156         }
    157     }
    158     #endregion
    159  
    160     #region AddListener
    161     //No parameters
    162     static public void AddListener(string eventType, Callback handler) {
    163         OnListenerAdding(eventType, handler);
    164         eventTable[eventType] = (Callback)eventTable[eventType] + handler;
    165     }
    166  
    167     //Single parameter
    168     static public void AddListener<T>(string eventType, Callback<T> handler) {
    169         OnListenerAdding(eventType, handler);
    170         eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler;
    171     }
    172  
    173     //Two parameters
    174     static public void AddListener<T, U>(string eventType, Callback<T, U> handler) {
    175         OnListenerAdding(eventType, handler);
    176         eventTable[eventType] = (Callback<T, U>)eventTable[eventType] + handler;
    177     }
    178  
    179     //Three parameters
    180     static public void AddListener<T, U, V>(string eventType, Callback<T, U, V> handler) {
    181         OnListenerAdding(eventType, handler);
    182         eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] + handler;
    183     }
    184     #endregion
    185  
    186     #region RemoveListener
    187     //No parameters
    188     static public void RemoveListener(string eventType, Callback handler) {
    189         OnListenerRemoving(eventType, handler);   
    190         eventTable[eventType] = (Callback)eventTable[eventType] - handler;
    191         OnListenerRemoved(eventType);
    192     }
    193  
    194     //Single parameter
    195     static public void RemoveListener<T>(string eventType, Callback<T> handler) {
    196         OnListenerRemoving(eventType, handler);
    197         eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler;
    198         OnListenerRemoved(eventType);
    199     }
    200  
    201     //Two parameters
    202     static public void RemoveListener<T, U>(string eventType, Callback<T, U> handler) {
    203         OnListenerRemoving(eventType, handler);
    204         eventTable[eventType] = (Callback<T, U>)eventTable[eventType] - handler;
    205         OnListenerRemoved(eventType);
    206     }
    207  
    208     //Three parameters
    209     static public void RemoveListener<T, U, V>(string eventType, Callback<T, U, V> handler) {
    210         OnListenerRemoving(eventType, handler);
    211         eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] - handler;
    212         OnListenerRemoved(eventType);
    213     }
    214     #endregion
    215  
    216     #region Broadcast
    217     //No parameters
    218     static public void Broadcast(string eventType) {
    219 #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
    220         Debug.Log("MESSENGER	" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "			Invoking 	"" + eventType + """);
    221 #endif
    222         OnBroadcasting(eventType);
    223  
    224         Delegate d;
    225         if (eventTable.TryGetValue(eventType, out d)) {
    226             Callback callback = d as Callback;
    227  
    228             if (callback != null) {
    229                 callback();
    230             } else {
    231                 throw CreateBroadcastSignatureException(eventType);
    232             }
    233         }
    234     }
    235  
    236     //Single parameter
    237     static public void Broadcast<T>(string eventType, T arg1) {
    238 #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
    239         Debug.Log("MESSENGER	" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "			Invoking 	"" + eventType + """);
    240 #endif
    241         OnBroadcasting(eventType);
    242  
    243         Delegate d;
    244         if (eventTable.TryGetValue(eventType, out d)) {
    245             Callback<T> callback = d as Callback<T>;
    246  
    247             if (callback != null) {
    248                 callback(arg1);
    249             } else {
    250                 throw CreateBroadcastSignatureException(eventType);
    251             }
    252         }
    253     }
    254  
    255     //Two parameters
    256     static public void Broadcast<T, U>(string eventType, T arg1, U arg2) {
    257 #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
    258         Debug.Log("MESSENGER	" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "			Invoking 	"" + eventType + """);
    259 #endif
    260         OnBroadcasting(eventType);
    261  
    262         Delegate d;
    263         if (eventTable.TryGetValue(eventType, out d)) {
    264             Callback<T, U> callback = d as Callback<T, U>;
    265  
    266             if (callback != null) {
    267                 callback(arg1, arg2);
    268             } else {
    269                 throw CreateBroadcastSignatureException(eventType);
    270             }
    271         }
    272     }
    273  
    274     //Three parameters
    275     static public void Broadcast<T, U, V>(string eventType, T arg1, U arg2, V arg3) {
    276 #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
    277         Debug.Log("MESSENGER	" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "			Invoking 	"" + eventType + """);
    278 #endif
    279         OnBroadcasting(eventType);
    280  
    281         Delegate d;
    282         if (eventTable.TryGetValue(eventType, out d)) {
    283             Callback<T, U, V> callback = d as Callback<T, U, V>;
    284  
    285             if (callback != null) {
    286                 callback(arg1, arg2, arg3);
    287             } else {
    288                 throw CreateBroadcastSignatureException(eventType);
    289             }
    290         }
    291     }
    292     #endregion
    293 }
    294  
    295 //This manager will ensure that the messenger's eventTable will be cleaned up upon loading of a new level.
    296 public sealed class MessengerHelper : MonoBehaviour {
    297     void Awake ()
    298     {
    299         DontDestroyOnLoad(gameObject);    
    300     }
    301  
    302     //Clean up eventTable every time a new level loads.
    303     public void OnDisable() {
    304         Messenger.Cleanup();
    305     }
    306 }

    然后就可以开始使用了,Messager.Broadcast()这样就好比我们发送了一条广播。

    1     void Update()
    2     {
    3         if(Input.GetMouseButtonDown(0))
    4         {
    5             Messenger.Broadcast("Send");
    6         }
    7     }

    在需要这条广播的类中来接受它,同样是刚刚说的Script类。接受广播的标志是 Messager.AddListener()参数1表示广播的名称,参数2表示广播所调用的方法。

     1 using UnityEngine;
     2 using System.Collections;
     3  
     4 public class Script : MonoBehaviour {
     5  
     6     void Awake()
     7     {
     8         Messenger.AddListener( "Send", DoSomething );
     9     }
    10     public void DoSomething()
    11     {
    12         Debug.Log("DoSomething");
    13     }
    14 }

    这样一来,只要发送名称为”Send”的方法,就可以在别的类中接收它了。

    我们在说说如何通过广播来传递参数,这也是那天那个哥们主要问我的问题。(其实是维基百科上写的不是特别特别的清楚,那哥们误解了)在Callback中可以看出参数最多可以是三个,参数的类型是任意类型,也就是说我们不仅能传递 int float bool 还能传递gameObject类型。

    如下所示,发送广播的时候传递了两个参数,参数1是一个游戏对象,参数2是一个int数值。

    1     void Update()
    2     {
    3         if(Input.GetMouseButtonDown(0))
    4         {
    5             GameObject cube = GameObject.Find("Cube");
    6             Messenger.Broadcast<GameObject,int>("Send",cube,1980);
    7         }
    8     }

    然后是接受的地方 参数用<>存在一起。游戏对象也可以完美的传递。

     1 using UnityEngine;
     2 using System.Collections;
     3  
     4 public class Script : MonoBehaviour {
     5  
     6     void Awake()
     7     {
     8         Messenger.AddListener<GameObject,int>( "Send", DoSomething );
     9     }
    10     public void DoSomething(GameObject obj,int i)
    11     {
    12         Debug.Log("name " + obj.name + " id =" + i);
    13     }
    14 }

    如果传递一个参数<T>

    两个参数<T,T>

    三个参数<T,T,T>   

    怎么样使用起来还是挺简单的吧?

    我觉得项目中最好不要大量的使用代理事件这类的方法(根据需求而定),虽然可以让你的代码非常的简洁,但是它的效率不高大概比直接调用慢5-倍左右吧,就好比美好的东西一定都有瑕疵一样。 还记得Unity自身也提供了一种发送消息的方法吗?,用过的都知道效率也非常低下,虽然我们看不到它具体实现的源码是如何实现的,但是我觉得原理可能也是这样的。 欢迎和大家一起讨论与学习。

  • 相关阅读:
    分布式事务解决方案
    数据库和缓存双写一致性解析
    RabbitMQ 分区脑裂处理策略
    RabbitMQ实现延迟队列
    RabbitMQ高可用原理
    PyTorch Lightning工具学习
    【数学知识拾贝】模式识别所需要的线性代数知识总结
    【深度强化学习】1. 基础部分
    给内容打标签
    前端性能优化有哪些点
  • 原文地址:https://www.cnblogs.com/ring1992/p/5993707.html
Copyright © 2020-2023  润新知