该博客,只为解析,解析,解析,已经整理好,已经整理好,已经整理好。代码核心原理套用网上最流行的那一套,也是最常用游戏开发适用的消息机制。这里面加上自己的一些优化,极大的修正(哈哈),实测,没问题。万一要是出现问题,欢迎童鞋可以留言给我修正。

有童鞋可能会好奇,unity里面不是有自己的一套消息发送, 例如什么SendMessage,这...这个几乎是不能用的。

为啥不能用,看看以下是网上给的解释,自己玩玩demo还是可以用,但是实际开发,是几乎不能用的。

I:它实现的是一种伪监听者模式,利用的是反射机制。

II:SendMessage效率不高,因为每次调用的时候都会去遍历检测自身或者子节点上要调用的方法。

III:需要知道响应事件的物件,还需要一个响应函数的函数名字符串作为参数,如果我们有多个物件都要响应某个事件怎么办呢,或者我们不知道有哪些物件要响应事件怎么办呢。(前面两句话比较抽象,这句话总能看的懂吧)

 (如有不理解委托,事件可以参考我的这篇帖子:https://www.cnblogs.com/u3ddjw/p/9920994.html)

1.思考

消息发送机制,也可以叫做观察者设计模式(应该是这样的)。

通俗易懂点讲,就是 一个物体发出消息,另外一个,或者几个物体可以同时接收到这一消息并作出各自不同的行为(反馈,处理)。

那么,首先,我们想到,需要什么?

I: 我们需要的是消息(实例),发送者。 消息(实例)+发送者=我们需要的消息,就能够处理任何消息。

II:怎么把这个消息发送出去(消息处理中心)。

III:发送者发送(分发)消息的行为

IV:接收消息。

换一种说法:发布-订阅模式。举例就是定报纸,你跟邮局定了报纸,邮局就会在指定时间把报纸发下来给你;中间如果你不需要报纸了,那么你就取消这个订阅,邮局就不会发给你了。

图解:

  

2.解析

1)具体消息

  1. public class Notification
  2. {
  3. /// <summary>
  4. /// 发送者
  5. /// </summary>
  6. public GameObject sender;
  7.  
  8. /// <summary>
  9. /// 消息内容
  10. /// </summary>
  11. public EventArgs param;
  12.  
  13. /// <summary>
  14. /// 构造函数 (初始化)
  15. /// </summary>
  16. ///<param name="sender">通知发送者
  17. ///<param name="param">通知内容
  18. public Notification(GameObject sender, EventArgs param)
  19. {
  20. this.sender = sender;
  21. this.param = param;
  22. }
        
  23. public Notification()
  24. {
  25.  
  26. }
  27.  
  28. /// <summary>
  29. /// 构造函数
  30. /// </summary>
  31. ///<param name="param">
  32. public Notification(EventArgs param)
  33. {
  34. this.sender = null;
  35. this.param = param;
  36. }
  37. }
  38. /// <summary>
  39. /// 传递的消息,这个是消息类中的具体消息种类 类
  40. /// </summary>
  41. public class EventArgsTest : EventArgs
  42. {
  43. public int id;
  44. public string name;
  45. }
  1. Notification是一个稍微抽象一点的消息类,要传递一个消息(类),我前面说到了,肯定是需要知道具体发送者和具体消息类的。
    而具体消息类,就是后面的EventArgsTest,这个是继承于System.EventArgs,该类是自定义类,看到后面,可能会理解为什么这样继承。

 2)声明一个消息的委托

  

  1. public delegate void NotificationDelegate(Notification notific);

  声明一个委托传递上面所说的消息类的委托,这边通俗一点来讲就是:声明一个可以传递Notification 参数的方法。至于委托的用法这里就不详诉了。

 3)消息处理中心

  1. public class NotificationCenter
  2. {
  3. private static NotificationCenter instance = null;
  4. public static NotificationCenter Get()
  5. {
  6. if (instance == null)
  7. {
  8. instance = new NotificationCenter();
  9. return instance;
  10. }
  11. return instance;
  12. }
  13.  
  14. private Dictionary<uint, NotificationDelegate> eventListeners
  15. = new Dictionary<uint, NotificationDelegate>();
  16. public void AddEventListener(uint eventKey, NotificationDelegate listener)
  17. {
  18. if (!HasEventListener(eventKey))
  19. {
  20. NotificationDelegate del = null; //定义方法
  21. eventListeners[eventKey] = del;// 给委托变量赋值
  22. }
  23. eventListeners[eventKey] += listener; //注册接收者的监听
  24. }
  25. public void RemoveEventListener(uint eventKey,NotificationDelegate listener)
  26. {
  27. if (!HasEventListener(eventKey))
  28. return;
  29. eventListeners[eventKey] -= listener;
  30. if (eventListeners[eventKey] == null)
  31. {
  32. RemoveEventListener(eventKey);
  33. }
  34. }
  35. public void RemoveEventListener(uint eventKey)
  36. {
  37. eventListeners.Remove(eventKey);
  38. }
  39.  
  40. /// <summary>
  41. /// 分发事件,不需要知道发送者的情况
  42. /// </summary>
  43. /// <param name="eventKey"></param>
  44. /// <param name="notific"></param>
  45. public void PostDispatchEvent(uint eventKey, Notification notific)
  46. {
  47. if (!HasEventListener(eventKey))
  48. return;
  49. // eventListeners[eventKey].Invoke(notific);
  50. eventListeners[eventKey](notific);
  51. }
  52.  
  53. /// <summary>
  54. /// 分发事件,需要知道发送者,具体消息的情况
  55. /// </summary>
  56. ///<param name="eventKey">事件Key
  57. ///<param name="sender">发送者
  58. ///<param name="param">通知内容
  59. public void PostDispatchEvent(uint eventKey, GameObject sender, EventArgs param)
  60. {
  61. if (!HasEventListener(eventKey))
  62. return;
  63. eventListeners[eventKey](new Notification(sender, param));
  64. }
  65. public void PostDispatchEvent(uint eventKey)
  66. {
  67. if (!HasEventListener(eventKey))
  68. return;
  69. eventListeners[eventKey](new Notification());
  70. }
  71.  
  72. /// <summary>
  73. /// 分发事件,不需要知道任何,只需要知道发送过来消息了
  74. /// </summary>
  75. ///<param name="eventKey">事件Key
  76. ///<param name="param">通知内容
  77. public void PostDispatchEvent(uint eventKey, EventArgs param)
  78. {
  79. if (!HasEventListener(eventKey))
  80. return;
  81. eventListeners[eventKey](new Notification(param));
  82. }
  83.  
  84. /// <summary>
  85. /// 是否存在指定事件的监听器
  86. /// </summary>
  87. public bool HasEventListener(uint eventKey)
  88. {
  89. return eventListeners.ContainsKey(eventKey);
  90. }
  91. }

该消息机制的核心,难点也就是在这里了。

首先,既然是消息处理中心,肯定是需要一个存放传递消息(上面那个声明的委托)的容器,于是声明一个

  1. private Dictionary<uint, OnNotification> eventListeners
  2. = new Dictionary<uint, OnNotification>();
  1. 增加,移除 传递消息(上面那个声明的委托),不就是以下代码,需要注意的是
  1. eventListeners[eventKey] -= listener;//取消接收者的监听
  2. eventListeners.Remove(eventKey);//移除存放在在eventListeners为eventKey的传递消息(上面那个委托)
  1. if (!HasEventListener(eventKey))
  2. {
  3. eventListeners[eventKey] = listener; //注册接收者的监听
  4. }
  5. else
  6. {
  7. eventListeners[eventKey] += listener; //注册接收者的监听,这个用法,是委托的一种机制,不理解的自己去百度看看委托咋回事。
  8. }
  1. 这样,如何存储消息做完了。

4) 发送者发送(分发)消息的行为

  1. /// <summary>
  2. /// 消息类型,枚举列出,调用时需要强转为uint
  3. /// </summary>
  4. public enum ENotificationMsgType // 消息发送的枚举值,应该转为uint型
  5. {
  6. ENull = , //Test
    ELoadResProgress = ,
  7. }

以上代码,写枚举,纯是为了提高代码可读性及可维护性,C#中多写枚举,少写那种莫名其妙的 int变量,真心感谢第一家公司对我的影响,保持良好的代码可读性。

  1.    EventArgsTest args = new EventArgsTest();
  2. args.id = ;
  3. args.name = "我是Test发送的 name 消息哦";
  4. NotificationCenter.Get().PostDispatchEvent((uint)ENotificationMsgType.ENull, args);
  5. // NotificationCenter.Get().PostDispatchEvent((uint)ENotificationMsgType.ENull); //我就是通知,不发送具体啥消息,也是可以的哦

这边需要理解的是 PostDispatchEvent,这个方法,这边我 写了三重重载,因为发送消息分三种情况,如注释那样

{

只需要通知发送,不需要知道发送的具体消息类型,也不需要发送者。

只需要发送具体消息类型,不需要发送者。

需要发送具体消息类型,需要发送者。

}

5)接收消息

  void Awake() { NotificationCenter.Get().AddEventListener((uint)ENotificationMsgType.ENull, UpdateTest); } void OnDestroy() { NotificationCenter.Get().RemoveEventListener((uint)ENotificationMsgType.ENull, UpdateTest); } void UpdateTest(Notification e) { EventArgsTest args = e.param as EventArgsTest; if (args != null) { string strName = args.name; int strId = args.id; } }

  可能你会奇怪,注册事件和移除事件为什么这样写。这是一种标准写法。

   写初始(Start),结束(OnDestroy),使得每个消息拥有一个自己的生命周期。

3.另外一种版本

  (2018年12月15日,补充更新,推荐使用这个版本,但是也上述比较本质都是基本一样的)

  该版本来自siki学院:给出视频链接 http://www.sikiedu.com/my/course/304

  

  该版本优势:①极大减少代码量。

        ②极大节省人力。

上述版本传递消息,自定义 EventArgs的子类,遇到特殊类型,声明的类的类型很多,增大了代码量,这是极大的弊端。

该版本也是我无意间看到的,感觉很不错,特来补充。

最终,如若有讲述不清,错误之处,欢迎指正。

Unity 消息发送机制 解析的更多相关文章

  1. Objective-C中的消息发送总结

    关于OC中的消息发送的实现,在去年也看过一次,当时有点不太理解,但是今年再看却很容易理解. 我想这跟知识体系的构建有关,如果你不认识有砖.水泥等这些建筑的基本组成部分,那么我们应该很难理解建筑是怎么建 ...

  2. Android HandlerThread 消息循环机制之源代码解析

    关于 HandlerThread 这个类.可能有些人眼睛一瞟,手指放在键盘上,然后就是一阵狂敲.立即就能敲出一段段华丽的代码: HandlerThread handlerThread = new Ha ...

  3. 【Azure Service Bus】 Service Bus如何确保消息发送成功,发送端是否有Ack机制 

    问题描述 Service Bus如何确保消息发送成功,发送端是否有Ack机制(是否有回调API告诉发送端,服务端已经收到消息)?根据对.NET发送Service Bus消息代码的分析,发送方法queu ...

  4. Objective-C 消息发送与转发机制原理(摘)

    八面玲珑的 objc_msgSend 此函数是消息发送必经之路,但只要一提 objc_msgSend,都会说它的伪代码如下或类似的逻辑,反正就是获取 IMP 并调用: id objc_msgSend( ...

  5. iOS 消息转发机制

    这篇博客的前置知识点是 OC 的消息传递机制,如果你对此还不了解,请先学习之,再来看这篇.这篇博客我尝试用口语的方式像讲述 PPT 一样给大家讲述这个知识点. 我们来思考一个问题,如果对象在收到无法解 ...

  6. iOS 消息发送与转发详解

    Objective-C 是一门动态语言,它将很多静态语言在编译和链接时期做的事情,放到了运行时来处理.之所以能具备这种特性,离不开 Runtime 这个库.Runtime 很好的解决了如何在运行时期找 ...

  7. ActiveMQ(2)---ActiveMQ原理分析之消息发送

    持久化消息和非持久化消息的发送策略 消息同步发送和异步发送 ActiveMQ支持同步.异步两种发送模式将消息发送到broker上.同步发送过程中,发送者发送一条消息会阻塞直到broker反馈一个确认消 ...

  8. 【XMPP】Smack源码之消息接收与解析

    XmpPullParser 鉴于xmpp协议都是以xml格式来传输,因此源码中解析协议都是用到XmpPullParser来解析xml XmpPullParser很简单,先简单介绍几个比较常用的方法 / ...

  9. C# Socket异步实现消息发送--附带源码

    前言 看了一百遍,不如动手写一遍. Socket这块使用不是特别熟悉,之前实现是公司有对应源码改改能用. 但是不理解实现的过程和步骤,然后最近有时间自己写个demo实现看看,熟悉熟悉Socket. 网 ...

随机推荐

  1. java学习笔记 --- 抽象类

    一.抽象类 (1)定义: 把多个共性的东西提取到一个类中,这是继承的做法. 但是呢,这多个共性的东西,在有些时候,方法声明一样,但是方法体. 也就是说,方法声明一样,但是每个具体的对象在具体实现的时候 ...

  2. java学习笔记 --- 条件,循环语句

    一.三元运算符 A:格式    比较表达式?表达式1:表达式2;   B:执行流程:    首先计算比较表达式的值,看是true还是false.    如果是true,表达式1就是结果.    如果是 ...

  3. 免安装版Tomcat配置内存

    去安装目录下 找 bin 目录找到这个文件 catalina.bat在文件的头部加上 set JAVA_OPTS=-Xms512m -Xmx512m -Xss1024k具体大小自己调整

  4. centos mail使用外部SMTP发送邮件

    1.安装mailx yum install mailx -y 安装好后,编辑配置文件 mailx -V 12.4 7/29/08  <<mailx的版本号 rpm -qc mailx /e ...

  5. Jmeter-线程组

    1.Sampler 取样器(Sampler)是性能测试中向服务器发送请求,记录响应信息,记录响应时间的最小单元,JMeter 原生支持多种不同的sampler , 如 HTTP Request Sam ...

  6. 关于微信小程序图片失真的解决方案

    今天来说一说 关于微信小程序的图片失真问题的解决,微信小程序的image标签要设置其宽高,不然图片若宽高过大会撑开原始图片大小的区域:如下 但是宽高设置固定了会导致有些图片和规定显示图片大小的比例不一 ...

  7. js求三位数的和

    例如输入508就输出5+0+8的和13: <!DOCTYPE html> <html lang="en"> <head> <meta ch ...

  8. 【Egret】Native版本 视频播放器(android)

    前段时间,领导说客户要一个平板版本的视频播放器,把我们做的一些视频资源放进去,要是本地的:我们部门又没有app开发程序员,正好又前段我在实验egret的app打包功能,就说用egret做(ps:本来想 ...

  9. 【原创】Octovis在Ubuntu16.04下运行出现core dump的解决方案

    本人SLAM研究新手,使用系统为Ubuntu16.04.本文原址:http://www.cnblogs.com/hitlrk/p/6667253.html 在学习SLAM的过程中,使用Octomap进 ...

  10. java开发中经典的三大框架SSH

    首先我们要明白什么是框架为什么用?相信一开始学习编程的时候都会听到什么.什么框架之类的:首先框架是一个软件半成品,都会预先实现一些通用功能,使用框架直接应用这些通用功能而不用重新实现,所以大多数企业都 ...