天天看點

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()。寫一段簡單的代碼,通常我們在調用方法的時候需要這樣來寫。

01

private

Script script;

02

void

Awake()

03

{

04

GameObject cube = GameObject.Find(

"Cube"

);

05

script = cube.GetComponent<Script>();

06

}

07

08

void

Update()

09

{

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

001

022

023

//#define LOG_ALL_MESSAGES

024

//#define LOG_ADD_LISTENER

025

//#define LOG_BROADCAST_MESSAGE

026

#define REQUIRE_LISTENER

027

028

using

System;

029

using

System.Collections.Generic;

030

using

UnityEngine;

031

032

static

internal

class

Messenger {

033

#region Internal variables

034

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

040

041

static

public

Dictionary<

string

, Delegate> eventTable = 

new

Dictionary<

string

, Delegate>();

042

043

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

044

static

public

List< 

string

> permanentMessages = 

new

List< 

string

> ();

045

#endregion

046

#region Helper methods

047

//Marks a certain message as permanent.

048

static

public

void

MarkAsPermanent(

string

eventType) {

049

#if LOG_ALL_MESSAGES

050

Debug.Log(

"Messenger MarkAsPermanent \t\""

+ eventType + 

"\""

);

051

#endif

052

053

permanentMessages.Add( eventType );

054

}

055

056

static

public

void

Cleanup()

057

{

058

#if LOG_ALL_MESSAGES

059

Debug.Log(

"MESSENGER Cleanup. Make sure that none of necessary listeners are removed."

);

060

#endif

061

062

List< 

string

> messagesToRemove = 

new

List<

string

>();

063

064

foreach

(KeyValuePair<

string

, Delegate> pair 

in

eventTable) {

065

bool

wasFound = 

false

;

066

067

foreach

(

string

message 

in

permanentMessages) {

068

if

(pair.Key == message) {

069

wasFound = 

true

;

070

break

;

071

}

072

}

073

074

if

(!wasFound)

075

messagesToRemove.Add( pair.Key );

076

}

077

078

foreach

(

string

message 

in

messagesToRemove) {

079

eventTable.Remove( message );

080

}

081

}

082

083

static

public

void

PrintEventTable()

084

{

085

Debug.Log(

"\t\t\t=== MESSENGER PrintEventTable ==="

);

086

087

foreach

(KeyValuePair<

string

, Delegate> pair 

in

eventTable) {

088

Debug.Log(

"\t\t\t"

+ pair.Key + 

"\t\t"

+ pair.Value);

089

}

090

091

Debug.Log(

"\n"

);

092

}

093

#endregion

094

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 + 

"}"

);

099

#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表示廣播所調用的方法。

01

using

UnityEngine;

02

using

System.Collections;

03

04

public

class

Script : MonoBehaviour {

05

06

void

Awake()

07

{

08

Messenger.AddListener( 

"Send"

, DoSomething );

09

}

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

}

 然後是接受的地方 參數用<>存在一起。遊戲對象也可以完美的傳遞。

01

using

UnityEngine;

02

using

System.Collections;

03

04

public

class

Script : MonoBehaviour {

05

06

void

Awake()

07

{

08

Messenger.AddListener<GameObject,

int

>( 

"Send"

, DoSomething );

09

}

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自身也提供了一種發送消息的方法嗎?,用過的都知道效率也非常低下,雖然我們看不到它具體實作的源碼是如何實作的,但是我覺得原理可能也是這樣的。 歡迎和大家一起讨論與學習。