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 \t\"" + 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("\t\t\t=== MESSENGER PrintEventTable ==="); 
  78.  
  79. foreach (KeyValuePair<string, Delegate> pair in eventTable) { 
  80. Debug.Log("\t\t\t" + pair.Key + "\t\t" + pair.Value); 

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

  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 \t\"" + eventType + "\"\t{" + 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 \t\"" + eventType + "\"\t{" + 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\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + 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\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + 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\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + 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\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + 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自身也提供了一种发送消息的方法吗?,用过的都知道效率也非常低下,虽然我们看不到它具体实现的源码是如何实现的,但是我觉得原理可能也是这样的。 欢迎和大家一起讨论与学习。

猜你喜欢

转载自www.cnblogs.com/Firepad-magic/p/9484304.html