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

原代码参考自
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;
            }
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值