Advanced CSharp Messenger 屬于C#事件的一種。 維基百科中由詳細的說明http://wiki.unity3d.com/index.php?title=Advanced_CSharp_Messenger 上周的一天剛巧有朋友問到我這一塊的知識,那麼我研究出來将它貼在部落格中,幫助了他也幫助我自己!哇咔咔。
Advanced CSharp Messenger的特點可以将遊戲對象做為參數發送。到底Advanced CSharp Messenger有什麼用呢?先建立一個立方體對象,然後把Script腳本綁定在這個對象中。腳本中有一個方法叫DoSomething()。寫一段簡單的代碼,通常我們在調用方法的時候需要這樣來寫。
01 | private Script script; |
04 | GameObject cube = GameObject.Find( "Cube" ); |
05 | script = cube.GetComponent<Script>(); |
10 | if (Input.GetMouseButtonDown(0)) |
代碼比較簡單,我就不注釋了。 原理就是先擷取遊戲對象,接着擷取腳本元件對象,最後通過腳本元件對象去調用對應腳本中的方法,這樣的調用方法我們稱之為直接調用。
這個例子中我隻調用了一個對象的方法,如果說有成千上萬個對象,那麼這樣調用是不是感覺自己的代碼非常的醜?因為你需要一個一個的擷取對象然後擷取腳本元件然後在調用方法。。。。。 (想想都恐怖!!)
下面我們在用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
023 | //#define LOG_ALL_MESSAGES |
024 | //#define LOG_ADD_LISTENER |
025 | //#define LOG_BROADCAST_MESSAGE |
026 | #define REQUIRE_LISTENER |
029 | using System.Collections.Generic; |
032 | static internal class Messenger { |
033 | #region Internal variables |
035 | //Disable the unused variable warning |
036 | #pragma warning disable 0414 |
037 | //Ensures that the MessengerHelper will be created automatically upon start of the game. |
038 | static private MessengerHelper messengerHelper = ( new GameObject( "MessengerHelper" ) ).AddComponent< MessengerHelper >(); |
039 | #pragma warning restore 0414 |
041 | static public Dictionary< string , Delegate> eventTable = new Dictionary< string , Delegate>(); |
043 | //Message handlers that should never be removed, regardless of calling Cleanup |
044 | static public List< string > permanentMessages = new List< string > (); |
046 | #region Helper methods |
047 | //Marks a certain message as permanent. |
048 | static public void MarkAsPermanent( string eventType) { |
050 | Debug.Log( "Messenger MarkAsPermanent \t\"" + eventType + "\"" ); |
053 | permanentMessages.Add( eventType ); |
056 | static public void Cleanup() |
059 | Debug.Log( "MESSENGER Cleanup. Make sure that none of necessary listeners are removed." ); |
062 | List< string > messagesToRemove = new List< string >(); |
064 | foreach (KeyValuePair< string , Delegate> pair in eventTable) { |
065 | bool wasFound = false ; |
067 | foreach ( string message in permanentMessages) { |
068 | if (pair.Key == message) { |
075 | messagesToRemove.Add( pair.Key ); |
078 | foreach ( string message in messagesToRemove) { |
079 | eventTable.Remove( message ); |
083 | static public void PrintEventTable() |
085 | Debug.Log( "\t\t\t=== MESSENGER PrintEventTable ===" ); |
087 | foreach (KeyValuePair< string , Delegate> pair in eventTable) { |
088 | Debug.Log( "\t\t\t" + pair.Key + "\t\t" + pair.Value); |
095 | #region Message logging and exception throwing |
096 | static public void OnListenerAdding( string eventType, Delegate listenerBeingAdded) { |
097 | #if LOG_ALL_MESSAGES || LOG_ADD_LISTENER |
098 | Debug.Log( "MESSENGER OnListenerAdding \t\"" + eventType + "\"\t{" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}" ); |
101 | if (!eventTable.ContainsKey(eventType)) { |
102 | eventTable.Add(eventType, null ); |
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)); |
111 | static public void OnListenerRemoving( string eventType, Delegate listenerBeingRemoved) { |
113 | Debug.Log( "MESSENGER OnListenerRemoving \t\"" + eventType + "\"\t{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}" ); |
116 | if (eventTable.ContainsKey(eventType)) { |
117 | Delegate d = eventTable[eventType]; |
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)); |
125 | throw new ListenerException( string .Format( "Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type." , eventType)); |
129 | static public void OnListenerRemoved( string eventType) { |
130 | if (eventTable[eventType] == null ) { |
131 | eventTable.Remove(eventType); |
135 | static public void OnBroadcasting( string eventType) { |
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)); |
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)); |
147 | public class BroadcastException : Exception { |
148 | public BroadcastException( string msg) |
153 | public class ListenerException : Exception { |
154 | public ListenerException( string msg) |
162 | static public void AddListener( string eventType, Callback handler) { |
163 | OnListenerAdding(eventType, handler); |
164 | eventTable[eventType] = (Callback)eventTable[eventType] + handler; |
168 | static public void AddListener<T>( string eventType, Callback<T> handler) { |
169 | OnListenerAdding(eventType, handler); |
170 | eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler; |
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; |
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; |
186 | #region RemoveListener |
188 | static public void RemoveListener( string eventType, Callback handler) { |
189 | OnListenerRemoving(eventType, handler); |
190 | eventTable[eventType] = (Callback)eventTable[eventType] - handler; |
191 | OnListenerRemoved(eventType); |
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); |
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); |
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); |
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 + "\"" ); |
222 | OnBroadcasting(eventType); |
225 | if (eventTable.TryGetValue(eventType, out d)) { |
226 | Callback callback = d as Callback; |
228 | if (callback != null ) { |
231 | throw CreateBroadcastSignatureException(eventType); |
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 + "\"" ); |
241 | OnBroadcasting(eventType); |
244 | if (eventTable.TryGetValue(eventType, out d)) { |
245 | Callback<T> callback = d as Callback<T>; |
247 | if (callback != null ) { |
250 | throw CreateBroadcastSignatureException(eventType); |
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 + "\"" ); |
260 | OnBroadcasting(eventType); |
263 | if (eventTable.TryGetValue(eventType, out d)) { |
264 | Callback<T, U> callback = d as Callback<T, U>; |
266 | if (callback != null ) { |
267 | callback(arg1, arg2); |
269 | throw CreateBroadcastSignatureException(eventType); |
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 + "\"" ); |
279 | OnBroadcasting(eventType); |
282 | if (eventTable.TryGetValue(eventType, out d)) { |
283 | Callback<T, U, V> callback = d as Callback<T, U, V>; |
285 | if (callback != null ) { |
286 | callback(arg1, arg2, arg3); |
288 | throw CreateBroadcastSignatureException(eventType); |
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 { |
299 | DontDestroyOnLoad(gameObject); |
302 | //Clean up eventTable every time a new level loads. |
303 | public void OnDisable() { |
然後就可以開始使用了,Messager.Broadcast()這樣就好比我們發送了一條廣播。
3 | if (Input.GetMouseButtonDown(0)) |
5 | Messenger.Broadcast( "Send" ); |
在需要這條廣播的類中來接受它,同樣是剛剛說的Script類。接受廣播的标志是 Messager.AddListener()參數1表示廣播的名稱,參數2表示廣播所調用的方法。
02 | using System.Collections; |
04 | public class Script : MonoBehaviour { |
08 | Messenger.AddListener( "Send" , DoSomething ); |
10 | public void DoSomething() |
12 | Debug.Log( "DoSomething" ); |
這樣一來,隻要發送名稱為”Send”的方法,就可以在别的類中接收它了。
我們在說說如何通過廣播來傳遞參數,這也是那天那個哥們主要問我的問題。(其實是維基百科上寫的不是特别特别的清楚,那哥們誤解了)在Callback中可以看出參數最多可以是三個,參數的類型是任意類型,也就是說我們不僅能傳遞 int float bool 還能傳遞gameObject類型。
如下所示,發送廣播的時候傳遞了兩個參數,參數1是一個遊戲對象,參數2是一個int數值。
3 | if (Input.GetMouseButtonDown(0)) |
5 | GameObject cube = GameObject.Find( "Cube" ); |
6 | Messenger.Broadcast<GameObject, int >( "Send" ,cube,1980); |
然後是接受的地方 參數用<>存在一起。遊戲對象也可以完美的傳遞。
02 | using System.Collections; |
04 | public class Script : MonoBehaviour { |
08 | Messenger.AddListener<GameObject, int >( "Send" , DoSomething ); |
10 | public void DoSomething(GameObject obj, int i) |
12 | Debug.Log( "name " + obj.name + " id =" + i); |
如果傳遞一個參數<T>
兩個參數<T,T>
三個參數<T,T,T>
怎麼樣使用起來還是挺簡單的吧?
我覺得項目中最好不要大量的使用代理事件這類的方法(根據需求而定),雖然可以讓你的代碼非常的簡潔,但是它的效率不高大概比直接調用慢5-倍左右吧,就好比美好的東西一定都有瑕疵一樣。 還記得Unity自身也提供了一種發送消息的方法嗎?,用過的都知道效率也非常低下,雖然我們看不到它具體實作的源碼是如何實作的,但是我覺得原理可能也是這樣的。 歡迎和大家一起讨論與學習。