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

http://www.xuanyusong.com/archives/2165

Advanced CSharp Messenger 属于C#事件的一种。 维基百科中由详细的说明http://wiki.unity3d.com/index.php?title=Advanced_CSharp_Messenger 上周的一天刚巧有朋友问到我这一块的知识,那么我研究出来将它贴在博客中,帮助了他也帮助我自己!哇咔咔。

Advanced CSharp Messenger的特点可以将游戏对象做为参数发送。到底Advanced CSharp Messenger有什么用呢?先创建一个立方体对象,然后把Script脚本绑定在这个对象中。脚本中有一个方法叫DoSomething()。写一段简单的代码,通常我们在调用方法的时候需要这样来写。

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

private Script script;

void Awake()

{

GameObject cube = GameObject.Find("Cube");

script = cube.GetComponent<Script>();

}

void Update()

{

if(Input.GetMouseButtonDown(0))

{

script.DoSomething();

}

}

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

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

下面我们在用Advanced CSharp Messenger来实现事件的调用。按照维基百科中首先把Message.cs 和Callback.cs拷贝在你的工程中。

CallBack.cs

C#

1

2

3

4

public delegate void Callback();

public delegate void Callback<T>(T arg1);

public delegate void Callback<T, U>(T arg1, U arg2);

public delegate void Callback<T, U, V>(T arg1, U arg2, V arg3);

 Message.cs

C#

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

using System;

using System.Collections.Generic;

using UnityEngine;

static internal class Messenger {

#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.

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

#pragma warning restore 0414

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

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

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

#endregion

#region Helper methods

//Marks a certain message as permanent.

static public void MarkAsPermanent(string eventType) {

#if LOG_ALL_MESSAGES

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

#endif

permanentMessages.Add( eventType );

}

static public void Cleanup()

{

#if LOG_ALL_MESSAGES

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

#endif

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

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

bool wasFound = false;

foreach (string message in permanentMessages) {

if (pair.Key == message) {

wasFound = true;

break;

}

}

if (!wasFound)

messagesToRemove.Add( pair.Key );

}

foreach (string message in messagesToRemove) {

eventTable.Remove( message );

}

}

static public void PrintEventTable()

{

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

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

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

}

Debug.Log("\n");

}

#endregion

#region Message logging and exception throwing

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

#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 );

}

Delegate d = eventTable[eventType];

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

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

}

}

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

#if LOG_ALL_MESSAGES

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

#endif

if (eventTable.ContainsKey(eventType)) {

Delegate d = eventTable[eventType];

if (d == null) {

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

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

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

}

} else {

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

}

}

static public void OnListenerRemoved(string eventType) {

if (eventTable[eventType] == null) {

eventTable.Remove(eventType);

}

}

static public void OnBroadcasting(string eventType) {

#if REQUIRE_LISTENER

if (!eventTable.ContainsKey(eventType)) {

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

}

#endif

}

static public BroadcastException CreateBroadcastSignatureException(string eventType) {

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

}

public class BroadcastException : Exception {

public BroadcastException(string msg)

: base(msg) {

}

}

public class ListenerException : Exception {

public ListenerException(string msg)

: base(msg) {

}

}

#endregion

#region AddListener

//No parameters

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

OnListenerAdding(eventType, handler);

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

}

//Single parameter

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

OnListenerAdding(eventType, handler);

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

}

//Two parameters

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

OnListenerAdding(eventType, handler);

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

}

//Three parameters

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

OnListenerAdding(eventType, handler);

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

}

#endregion

#region RemoveListener

//No parameters

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

OnListenerRemoving(eventType, handler);

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

OnListenerRemoved(eventType);

}

//Single parameter

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

OnListenerRemoving(eventType, handler);

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

OnListenerRemoved(eventType);

}

//Two parameters

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

OnListenerRemoving(eventType, handler);

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

OnListenerRemoved(eventType);

}

//Three parameters

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

OnListenerRemoving(eventType, handler);

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

OnListenerRemoved(eventType);

}

#endregion

#region Broadcast

//No parameters

static public void Broadcast(string eventType) {

#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);

Delegate d;

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

Callback callback = d as Callback;

if (callback != null) {

callback();

} else {

throw CreateBroadcastSignatureException(eventType);

}

}

}

//Single parameter

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

#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);

Delegate d;

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

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

if (callback != null) {

callback(arg1);

} else {

throw CreateBroadcastSignatureException(eventType);

}

}

}

//Two parameters

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

#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);

Delegate d;

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

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

if (callback != null) {

callback(arg1, arg2);

} else {

throw CreateBroadcastSignatureException(eventType);

}

}

}

//Three parameters

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

#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);

Delegate d;

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

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

if (callback != null) {

callback(arg1, arg2, arg3);

} else {

throw CreateBroadcastSignatureException(eventType);

}

}

}

#endregion

}

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

public sealed class MessengerHelper : MonoBehaviour {

void Awake ()

{

DontDestroyOnLoad(gameObject);

}

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

public void OnDisable() {

Messenger.Cleanup();

}

}

 然后就可以开始使用了,Messager.Broadcast()这样就好比我们发送了一条广播。

C#

1

2

3

4

5

6

7

void Update()

{

if(Input.GetMouseButtonDown(0))

{

Messenger.Broadcast("Send");

}

}

 在需要这条广播的类中来接受它,同样是刚刚说的Script类。接受广播的标志是 Messager.AddListener()参数1表示广播的名称,参数2表示广播所调用的方法。

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

using UnityEngine;

using System.Collections;

public class Script : MonoBehaviour {

void Awake()

{

Messenger.AddListener( "Send", DoSomething );

}

public void DoSomething()

{

Debug.Log("DoSomething");

}

}

 这样一来,只要发送名称为”Send”的方法,就可以在别的类中接收它了。

我们在说说如何通过广播来传递参数,这也是那天那个哥们主要问我的问题。(其实是维基百科上写的不是特别特别的清楚,那哥们误解了)在Callback中可以看出参数最多可以是三个,参数的类型是任意类型,也就是说我们不仅能传递 int float bool 还能传递gameObject类型。

如下所示,发送广播的时候传递了两个参数,参数1是一个游戏对象,参数2是一个int数值。

C#

1

2

3

4

5

6

7

8

void Update()

{

if(Input.GetMouseButtonDown(0))

{

GameObject cube = GameObject.Find("Cube");

Messenger.Broadcast<GameObject,int>("Send",cube,1980);

}

}

 然后是接受的地方 参数用<>存在一起。游戏对象也可以完美的传递。

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

using UnityEngine;

using System.Collections;

public class Script : MonoBehaviour {

void Awake()

{

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

}

public void DoSomething(GameObject obj,int i)

{

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

}

}

如果传递一个参数<T>

两个参数<T,T>

三个参数<T,T,T>   

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

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

时间: 2024-10-07 05:29:44

【转】Unity3D研究院之通过C#使用Advanced CSharp Messenger(五十)的相关文章

ZT:Unity3D研究院之使用Animation编辑器编辑动画(五十四)

原文地址:http://www.xuanyusong.com/archives/2246#comments 原文作者: 雨松MOMO 2013年04月16日 于 雨松MOMO程序研究院 发表 Unity提供了Animation编辑器,它可以为我们编辑物理动画.举个例子比如场景中有一个来回摇动的秋千,这个秋千在项目中完全只起到衬托作用,它不会与别的游戏对象有任何交互.如果这个秋千也用代码来写控制它来回摇动,会感觉小题大做.此时完全可以使用Animation编辑器来完成.但是它目前还不能编辑 FK

Unity3D研究院之Jenkins的使用(七十八)

长夜漫漫无心睡眠,来一篇嘿嘿.我相信如果已经用Shell脚本完成IOS和Android打包的朋友一定需要Jenkins 怎么才能让策划打包ipa和apk?怎么才能彻底省去程序的时间,只要在同一局域网内不需要unity的开发环境,只要它有浏览器,它就能打包Jenkins无疑是最佳选择. Unity3D研究院之IOS全自动编辑framework.plist.oc代码(六十七) Unity3D研究院之IOS全自动打包生成ipa(六十八) Unity3D研究院之Android全自动打包生成apk(六十九

Unity3D研究院之IOS本地消息通知LocalNotification的使用

原地址:http://www.xuanyusong.com/archives/2632   现在的游戏里一般都会有本地消息,比如每天定时12点或者下午6点告诉玩家进入游戏领取体力.这种东西没必要服务器去推送,客户端就可以完成.Unity里面提供了本地任务的功能但是只有IOS上才支持,开始我有点不解为什么Android上不支持,当我把Android的本地通知做完后,我才明白.IOS源生的API中就支持固定时间循环推送,而Android上需要自己开启一个Services,启动一个AlarmManag

Unity3D研究院之IOS全自动打包生成ipa

接着上一篇文章, 自动生成framework,这篇文章我把shell自动化打包ipa整理了一下,希望大家喜欢,嘿嘿.. 建议大家先看一下上一篇文章.http://www.xuanyusong.com/archives/2720 首先我们要先搞清楚nity全自动打包的重要步骤. 1.自动生成xcode工程. 2.自动生成.ipa和dsym文件. 3.上传appstore(本篇略) 首先我们在做渠道包时,一般每个渠道都有自己一些特殊的需求,比如 游戏名子 .游戏图标.SDK.等等.那么我在在做自动化

Unity3D研究院之IOS全自动编辑framework、plist、oc代码

Unity打IOS时会先生成一个Xcode工程,如果你需要增加一些第三方的framework那么需要手动一条一条的添加,这太烦了..而且可能你还需要修改Plist文件,甚至还可能要修改unity自动生成的oc代码,每次打包都要修改的话,那太累了..这篇文章就是全自动打包的第一步..建议使用XUPorter,我在它的基础上拓展了两个类,一个用来修改plist,一个用来修改unity生成出来的OC代码.文章的最后我会给出代码.. 那么我用一个比较变态的SDK举个例子ShareSDK,它就需要自动添加

Unity3D研究院之打开Activity与调用JAVA代码传递参数

原地址:http://www.xuanyusong.com/archives/667    Unity for Android 比较特殊,Unity for IOS 打包是将XCODE工程直接交给开发者,开发者可以在工程的基础上继续添加新的视图,最后由开发者自行打包生成IPA包,发布程序.而Unity for Android打包直接生成APK包,等于说源代码开发者是看不到的,但是Unity的自身确实有些局限,针对Android平台我们需要学习如何在Unity中调用Android的JAVA代码.本

(转)Unity3D研究院之将场景导出XML或JSON或二进制并且解析还原场景

自:http://www.xuanyusong.com/archives/1919 导出Unity场景的所有游戏对象信息,一种是XML一种是JSON.本篇文章我们把游戏场景中游戏对象的.旋转.缩放.平移与Prefab的名称导出在XML与JSON中.然后解析刚刚导出的XML或JSON通过脚本把导出的游戏场景还原.在Unity官网上下载随便下载一个demo Project,如下图所示这是我刚刚在官网上下载的一个范例程序.           接着将层次视图中的所有游戏对象都封装成Prefab保存在资

Unity3D研究院之与Android相互传递消息

原地址:http://www.xuanyusong.com/archives/676 上一篇文章我们学习了Unity向Android发送消息,如果Android又能给Unity回馈消息那么这就玩美了.恰好Unity for Andoid 和 IOS一样都是可以相互与Unity发送与接收消息,这篇文章MOMO就和大家好好聊聊Android向Unity发送消息的方法.在读本片博文之前,建议大家读一下我上一篇文章Unity3D研究院之打开Activity与调用JAVA代码传递参数(十八),有关数据打包

Unity3D研究院之Assetbundle的实战(六十三)

1.创建Assetbundle          无论是模型资源还是UI资源,最好是先把他们放在Prefab中,然后在做成Assetbundle.我们以模型来举例,Assetbundle中可以放一个模型.也可以放多个模型,它是非常灵活了那么最需要考虑的就是模型空间占用的问题. 比如我们有两个完全一样的模型,但是他们身上绑定的脚本不一样,此时需要把这两个模型放在两个不同Prefab中.如下图所示,我们分别对这两个Prefab打包,我们可以清晰的看到两个相同的Prefab打包在一起只占1M空间,而将