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. { 
  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. } 
  15.  

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

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

下面我们在用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.  
  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("\t\t\t=== MESSENGER PrintEventTable ==="); 
  86.  
  87. foreach (KeyValuePair<string, Delegate> pair in eventTable) { 
  88. Debug.Log("\t\t\t" + pair.Key + "\t\t" + pair.Value); 
  89. } 
  90.  
  91. Debug.Log("\n"); 
  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 \t\"" + eventType + "\"\t{" + 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 \t\"" + eventType + "\"\t{" + 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\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + 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\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + 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\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + 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\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + 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>

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

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

posted @ 2018-08-15 22:00  世纪末の魔术师  阅读(1743)  评论(0)    收藏  举报