Unity消息简易框架 Advanced C# messenger

Unity C# 消息机制 

【转载 雨凇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. void Update() 

  8. { 

  9. if(Input.GetMouseButtonDown(0)) 

  10. { 

  11. script.DoSomething(); 

  12. } 

  13. } 


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

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

下面我们在用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. //#define LOG_ALL_MESSAGES 

  23. //#define LOG_ADD_LISTENER 

  24. //#define LOG_BROADCAST_MESSAGE 

  25. #define REQUIRE_LISTENER 


  26. using System; 

  27. using System.Collections.Generic; 

  28. using UnityEngine; 


  29. static internal class Messenger { 

  30. #region Internal variables 


  31. //Disable the unused variable warning 

  32. #pragma warning disable 0414 

  33. //Ensures that the MessengerHelper will be created automatically upon start of the game. 

  34. static private MessengerHelper messengerHelper = ( new GameObject("MessengerHelper") ).AddComponent< MessengerHelper >(); 

  35. #pragma warning restore 0414 


  36. static public Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>(); 


  37. //Message handlers that should never be removed, regardless of calling Cleanup 

  38. static public List< string > permanentMessages = new List< string > (); 

  39. #endregion 

  40. #region Helper methods 

  41. //Marks a certain message as permanent. 

  42. static public void MarkAsPermanent(string eventType) { 

  43. #if LOG_ALL_MESSAGES 

  44. Debug.Log("Messenger MarkAsPermanent \t\"" + eventType + "\""); 

  45. #endif 


  46. permanentMessages.Add( eventType ); 

  47. } 


  48. static public void Cleanup() 

  49. { 

  50. #if LOG_ALL_MESSAGES 

  51. Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed."); 

  52. #endif 


  53. List< string > messagesToRemove = new List<string>(); 


  54. foreach (KeyValuePair<string, Delegate> pair in eventTable) { 

  55. bool wasFound = false; 


  56. foreach (string message in permanentMessages) { 

  57. if (pair.Key == message) { 

  58. wasFound = true; 

  59. break; 

  60. } 

  61. } 


  62. if (!wasFound) 

  63. messagesToRemove.Add( pair.Key ); 

  64. } 


  65. foreach (string message in messagesToRemove) { 

  66. eventTable.Remove( message ); 

  67. } 

  68. } 


  69. static public void PrintEventTable() 

  70. { 

  71. Debug.Log("\t\t\t=== MESSENGER PrintEventTable ==="); 


  72. foreach (KeyValuePair<string, Delegate> pair in eventTable) { 

  73. Debug.Log("\t\t\t" + pair.Key + "\t\t" + pair.Value); 

  74. } 


  75. Debug.Log("\n"); 

  76. } 

  77. #endregion 


  78. #region Message logging and exception throwing 

  79. static public void OnListenerAdding(string eventType, Delegate listenerBeingAdded) { 

  80. #if LOG_ALL_MESSAGES || LOG_ADD_LISTENER 

  81. Debug.Log("MESSENGER OnListenerAdding \t\"" + eventType + "\"\t{" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}"); 

  82. #endif 


  83. if (!eventTable.ContainsKey(eventType)) { 

  84. eventTable.Add(eventType, null ); 

  85. } 


  86. Delegate d = eventTable[eventType]; 

  87. if (d != null && d.GetType() != listenerBeingAdded.GetType()) { 

  88. 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)); 

  89. } 

  90. } 


  91. static public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved) { 

  92. #if LOG_ALL_MESSAGES 

  93. Debug.Log("MESSENGER OnListenerRemoving \t\"" + eventType + "\"\t{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}"); 

  94. #endif 


  95. if (eventTable.ContainsKey(eventType)) { 

  96. Delegate d = eventTable[eventType]; 


  97. if (d == null) { 

  98. throw new ListenerException(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType)); 

  99. } else if (d.GetType() != listenerBeingRemoved.GetType()) { 

  100. 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)); 

  101. } 

  102. } else { 

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

  104. } 

  105. } 


  106. static public void OnListenerRemoved(string eventType) { 

  107. if (eventTable[eventType] == null) { 

  108. eventTable.Remove(eventType); 

  109. } 

  110. } 


  111. static public void OnBroadcasting(string eventType) { 

  112. #if REQUIRE_LISTENER 

  113. if (!eventTable.ContainsKey(eventType)) { 

  114. throw new BroadcastException(string.Format("Broadcasting message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", eventType)); 

  115. } 

  116. #endif 

  117. } 


  118. static public BroadcastException CreateBroadcastSignatureException(string eventType) { 

  119. return new BroadcastException(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType)); 

  120. } 


  121. public class BroadcastException : Exception { 

  122. public BroadcastException(string msg) 

  123. : base(msg) { 

  124. } 

  125. } 


  126. public class ListenerException : Exception { 

  127. public ListenerException(string msg) 

  128. : base(msg) { 

  129. } 

  130. } 

  131. #endregion 


  132. #region AddListener 

  133. //No parameters 

  134. static public void AddListener(string eventType, Callback handler) { 

  135. OnListenerAdding(eventType, handler); 

  136. eventTable[eventType] = (Callback)eventTable[eventType] + handler; 

  137. } 


  138. //Single parameter 

  139. static public void AddListener<T>(string eventType, Callback<T> handler) { 

  140. OnListenerAdding(eventType, handler); 

  141. eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler; 

  142. } 


  143. //Two parameters 

  144. static public void AddListener<T, U>(string eventType, Callback<T, U> handler) { 

  145. OnListenerAdding(eventType, handler); 

  146. eventTable[eventType] = (Callback<T, U>)eventTable[eventType] + handler; 

  147. } 


  148. //Three parameters 

  149. static public void AddListener<T, U, V>(string eventType, Callback<T, U, V> handler) { 

  150. OnListenerAdding(eventType, handler); 

  151. eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] + handler; 

  152. } 

  153. #endregion 


  154. #region RemoveListener 

  155. //No parameters 

  156. static public void RemoveListener(string eventType, Callback handler) { 

  157. OnListenerRemoving(eventType, handler);  

  158. eventTable[eventType] = (Callback)eventTable[eventType] - handler; 

  159. OnListenerRemoved(eventType); 

  160. } 


  161. //Single parameter 

  162. static public void RemoveListener<T>(string eventType, Callback<T> handler) { 

  163. OnListenerRemoving(eventType, handler); 

  164. eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler; 

  165. OnListenerRemoved(eventType); 

  166. } 


  167. //Two parameters 

  168. static public void RemoveListener<T, U>(string eventType, Callback<T, U> handler) { 

  169. OnListenerRemoving(eventType, handler); 

  170. eventTable[eventType] = (Callback<T, U>)eventTable[eventType] - handler; 

  171. OnListenerRemoved(eventType); 

  172. } 


  173. //Three parameters 

  174. static public void RemoveListener<T, U, V>(string eventType, Callback<T, U, V> handler) { 

  175. OnListenerRemoving(eventType, handler); 

  176. eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] - handler; 

  177. OnListenerRemoved(eventType); 

  178. } 

  179. #endregion 


  180. #region Broadcast 

  181. //No parameters 

  182. static public void Broadcast(string eventType) { 

  183. #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE 

  184. Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); 

  185. #endif 

  186. OnBroadcasting(eventType); 


  187. Delegate d; 

  188. if (eventTable.TryGetValue(eventType, out d)) { 

  189. Callback callback = d as Callback; 


  190. if (callback != null) { 

  191. callback(); 

  192. } else { 

  193. throw CreateBroadcastSignatureException(eventType); 

  194. } 

  195. } 

  196. } 


  197. //Single parameter 

  198. static public void Broadcast<T>(string eventType, T arg1) { 

  199. #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE 

  200. Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); 

  201. #endif 

  202. OnBroadcasting(eventType); 


  203. Delegate d; 

  204. if (eventTable.TryGetValue(eventType, out d)) { 

  205. Callback<T> callback = d as Callback<T>; 


  206. if (callback != null) { 

  207. callback(arg1); 

  208. } else { 

  209. throw CreateBroadcastSignatureException(eventType); 

  210. } 

  211. } 

  212. } 


  213. //Two parameters 

  214. static public void Broadcast<T, U>(string eventType, T arg1, U arg2) { 

  215. #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE 

  216. Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); 

  217. #endif 

  218. OnBroadcasting(eventType); 


  219. Delegate d; 

  220. if (eventTable.TryGetValue(eventType, out d)) { 

  221. Callback<T, U> callback = d as Callback<T, U>; 


  222. if (callback != null) { 

  223. callback(arg1, arg2); 

  224. } else { 

  225. throw CreateBroadcastSignatureException(eventType); 

  226. } 

  227. } 

  228. } 


  229. //Three parameters 

  230. static public void Broadcast<T, U, V>(string eventType, T arg1, U arg2, V arg3) { 

  231. #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE 

  232. Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); 

  233. #endif 

  234. OnBroadcasting(eventType); 


  235. Delegate d; 

  236. if (eventTable.TryGetValue(eventType, out d)) { 

  237. Callback<T, U, V> callback = d as Callback<T, U, V>; 


  238. if (callback != null) { 

  239. callback(arg1, arg2, arg3); 

  240. } else { 

  241. throw CreateBroadcastSignatureException(eventType); 

  242. } 

  243. } 

  244. } 

  245. #endregion 

  246. } 


  247. //This manager will ensure that the messenger's eventTable will be cleaned up upon loading of a new level. 

  248. public sealed class MessengerHelper : MonoBehaviour { 

  249. void Awake () 

  250. { 

  251. DontDestroyOnLoad(gameObject);  

  252. } 


  253. //Clean up eventTable every time a new level loads. 

  254. public void OnDisable() { 

  255. Messenger.Cleanup(); 

  256. } 

  257. } 

然后就可以开始使用了,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. public class Script : MonoBehaviour { 


  4. void Awake() 

  5. { 

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

  7. } 

  8. public void DoSomething() 

  9. { 

  10. Debug.Log("DoSomething"); 

  11. } 

  12. } 

这样一来,只要发送名称为”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. public class Script : MonoBehaviour { 


  4. void Awake() 

  5. { 

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

  7. } 

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

  9. { 

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

  11. } 

  12. } 

如果传递一个参数

两个参数<T,T>

三个参数<T,T,T>

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

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

Unity消息简易框架 Advanced C# messenger的更多相关文章

  1. unity客户端基本框架(转载)

    框架概述: 基础系统的框架搭建,其中包括: UI框架(NGUI + MVC) 消息管理(Advanced CSharp Messenger) 网络层框架(Socket + Protobuf ) 表格数 ...

  2. 【转】Unity3D研究院之通过C#使用Advanced CSharp Messenger(五十)

    http://www.xuanyusong.com/archives/2165 Advanced CSharp Messenger 属于C#事件的一种. 维基百科中由详细的说明http://wiki. ...

  3. Unity3D之通过C#使用Advanced CSharp Messenger

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

  4. 【转】快速理解Kafka分布式消息队列框架

     from:http://blog.csdn.net/colorant/article/details/12081909 快速理解Kafka分布式消息队列框架 标签: kafkamessage que ...

  5. 快速理解Kafka分布式消息队列框架

    作者:刘旭晖 Raymond 转载请注明出处 Email:colorant at 163.com BLOG:http://blog.csdn.net/colorant/ ==是什么 == 简单的说,K ...

  6. Advanced CSharp Messenger

    http://wiki.unity3d.com/index.php?title=Advanced_CSharp_Messenger Author: Ilya Suzdalnitski Contents ...

  7. “一切都是消息”--MSF(消息服务框架)入门简介

    “一切都是消息”--这是MSF(消息服务框架)的设计哲学. MSF的名字是 Message Service Framework 的简称,中文名称:消息服务框架,它是PDF.NET框架的一部分. 1,M ...

  8. “一切都是消息”--MSF(消息服务框架)之【请求-响应】模式

    在前一篇, “一切都是消息”--MSF(消息服务框架)入门简介, 我们介绍了MSF基于异步通信,支持请求-响应通信模式和发布-订阅通信模式,并且介绍了如何获取MSF.今天,我们来看看如何使用MSF来做 ...

  9. “一切都是消息”--MSF(消息服务框架)之【发布-订阅】模式

    在上一篇,“一切都是消息”--MSF(消息服务框架)之[请求-响应]模式 ,我们演示了MSF实现简单的请求-响应模式的示例,今天来看看如何实现[发布-订阅]模式.简单来说,该模式的工作过程是: 客户端 ...

随机推荐

  1. JavaScript基础进阶之数组方法总结

    数组常用方法总结:  下面我只总结了es3中常用的数组方法,一共有11个.es5中新增的9个数组方法,后续再单独总结. 1个连接数组的方法:concat() 2个数组转换为字符串的方法:join(). ...

  2. P2619 [国家集训队2]Tree I

    Description 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色 ...

  3. ajax调用webservice 跨域问题

    用js或者jquery跨域调用接口时 对方的接口需要做jsonp处理,你的ajax jsonp调用才可以 egg 接口中已经做了jsonp处理,所以可以跨域调用 //$.ajax({ // url: ...

  4. Sequelize-nodejs-3-model definition

    Model definition模型定义 To define mappings between a model and a table, use the define method.定义模型和表之间的 ...

  5. 正则表达式详解<一>

    正则表达式是一种处理字符串的微型语言:有以下的基本术语: l  模式(pattern):正则表达式实际上是通过字符串表达的一个模式 l  匹配(match): 判断正则表达式和给出的序列[first ...

  6. AdminLTE前端模板

    AdminLTE是一个Bootstrap框架模板,其功能组件并不比我直接提到的Metronic差. 第一次接触过AdminLTE是在学习Jeesite这个开源项目的时候.离现在已经有半年了. Admi ...

  7. PAT乙级1005

    1005 继续(3n+1)猜想 (25 分) 卡拉兹(Callatz)猜想已经在1001中给出了描述.在这个题目里,情况稍微有些复杂. 当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程 ...

  8. C语言程序设计I—第十一周教学

    第十一周教学总结(12/11-17/11) 教学内容 第4章 循环结构-break continue嵌套循环 4.3 判断素数,4.4求1! + 2! + -. + 100! 课前准备 在蓝墨云班课发 ...

  9. Eclipse设置格式化每行字符的长度

    Windows>>prefrence>>Java>>CodeStyle>>formatter>>edit>>line wrapp ...

  10. 01_Docker概念简介、组件介绍、使用场景和命名空间

    一.简介 Docker是一个能够把开发的应用程序自动部署到容器的开源引擎.Docker在虚拟化的容器执行环境中增加了一个应用程序部署引擎.该引擎的目标就是提供一个轻量.快速的环境,能够运行开发者的程序 ...