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