原代码参考自
http://www.unity.5helpyou.com/3695.html#more-3695
原代码作者提供了C#的基于观察者模式的事件机制,但还有许多问题,一是数据传输时装箱拆箱引起性能损耗,二是注册事件代码有错误,只能注册一个事件,没有充分使用委托来注册多个事件。
针对以上问题,我做出了修改。
原代码
订阅者模式实现

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;namespace UniEventDispatcher
{
/// <summary>
/// 定义事件分发委托
/// </summary>
public delegate void OnNotification(Notification notific);/// <summary>
///通知中心
/// </summary>
public class NotificationCenter
{/// <summary>
/// 通知中心单例
/// </summary>
private static NotificationCenter instance=null;
public static NotificationCenter Get()
{
if(instance == null){
instance = new NotificationCenter();
return instance;
}
return instance;
}
/// <summary>
/// 存储事件的字典
/// </summary>
private Dictionary<string,OnNotification> eventListeners
= new Dictionary<string, OnNotification>();/// <summary>
/// 注册事件
/// </summary>
/// <param name="eventKey">事件Key</param>
/// <param name="eventListener">事件监听器</param>
public void AddEventListener(string eventKey,OnNotification eventListener)
{
if(!eventListeners.ContainsKey(eventKey)){
eventListeners.Add(eventKey,eventListener);
}
}/// <summary>
/// 移除事件
/// </summary>
/// <param name="eventKey">事件Key</param>
public void RemoveEventListener(string eventKey)
{
if(!eventListeners.ContainsKey(eventKey))
return;eventListeners[eventKey] =null;
eventListeners.Remove(eventKey);
}/// <summary>
/// 分发事件
/// </summary>
/// <param name="eventKey">事件Key</param>
/// <param name="notific">通知</param>
public void DispatchEvent(string eventKey,Notification notific)
{
if (!eventListeners.ContainsKey(eventKey))
return;
eventListeners[eventKey](notific);
}/// <summary>
/// 分发事件
/// </summary>
/// <param name="eventKey">事件Key</param>
/// <param name="sender">发送者</param>
/// <param name="param">通知内容</param>
public void DispatchEvent(string eventKey, GameObject sender, object param)
{
if(!eventListeners.ContainsKey(eventKey))
return;
eventListeners[eventKey](new Notification(sender,param));
}/// <summary>
/// 分发事件
/// </summary>
/// <param name="eventKey">事件Key</param>
/// <param name="param">通知内容</param>
public void DispatchEvent(string eventKey,object param)
{
if(!eventListeners.ContainsKey(eventKey))
return;
eventListeners[eventKey](new Notification(param));
}/// <summary>
/// 是否存在指定事件的监听器
/// </summary>
public Boolean HasEventListener(string eventKey)
{
return eventListeners.ContainsKey(eventKey);
}
}
}

通知类

using System;
using UnityEngine;namespace UniEventDispatcher
{
public class Notification
{
/// <summary>
/// 通知发送者
/// </summary>
public GameObject sender;/// <summary>
/// 通知内容
/// 备注:在发送消息时需要装箱、解析消息时需要拆箱
/// 所以这是一个糟糕的设计,需要注意。
/// </summary>
public object param;/// <summary>
/// 构造函数
/// </summary>
/// <param name="sender">通知发送者</param>
/// <param name="param">通知内容</param>
public Notification(GameObject sender, object param)
{
this.sender = sender;
this.param = param;
}/// <summary>
/// 构造函数
/// </summary>
/// <param name="param"></param>
public Notification(object param)
{
this.sender = null;
this.param = param;
}/// <summary>
/// 实现ToString方法
/// </summary>
/// <returns></returns>
public override string ToString()
{
return string.Format("sender={0},param={1}", this.sender, this.param);
}
}
}

至于原代码的使用实例参考原网站,这里没有贴出

原代码的主要缺陷是装箱拆箱造成了性能问题

我利用泛型编程解决这一问题

using System;
using System.Collections.Generic;
using UnityEngine;namespace MyUnityEventDispatcher
{/// <summary>/// 定义事件分发委托/// </summary>public delegate void OnNotification<T>(Notification<T> notific);/// <summary>/// 通知中心/// </summary>public class NotificationCenter<T>{/// <summary>/// 通知中心单例/// </summary>public static NotificationCenter<T> _instance;public static NotificationCenter<T>  Get(){if(_instance==null){_instance = new NotificationCenter<T>();}return _instance;}/// <summary>/// 存储事件的字典/// </summary>private Dictionary<string, OnNotification<T>> eventListeners= new Dictionary<string, OnNotification<T>>();/// <summary>/// 注册事件/// </summary>/// <param name="eventKey">事件Key</param>/// <param name="eventListener">事件监听器</param>public void AddEventListener(string eventKey, OnNotification<T> eventListener){if (!eventListeners.ContainsKey(eventKey)){eventListeners.Add(eventKey, eventListener);}}/// <summary>/// 移除事件/// </summary>/// <param name="eventKey">事件Key</param>public void RemoveEventListener(string eventKey){if (!eventListeners.ContainsKey(eventKey))return;eventListeners[eventKey] = null;eventListeners.Remove(eventKey);}/// <summary>/// 分发事件/// </summary>/// <param name="eventKey">事件Key</param>/// <param name="notific">通知</param>public void DispatchEvent(string eventKey, Notification<T> notific){if (!eventListeners.ContainsKey(eventKey))return;eventListeners[eventKey](notific);}/// <summary>/// 分发事件/// </summary>/// <param name="eventKey">事件Key</param>/// <param name="sender">发送者</param>/// <param name="param">通知内容</param>public void DispatchEvent(string eventKey, GameObject sender, T param){if (!eventListeners.ContainsKey(eventKey))return;eventListeners[eventKey](new Notification<T>(sender, param));}/// <summary>/// 分发事件/// </summary>/// <param name="eventKey">事件Key</param>/// <param name="param">通知内容</param>public void DispatchEvent(string eventKey, T param){if (!eventListeners.ContainsKey(eventKey))return;eventListeners[eventKey](new Notification<T>(param));}/// <summary>/// 是否存在指定事件的监听器/// </summary>public Boolean HasEventListener(string eventKey){return eventListeners.ContainsKey(eventKey);}}
}
using System;
using System.Collections.Generic;
using UnityEngine;namespace MyUnityEventDispatcher
{public class Notification<T>{/// <summary>///通知发送者/// </summary>public GameObject sender;/// <summary>/// 限制参数类型/// </summary>public T param;/// <summary>/// 构造函数/// </summary>/// <param name="sender">通知发送者</param>/// <param name="param">通知内容</param>public Notification(GameObject sender,T param){this.sender = sender;this.param = param;}/// <summary>/// 构造函数/// </summary>/// <param name="param"></param>public Notification(T param){this.sender = null;this.param = param;}/// <summary>/// 实现ToString方法/// </summary>/// <returns></returns>public override string ToString(){return string.Format("sender={0},param={1}", this.sender, this.param);}}
}

使用实例

数据类型

public struct HealthData
{public int currentHealth;public int fullHealth;public HealthData(int h){currentHealth = h;fullHealth = h;}
}

委托生命更改事件

 /// <summary>/// 更改当前生命/// </summary>private delegate void ChangeHealth(int currentHealth,int fullHealth);private ChangeHealth changeHealth;private void Awake(){healthData = new HealthData(health) ;//事件注册changeHealth += OnHealthValueChanged;}
 /// <summary>/// 生命更改事件/// </summary>/// <param name="healthValue"></param>/// <param name="fullhealthValue"></param>public void OnHealthValueChanged(int healthValue,int fullhealthValue){//修改本地属性CurrentHealth = healthValue;FullHealth = fullhealthValue;NotificationCenter<HealthData>.Get().DispatchEvent("ChangeHealth"+this.GetHashCode(), healthData);//GetHashCode()是为获取脚本的唯一id,因为我有其他同样的脚本挂在其他角色上}

血条脚本上的监听事件

 private void Start(){Init();Register();}/// <summary>/// 事件注册/// </summary>private void Register(){NotificationCenter<HealthData>.Get().AddEventListener("ChangeHealth"+_p.Att.GetHashCode(), ChangeCurrHealth);}private void ChangeCurrHealth(Notification<HealthData> notific){HealthData hd = notific.param;ShowBlod(hd.currentHealth, hd.fullHealth);}
//实际的更改生命值事件
public void ShowBlod(int currentHealth,int fullHealth){float nowhealth = currentHealth;float fullhealth = fullHealth;float f = nowhealth / fullhealth;if(BloodSlide==null){Init();}BloodSlide.value = f;nowhealthText.text = currentHealth >= 0? currentHealth + "":"0";fullHealthText.text = fullHealth + "";}

只要使用时,限制好数据传输的类型,就不用担心装箱拆箱的问题

以上代码还有些问题,同一个key的注册的监听事件只能是一个,这其实是原代码的作者的疏漏
更改注册事件为以下代码就能解决了

/// <summary>/// 注册事件/// </summary>/// <param name="eventKey">事件Key</param>/// <param name="eventListener">事件监听器</param>public void AddEventListener(string eventKey, OnNotification<T> eventListener){if (!eventListeners.ContainsKey(eventKey)){eventListeners.Add(eventKey, eventListener);}else{eventListeners[eventKey] += eventListener;}}

如果想取消监听事件,可加入以下代码

/// <summary>/// 取消监听/// </summary>/// <param name="eventKey"></param>/// <param name="eventListener"></param>public void RemoveEventListener(string eventKey, OnNotification<T> eventListener){if (!eventListeners.ContainsKey(eventKey)){return;}else{eventListeners[eventKey] -= eventListener;}}

Unity3d基于订阅者模式实现事件机制_解决装箱拆箱问题和注册的监听事件单一问题相关推荐

  1. ROS中 Python/C++ 键盘按键监听事件

    ROS中 Python/C++ 键盘按键监听事件 这几天在肝全国智能驾驶大赛,真就挺累的:抓狂思考,手搓代码,疯狂编译,要命运行.在这趟火车上,被各种各样的问题卡住甚至卡死,就很难受好吧.至今,我深深 ...

  2. Cocos 发射和监听事件 事件派送(TypeScript)

    监听和发射事件 监听事件 事件处理是在节点(cc.Node)中完成的.对于组件,可以通过访问节点 this.node 来注册和监听事件. 事件监听函数 on 可以传第三个参数 target,用于绑定响 ...

  3. Spring Boot监听事件同步和异步使用

    废话前言: 代码环境:WIN7+IDEA+JAD1.8+Spring Boot 2.0 首先说一下我为什么使用事件,比如现在创建一个订单但是我创建成功后要给客户发送一条短信和一个邮件提醒,本身没创建订 ...

  4. 【冷知识】获取网页所有的监听事件类型、方法。请认准getEventListeners

    获取事件列表 getEventListeners(window)//获取window绑定的所有监听事件列表//----------------------------------------getEv ...

  5. 微信hash ajax,基于vue hash模式微信分享#号的解决

    看代码吧~ // 问题描述在微信中分享到朋友圈或好友时,分享出去的路由被破坏,打开分享的链接,路由中的"#"会被去掉并追加?fromTimeline之类的后缀参数,这就造成了分享出 ...

  6. 飞书监听事件(事件订阅)

    前言 飞书开发涉及到会议系统,会议需要在每个会议室的pad上进行展示,所以涉及到会议一些数据的监听,进行变更,飞书app机器人也涉及到监听用户@机器人事件来进行做出相应的动作 <1>飞书端 ...

  7. springboot13 发布和监听事件

    spring中的事件驱动模型Event(也叫发布订阅模式),是观察者模式的一个典型的应用 好处:业务解耦,在不影响原来业务逻辑的情况下,加入其它业务 场景: app上线后已实现用户注册功能,现需要在用 ...

  8. java 文本框输入监听事件_JAVA GUI 事件监听事件 详解 和 案例.

    GUI(图形用户界面) Graphical User Interface(图形用户接口) 用图形的 方式, 来显示 计算机操作的 界面, 这样更方便 更直观. CLI Command Line Use ...

  9. Android中Preference的使用以及监听事件分析

                                                                                                        ...

  10. android 常用的监听器,Android中的Keyboard监听事件

    前言 有关Keyboard监听事件,无非就是以下这几种情况: 1,操作显示或隐藏键盘: 2,判断当前键盘的状态: 3,键盘显示或隐藏后的拦截处理: 只要有使用到EditText,避免不了肯定会遇到以上 ...

最新文章

  1. 石家庄计算机职业学院张秋玉,九龙坡有什么大学
  2. Core data 框架
  3. Codeforce 697A - Pineapple Incident
  4. 在计算机领域黑箱,探究黑箱的认知革命
  5. 微软私有云分享(R2)9-SCVMM R2和SP1界面的不同
  6. 虚拟机Ubuntu复制粘贴到主机(不安装vmware-tools实现两者之间文件共享)
  7. 思科模拟器Cisco Packet Tracer菜单栏——参数选择
  8. 象棋巫师魔法学校/象棋路边摊 前1050关
  9. Could not autowire. No beans of xxx 解决办法
  10. html字体加载规则,CSS-等待字体加载,然后渲染网页
  11. 《Adobe Premiere Pro视频编辑指南(第2版)》——水银回放引擎
  12. assaasasas
  13. 今天起,属于Windows 7的时代结束了...
  14. 股票的最新和历史股息收益率查询(3)
  15. Ubuntu make 降级方法
  16. Halcon2019软件安装教程
  17. 全球各地区MODIS影像对应行列号
  18. camunda入门教程及实现原理
  19. 常用的表格检测识别方法 - 表格区域检测方法(下)
  20. Keycloak使用说明(Java Spring Boot)

热门文章

  1. 前端html与css学习笔记总结篇
  2. 软件工程实训有必要吗_软件工程实训总结
  3. 如何取消html5微信页面授权,微信授权管理在哪里?查看微信授权过的第三方应用并取消授权的方法图解...
  4. android 手势高度,克制的 Android 手势插件:滑动 Home 键
  5. aspnet mvc 中 跨域请求的处理方法
  6. CDN中加速域名和源站地址和回源HOST是什么,应该怎么填
  7. 数据中台之血缘篇:Atlas 详解
  8. PHP没有工作经验简历怎么写,没有工作经验应届生如何写简历呢?
  9. LoRa自动组网实现方案
  10. 巨星陨落 - Jim Gary