Advanced CSharp Messenger 屬於C#事件的一種。 維基百科中由詳細的說明http://wiki.unity3d.com/index.php?title=Advanced_CSharp_Messenger 上週的一天剛巧有朋友問到我這一塊的知識,那麼我研究出來將它貼在博客中,幫助了他也幫助我自己!哇咔咔。
Advanced CSharp Messenger的特點可以將遊戲對象做爲參數發送。到底Advanced CSharp Messenger有什麼用呢?先創建一個立方體對象,然後把Script腳本綁定在這個對象中。腳本中有一個方法叫DoSomething()。寫一段簡單的代碼,通常我們在調用方法的時候需要這樣來寫。
privateScriptscript;
voidAwake()
{
}
voidUpdate()
{
}
|
代碼比較簡單,我就不註釋了。 原理就是先獲取遊戲對象,接着獲取腳本組件對象,最後通過腳本組件對象去調用對應腳本中的方法,這樣的調用方法我們稱之爲直接調用。
這個例子中我只調用了一個對象的方法,如果說有成千上萬個對象,那麼這樣調用是不是感覺自己的代碼非常的醜?因爲你需要一個一個的獲取對象然後獲取腳本組件然後在調用方法。。。。。 (想想都恐怖!!)
下面我們在用Advanced CSharp Messenger來實現事件的調用。按照維基百科中首先把Message.cs 和Callback.cs拷貝在你的工程中。
CallBack.cs
1
2
3
4
|
publicdelegatevoidCallback();
publicdelegatevoidCallback<T>(Targ1);
publicdelegatevoidCallback<T,U>(Targ1,Uarg2);
publicdelegatevoidCallback<T,U,V>(Targ1,Uarg2,Varg3);
|
Message.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
|
/*
* Advanced C# messenger by Ilya Suzdalnitski. V1.0
*
* Based on Rod Hyde's "CSharpMessenger" and Magnus Wolffelt's "CSharpMessenger
Extended".
*
* Features:
* Prevents a MissingReferenceException because of a reference to a
destroyed message handler.
* Option to log all messages
* Extensive error detection, preventing silent bugs
*
* Usage examples:
1. Messenger.AddListener<GameObject>("prop collected", PropCollected);
Messenger.Broadcast<GameObject>("prop collected", prop);
2. Messenger.AddListener<float>("speed changed", SpeedChanged);
Messenger.Broadcast<float>("speed changed", 0.5f);
*
* Messenger cleans up its evenTable automatically upon loading of a
new level.
*
* Don't forget that the messages that should survive the cleanup, should
be marked with Messenger.MarkAsPermanent(string)
*
*/
//#define LOG_ALL_MESSAGES
//#define LOG_ADD_LISTENER
//#define LOG_BROADCAST_MESSAGE
#define REQUIRE_LISTENER
usingSystem;
usingSystem.Collections.Generic;
usingUnityEngine;
staticinternalclassMessenger{
#region
Internal variables
//Disable
the unused variable warning
#pragma warning disable 0414
//Ensures
that the MessengerHelper will be created automatically upon start of the game.
staticprivateMessengerHelpermessengerHelper=(newGameObject("MessengerHelper")).AddComponent<MessengerHelper>();
#pragma warning restore 0414
staticpublicDictionary<string,Delegate>eventTable=newDictionary<string,Delegate>();
//Message
handlers that should never be removed, regardless of calling Cleanup
staticpublicList<string>permanentMessages=newList<string>();
#endregion
#region
Helper methods
//Marks
a certain message as permanent.
staticpublicvoidMarkAsPermanent(stringeventType){
#if LOG_ALL_MESSAGES
Debug.Log("Messenger
MarkAsPermanent \t\""+eventType+"\"");
#endif
permanentMessages.Add(eventType);
}
staticpublicvoidCleanup()
{
#if LOG_ALL_MESSAGES
Debug.Log("MESSENGER
Cleanup. Make sure that none of necessary listeners are removed.");
#endif
List<string>messagesToRemove=newList<string>();
foreach(KeyValuePair<string,Delegate>pairineventTable){
boolwasFound=false;
foreach(stringmessageinpermanentMessages){
if(pair.Key==message){
wasFound=true;
break;
}
}
if(!wasFound)
messagesToRemove.Add(pair.Key);
}
foreach(stringmessageinmessagesToRemove){
eventTable.Remove(message);
}
}
staticpublicvoidPrintEventTable()
{
Debug.Log("\t\t\t===
MESSENGER PrintEventTable ===");
foreach(KeyValuePair<string,Delegate>pairineventTable){
Debug.Log("\t\t\t"+pair.Key+"\t\t"+pair.Value);
}
Debug.Log("\n");
}
#endregion
#region
Message logging and exception throwing
staticpublicvoidOnListenerAdding(stringeventType,DelegatelistenerBeingAdded){
#if LOG_ALL_MESSAGES || LOG_ADD_LISTENER
Debug.Log("MESSENGER
OnListenerAdding \t\""+eventType+"\"\t{"+listenerBeingAdded.Target+"
-> "+listenerBeingAdded.Method+"}");
#endif
if(!eventTable.ContainsKey(eventType)){
eventTable.Add(eventType,null);
}
Delegated=eventTable[eventType];
if(d!=null&&d.GetType()!=listenerBeingAdded.GetType()){
thrownewListenerException(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));
}
}
staticpublicvoidOnListenerRemoving(stringeventType,DelegatelistenerBeingRemoved){
#if LOG_ALL_MESSAGES
Debug.Log("MESSENGER
OnListenerRemoving \t\""+eventType+"\"\t{"+listenerBeingRemoved.Target+"
-> "+listenerBeingRemoved.Method+"}");
#endif
if(eventTable.ContainsKey(eventType)){
Delegated=eventTable[eventType];
if(d==null){
thrownewListenerException(string.Format("Attempting
to remove listener with for event type \"{0}\" but current listener is null.",eventType));
}elseif(d.GetType()!=listenerBeingRemoved.GetType()){
thrownewListenerException(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));
}
}else{
thrownewListenerException(string.Format("Attempting
to remove listener for type \"{0}\" but Messenger doesn't know about this event type.",eventType));
}
}
staticpublicvoidOnListenerRemoved(stringeventType){
if(eventTable[eventType]==null){
eventTable.Remove(eventType);
}
}
staticpublicvoidOnBroadcasting(stringeventType){
#if REQUIRE_LISTENER
if(!eventTable.ContainsKey(eventType)){
thrownewBroadcastException(string.Format("Broadcasting
message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.",eventType));
}
#endif
}
staticpublicBroadcastExceptionCreateBroadcastSignatureException(stringeventType){
returnnewBroadcastException(string.Format("Broadcasting
message \"{0}\" but listeners have a different signature than the broadcaster.",eventType));
}
publicclassBroadcastException:Exception{
publicBroadcastException(stringmsg)
:base(msg){
}
}
publicclassListenerException:Exception{
publicListenerException(stringmsg)
:base(msg){
}
}
#endregion
#region
AddListener
//No
parameters
staticpublicvoidAddListener(stringeventType,Callbackhandler){
OnListenerAdding(eventType,handler);
eventTable[eventType]=(Callback)eventTable[eventType]+handler;
}
//Single
parameter
staticpublicvoidAddListener<T>(stringeventType,Callback<T>handler){
OnListenerAdding(eventType,handler);
eventTable[eventType]=(Callback<T>)eventTable[eventType]+handler;
}
//Two
parameters
staticpublicvoidAddListener<T,U>(stringeventType,Callback<T,U>handler){
OnListenerAdding(eventType,handler);
eventTable[eventType]=(Callback<T,U>)eventTable[eventType]+handler;
}
//Three
parameters
staticpublicvoidAddListener<T,U,V>(stringeventType,Callback<T,U,V>handler){
OnListenerAdding(eventType,handler);
eventTable[eventType]=(Callback<T,U,V>)eventTable[eventType]+handler;
}
#endregion
#region
RemoveListener
//No
parameters
staticpublicvoidRemoveListener(stringeventType,Callbackhandler){
OnListenerRemoving(eventType,handler);
eventTable[eventType]=(Callback)eventTable[eventType]-handler;
OnListenerRemoved(eventType);
}
//Single
parameter
staticpublicvoidRemoveListener<T>(stringeventType,Callback<T>handler){
OnListenerRemoving(eventType,handler);
eventTable[eventType]=(Callback<T>)eventTable[eventType]-handler;
OnListenerRemoved(eventType);
}
//Two
parameters
staticpublicvoidRemoveListener<T,U>(stringeventType,Callback<T,U>handler){
OnListenerRemoving(eventType,handler);
eventTable[eventType]=(Callback<T,U>)eventTable[eventType]-handler;
OnListenerRemoved(eventType);
}
//Three
parameters
staticpublicvoidRemoveListener<T,U,V>(stringeventType,Callback<T,U,V>handler){
OnListenerRemoving(eventType,handler);
eventTable[eventType]=(Callback<T,U,V>)eventTable[eventType]-handler;
OnListenerRemoved(eventType);
}
#endregion
#region
Broadcast
//No
parameters
staticpublicvoidBroadcast(stringeventType){
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
Debug.Log("MESSENGER\t"+System.DateTime.Now.ToString("hh:mm:ss.fff")+"\t\t\tInvoking
\t\""+eventType+"\"");
#endif
OnBroadcasting(eventType);
Delegated;
if(eventTable.TryGetValue(eventType,outd)){
Callbackcallback=dasCallback;
if(callback!=null){
callback();
}else{
throwCreateBroadcastSignatureException(eventType);
}
}
}
//Single
parameter
staticpublicvoidBroadcast<T>(stringeventType,Targ1){
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
Debug.Log("MESSENGER\t"+System.DateTime.Now.ToString("hh:mm:ss.fff")+"\t\t\tInvoking
\t\""+eventType+"\"");
#endif
OnBroadcasting(eventType);
Delegated;
if(eventTable.TryGetValue(eventType,outd)){
Callback<T>callback=dasCallback<T>;
if(callback!=null){
callback(arg1);
}else{
throwCreateBroadcastSignatureException(eventType);
}
}
}
//Two
parameters
staticpublicvoidBroadcast<T,U>(stringeventType,Targ1,Uarg2){
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
Debug.Log("MESSENGER\t"+System.DateTime.Now.ToString("hh:mm:ss.fff")+"\t\t\tInvoking
\t\""+eventType+"\"");
#endif
OnBroadcasting(eventType);
Delegated;
if(eventTable.TryGetValue(eventType,outd)){
Callback<T,U>callback=dasCallback<T,U>;
if(callback!=null){
callback(arg1,arg2);
}else{
throwCreateBroadcastSignatureException(eventType);
}
}
}
//Three
parameters
staticpublicvoidBroadcast<T,U,V>(stringeventType,Targ1,Uarg2,Varg3){
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
Debug.Log("MESSENGER\t"+System.DateTime.Now.ToString("hh:mm:ss.fff")+"\t\t\tInvoking
\t\""+eventType+"\"");
#endif
OnBroadcasting(eventType);
Delegated;
if(eventTable.TryGetValue(eventType,outd)){
Callback<T,U,V>callback=dasCallback<T,U,V>;
if(callback!=null){
callback(arg1,arg2,arg3);
}else{
throwCreateBroadcastSignatureException(eventType);
}
}
}
#endregion
}
//This manager will ensure that the messenger's eventTable will be
cleaned up upon loading of a new level.
publicsealedclassMessengerHelper:MonoBehaviour{
voidAwake()
{
DontDestroyOnLoad(gameObject);
}
//Clean
up eventTable every time a new level loads.
publicvoidOnDisable(){
Messenger.Cleanup();
}
}
|
然後就可以開始使用了,Messager.Broadcast()這樣就好比我們發送了一條廣播。
1
2
3
4
5
6
7
|
voidUpdate()
{
if(Input.GetMouseButtonDown(0))
{
Messenger.Broadcast("Send");
}
}
|
在需要這條廣播的類中來接受它,同樣是剛剛說的Script類。接受廣播的標誌是 Messager.AddListener()參數1表示廣播的名稱,參數2表示廣播所調用的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
usingUnityEngine;
usingSystem.Collections;
publicclassScript:MonoBehaviour{
voidAwake()
{
Messenger.AddListener("Send",DoSomething);
}
publicvoidDoSomething()
{
Debug.Log("DoSomething");
}
}
|
這樣一來,只要發送名稱爲”Send”的方法,就可以在別的類中接收它了。
我們在說說如何通過廣播來傳遞參數,這也是那天那個哥們主要問我的問題。(其實是維基百科上寫的不是特別特別的清楚,那哥們誤解了)在Callback中可以看出參數最多可以是三個,參數的類型是任意類型,也就是說我們不僅能傳遞 int float bool 還能傳遞gameObject類型。
如下所示,發送廣播的時候傳遞了兩個參數,參數1是一個遊戲對象,參數2是一個int數值。
1
2
3
4
5
6
7
8
|
voidUpdate()
{
if(Input.GetMouseButtonDown(0))
{
GameObjectcube=GameObject.Find("Cube");
Messenger.Broadcast<GameObject,int>("Send",cube,1980);
}
}
|
然後是接受的地方 參數用<>存在一起。遊戲對象也可以完美的傳遞。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
usingUnityEngine;
usingSystem.Collections;
publicclassScript:MonoBehaviour{
voidAwake()
{
Messenger.AddListener<GameObject,int>("Send",DoSomething);
}
publicvoidDoSomething(GameObjectobj,inti)
{
Debug.Log("name
"+obj.name+"
id ="+i);
}
}
|
如果傳遞一個參數<T>
兩個參數<T,T>
三個參數<T,T,T>
怎麼樣使用起來還是挺簡單的吧?
我覺得項目中最好不要大量的使用代理事件這類的方法(根據需求而定),雖然可以讓你的代碼非常的簡潔,但是它的效率不高大概比直接調用慢5-倍左右吧,就好比美好的東西一定都有瑕疵一樣。 還記得Unity自身也提供了一種發送消息的方法嗎?,用過的都知道效率也非常低下,雖然我們看不到它具體實現的源碼是如何實現的,但是我覺得原理可能也是這樣的。 歡迎和大家一起討論與學習。