原代码参考自
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;
}
}