• Unity消息简易框架 Advanced C# messenger


    Unity消息简易框架 Advanced C# messenger

    【转载 雨凇MOMO博客】 https://www.xuanyusong.com/archives/2165

    Tips

    在寻找Unity下好用的消息事件框架的时候无意中发现该框架,十分简易,但又用起来特别方便。公司VR游戏中试了下,真滴方便。也不造轮子了,直接转载雨凇大佬的博客,就当是Mark一下,方便以后学习。

    正文

    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. GameObject cube = GameObject.Find("Cube"); 
    4. script = cube.GetComponent<Script>(); 

    5.  
    6. void Update() 

    7. if(Input.GetMouseButtonDown(0)) 

    8. script.DoSomething(); 


    9.  

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

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

    下面我们在用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. static public void Cleanup() 

    56. #if LOG_ALL_MESSAGES 
    57. Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed."); 
    58. #endif 
    59.  
    60. List< string > messagesToRemove = new List<string>(); 
    61.  
    62. foreach (KeyValuePair<string, Delegate> pair in eventTable) { 
    63. bool wasFound = false
    64.  
    65. foreach (string message in permanentMessages) { 
    66. if (pair.Key == message) { 
    67. wasFound = true
    68. break


    69.  
    70. if (!wasFound) 
    71. messagesToRemove.Add( pair.Key ); 

    72.  
    73. foreach (string message in messagesToRemove) { 
    74. eventTable.Remove( message ); 


    75.  
    76. static public void PrintEventTable() 

    77. Debug.Log(" === MESSENGER PrintEventTable ==="); 
    78.  
    79. foreach (KeyValuePair<string, Delegate> pair in eventTable) { 
    80. Debug.Log(" " + pair.Key + " " + pair.Value); 

    81.  
    82. Debug.Log(" "); 

    83. #endregion 
    84.  
    85. #region Message logging and exception throwing 
    86. static public void OnListenerAdding(string eventType, Delegate listenerBeingAdded)
    87. #if LOG_ALL_MESSAGES || LOG_ADD_LISTENER 
    88. Debug.Log("MESSENGER OnListenerAdding "" + eventType + "" {" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}"); 
    89. #endif 
    90.  
    91. if (!eventTable.ContainsKey(eventType)) { 
    92. eventTable.Add(eventType, null ); 

    93.  
    94. Delegate d = eventTable[eventType]; 
    95. if (d != null && d.GetType() != listenerBeingAdded.GetType()) { 
    96. 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)); 


    97.  
    98. static public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved)
    99. #if LOG_ALL_MESSAGES 
    100. Debug.Log("MESSENGER OnListenerRemoving "" + eventType + "" {" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}"); 
    101. #endif 
    102.  
    103. if (eventTable.ContainsKey(eventType)) { 
    104. Delegate d = eventTable[eventType]; 
    105.  
    106. if (d == null) { 
    107. throw new ListenerException(string.Format("Attempting to remove listener with for event type "{0}" but current listener is null.", eventType)); 
    108. } else if (d.GetType() != listenerBeingRemoved.GetType()) { 
    109. 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)); 

    110. } else
    111. throw new ListenerException(string.Format("Attempting to remove listener for type "{0}" but Messenger doesn't know about this event type.", eventType)); 


    112.  
    113. static public void OnListenerRemoved(string eventType)
    114. if (eventTable[eventType] == null) { 
    115. eventTable.Remove(eventType); 


    116.  
    117. static public void OnBroadcasting(string eventType)
    118. #if REQUIRE_LISTENER 
    119. if (!eventTable.ContainsKey(eventType)) { 
    120. throw new BroadcastException(string.Format("Broadcasting message "{0}" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", eventType)); 

    121. #endif 

    122.  
    123. static public BroadcastException CreateBroadcastSignatureException(string eventType)
    124. return new BroadcastException(string.Format("Broadcasting message "{0}" but listeners have a different signature than the broadcaster.", eventType)); 

    125.  
    126. public class BroadcastException : Exception
    127. public BroadcastException(string msg) 
    128. : base(msg)


    129.  
    130. public class ListenerException : Exception
    131. public ListenerException(string msg) 
    132. : base(msg)


    133. #endregion 
    134.  
    135. #region AddListener 
    136. //No parameters 
    137. static public void AddListener(string eventType, Callback handler)
    138. OnListenerAdding(eventType, handler); 
    139. eventTable[eventType] = (Callback)eventTable[eventType] + handler; 

    140.  
    141. //Single parameter 
    142. static public void AddListener<T>(string eventType, Callback<T> handler) { 
    143. OnListenerAdding(eventType, handler); 
    144. eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler; 

    145.  
    146. //Two parameters 
    147. static public void AddListener<T, U>(string eventType, Callback<T, U> handler) { 
    148. OnListenerAdding(eventType, handler); 
    149. eventTable[eventType] = (Callback<T, U>)eventTable[eventType] + handler; 

    150.  
    151. //Three parameters 
    152. static public void AddListener<T, U, V>(string eventType, Callback<T, U, V> handler) { 
    153. OnListenerAdding(eventType, handler); 
    154. eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] + handler; 

    155. #endregion 
    156.  
    157. #region RemoveListener 
    158. //No parameters 
    159. static public void RemoveListener(string eventType, Callback handler)
    160. OnListenerRemoving(eventType, handler);  
    161. eventTable[eventType] = (Callback)eventTable[eventType] - handler; 
    162. OnListenerRemoved(eventType); 

    163.  
    164. //Single parameter 
    165. static public void RemoveListener<T>(string eventType, Callback<T> handler) { 
    166. OnListenerRemoving(eventType, handler); 
    167. eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler; 
    168. OnListenerRemoved(eventType); 

    169.  
    170. //Two parameters 
    171. static public void RemoveListener<T, U>(string eventType, Callback<T, U> handler) { 
    172. OnListenerRemoving(eventType, handler); 
    173. eventTable[eventType] = (Callback<T, U>)eventTable[eventType] - handler; 
    174. OnListenerRemoved(eventType); 

    175.  
    176. //Three parameters 
    177. static public void RemoveListener<T, U, V>(string eventType, Callback<T, U, V> handler) { 
    178. OnListenerRemoving(eventType, handler); 
    179. eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] - handler; 
    180. OnListenerRemoved(eventType); 

    181. #endregion 
    182.  
    183. #region Broadcast 
    184. //No parameters 
    185. static public void Broadcast(string eventType)
    186. #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE 
    187. Debug.Log("MESSENGER " + System.DateTime.Now.ToString("hh:mm:ss.fff") + " Invoking "" + eventType + """); 
    188. #endif 
    189. OnBroadcasting(eventType); 
    190.  
    191. Delegate d; 
    192. if (eventTable.TryGetValue(eventType, out d)) { 
    193. Callback callback = d as Callback; 
    194.  
    195. if (callback != null) { 
    196. callback(); 
    197. } else
    198. throw CreateBroadcastSignatureException(eventType); 



    199.  
    200. //Single parameter 
    201. static public void Broadcast<T>(string eventType, T arg1) { 
    202. #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE 
    203. Debug.Log("MESSENGER " + System.DateTime.Now.ToString("hh:mm:ss.fff") + " Invoking "" + eventType + """); 
    204. #endif 
    205. OnBroadcasting(eventType); 
    206.  
    207. Delegate d; 
    208. if (eventTable.TryGetValue(eventType, out d)) { 
    209. Callback<T> callback = d as Callback<T>; 
    210.  
    211. if (callback != null) { 
    212. callback(arg1); 
    213. } else
    214. throw CreateBroadcastSignatureException(eventType); 



    215.  
    216. //Two parameters 
    217. static public void Broadcast<T, U>(string eventType, T arg1, U arg2) { 
    218. #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE 
    219. Debug.Log("MESSENGER " + System.DateTime.Now.ToString("hh:mm:ss.fff") + " Invoking "" + eventType + """); 
    220. #endif 
    221. OnBroadcasting(eventType); 
    222.  
    223. Delegate d; 
    224. if (eventTable.TryGetValue(eventType, out d)) { 
    225. Callback<T, U> callback = d as Callback<T, U>; 
    226.  
    227. if (callback != null) { 
    228. callback(arg1, arg2); 
    229. } else
    230. throw CreateBroadcastSignatureException(eventType); 



    231.  
    232. //Three parameters 
    233. static public void Broadcast<T, U, V>(string eventType, T arg1, U arg2, V arg3) { 
    234. #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE 
    235. Debug.Log("MESSENGER " + System.DateTime.Now.ToString("hh:mm:ss.fff") + " Invoking "" + eventType + """); 
    236. #endif 
    237. OnBroadcasting(eventType); 
    238.  
    239. Delegate d; 
    240. if (eventTable.TryGetValue(eventType, out d)) { 
    241. Callback<T, U, V> callback = d as Callback<T, U, V>; 
    242.  
    243. if (callback != null) { 
    244. callback(arg1, arg2, arg3); 
    245. } else
    246. throw CreateBroadcastSignatureException(eventType); 



    247. #endregion 

    248.  
    249. //This manager will ensure that the messenger's eventTable will be cleaned up upon loading of a new level. 
    250. public sealed class MessengerHelper : MonoBehaviour
    251. void Awake () 

    252. DontDestroyOnLoad(gameObject);  

    253.  
    254. //Clean up eventTable every time a new level loads. 
    255. public void OnDisable()
    256. Messenger.Cleanup(); 


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

    1. void Update() 

    2. if(Input.GetMouseButtonDown(0)) 

    3. Messenger.Broadcast("Send"); 


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

    1. using UnityEngine; 
    2. using System.Collections; 
    3.  
    4. public class Script : MonoBehaviour
    5.  
    6. void Awake() 

    7. Messenger.AddListener( "Send", DoSomething ); 

    8. public void DoSomething() 

    9. Debug.Log("DoSomething"); 


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

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

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

    1. void Update() 

    2. if(Input.GetMouseButtonDown(0)) 

    3. GameObject cube = GameObject.Find("Cube"); 
    4. Messenger.Broadcast<GameObject,int>("Send",cube,1980); 


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

    1. using UnityEngine; 
    2. using System.Collections; 
    3.  
    4. public class Script : MonoBehaviour
    5.  
    6. void Awake() 

    7. Messenger.AddListener<GameObject,int>( "Send", DoSomething ); 

    8. public void DoSomething(GameObject obj,int i) 

    9. Debug.Log("name " + obj.name + " id =" + i); 


    如果传递一个参数

    两个参数<T,T>

    三个参数<T,T,T>

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

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

  • 相关阅读:
    8.C++-类的关键字
    BFS-九宫格重排(详解)
    7.C++类与封装的概念
    4.数码相框-freetype多行显示,居中显示
    3.数码相框-通过freetype库实现矢量显示
    Linux-iconv命令、并批处理将所有GBK文件转换为UTF8(18)
    2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)
    1.数码相框-相框框架分析(1)
    Kotlin 之操作符重载
    Andorid-解决View重复点击的思路
  • 原文地址:https://www.cnblogs.com/Firepad-magic/p/9484304.html
Copyright © 2020-2023  润新知