Unity:事件总线优化

单例模式

    /// <summary>
    /// 不需要管理对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class BaseManager<T> where T : new()
    {
        private static T instance;
        protected static readonly object m_staticSyncRoot = new object();

        public static T Instance
        {
            get
            {
                lock (m_staticSyncRoot)
                {
                    if (instance == null)
                    {
                        instance = new T();
                    }
                }
                return instance;
            }
        }
    }

消息抽象类

    public class EventManager:BaseManager<EventManager>
    {
        #region 单例

        /// <summary>
        /// 事件总线实例
        /// </summary>
/*        private static EventManager entity = null;

        protected static readonly object m_staticSyncRoot = new object();

        public static EventManager Instance
        {
            get
            {
                if (null == entity)
                {
                    lock (m_staticSyncRoot)
                    {
                        if (null == entity)
                        {
                            entity = new EventManager();
                        }
                    }
                }
                return entity;
            }
        }
*/
        #endregion 单例

        /// <summary>
        /// 事件链
        /// </summary>
        private Dictionary<Enum, Action<BaseEventArgs>> listeners = null;//使用.Net 默认的 多播委托 Action

        /// <summary>
        /// 是否中断事件分发,默认不中断
        /// </summary>
        public static bool Interrupt { get; internal set; } = false;

        public EventManager()
        {
            this.Capacity = 60;
            InitEvent();
        }

        /// <summary>
        /// 得到指定枚举项的所有事件链
        /// </summary>
        /// <param name="_type">指定枚举项</param>
        /// <returns>事件链</returns>
        private Action<BaseEventArgs> GetEventList(Enum _type)
        {
            if (!listeners.ContainsKey(_type))
            {
                listeners.Add(_type, default(Action<BaseEventArgs>));
            }
            return listeners[_type];
        }

        /// <summary>
        /// 添加事件
        /// </summary>
        /// <param name="_type">指定类型</param>
        /// <param name="action">指定事件</param>
        private void AddEvent(Enum _type, Action<BaseEventArgs> action)
        {
            Action<BaseEventArgs> actions = GetEventList(_type);
            if (null != action)
            {
                Delegate[] delegates = actions?.GetInvocationList();//
                if (null != delegates)
                {
                    if (!Array.Exists(delegates, v => v == (Delegate)action))
                    {
                        actions += action;
                    }
                    else
                    {
                        UnityEngine.Debug.LogWarningFormat("重复的事件监听:{0}.{1}", _type.GetType().Name, _type.ToString());
                    }
                }
                else
                {
                    actions = action;
                }
                listeners[_type] = actions;
            }
            else
            {
                UnityEngine.Debug.LogWarning("指定添加的事件为 null !");
            }
        }

        /// <summary>
        /// 执行事件
        /// </summary>
        /// <param name="_type">指定事件类型</param>
        /// <param name="args">事件参数</param>
        private void CallEvent(BaseEventArgs args)
        {
            Action<BaseEventArgs> actions = GetEventList(args.EventType);//获取事件链
            actions?.Invoke(args);//触发所有已经注册到该事件的委托
            Recycle(args); //回收第一次的事件参数
        }

        /// <summary>
        /// 删除指定的事件
        /// </summary>
        /// <param name="_type">指定类型</param>
        /// <param name="action">指定的事件</param>
        private void DelEvent(Enum _type, Action<BaseEventArgs> action)
        {
            if (null != action)
            {
                Action<BaseEventArgs> actions = GetEventList(_type);//获取事件链
                if (null != action)
                {
                    actions -= action;//移除指定的委托监听
                }
                listeners[_type] = actions;
            }
            else
            {
                UnityEngine.Debug.LogWarning("指定移除的事件为 null !");
            }
        }

        /// <summary>
        /// 删除指定的事件(写的这么多就知道不指定事件枚举不被建议了)不知道类型的情况下
        /// </summary>
        /// <param name="action">指定的事件</param>
        private void DelEvent(Action<BaseEventArgs> action)
        {
            if (null != action)
            {
                List<Action<BaseEventArgs>> actionsArr = new List<Action<BaseEventArgs>>(listeners.Values);//所有事件链
                List<Enum> eventTypeArr = new List<Enum>(listeners.Keys);//所有事件类型
                //定义一个查找条件
                Predicate<Action<BaseEventArgs>> predicate = v =>
                {
                    Delegate[] ds = v?.GetInvocationList();//返回一个包含了委托中所有方法的数组 delegates
                    return (null != ds && Array.Exists(ds, d => d == (Delegate)action));
                };
                int index = actionsArr.FindIndex(predicate);//查找索引
                if (index != -1)
                {
                    DelEvent(eventTypeArr[index], action);//删除指定事件类型的指定事件监听
                }
                else
                {
                    UnityEngine.Debug.LogWarning("移除的事件未曾注册过 !");
                }
            }
            else
            {
                UnityEngine.Debug.LogWarning("指定移除的事件为 null !");
            }
        }

        /// <summary>
        /// 删除指定事件类型的所有事件
        /// </summary>
        /// <param name="eventType">指定的事件类型</param>
        private void DelEvent(Enum eventType)
        {
            listeners.Remove(eventType);
        }

        /// <summary>
        /// 初始化事件链
        /// </summary>
        private void InitEvent()
        {
            recycled = new Dictionary<Type, Queue<BaseEventArgs>>();
            listeners = new Dictionary<Enum, Action<BaseEventArgs>>();
        }

        #region//--------------------------StaticFunction-------------------------------

        /// <summary>
        /// 添加事件监听
        /// </summary>
        /// <param name="_type">事件类型</param>
        /// <param name="action">事件</param>
        public static void AddListener(Enum _type, Action<BaseEventArgs> action)
        {
            Instance.AddEvent(_type, action);
        }

        /// <summary>
        /// 事件分发
        /// </summary>
        /// <param name="_type">事件类型</param>
        /// <param name="args">事件参数</param>
        public static void Invoke(BaseEventArgs args)
        {
            if (Interrupt)
            {
                return;
            }
            Instance?.CallEvent(args);
        }

        /// <summary>
        /// 指定类型事件的指定事件监听
        /// </summary>
        /// <param name="_type">事件类型</param>
        /// <param name="action">事件</param>
        public static void DelListener(Enum _type, Action<BaseEventArgs> action)
        {
            Instance?.DelEvent(_type, action);
        }

        /// <summary>
        /// 移除所有类型事件的指定事件监听
        /// </summary>
        /// <param name="_type">事件类型</param>
        /// <param name="action">事件</param>
        public static void DelListener(Action<BaseEventArgs> action)
        {
            Instance?.DelEvent(action);
        }

        /// <summary>
        /// 移除指定类型的所有事件监听
        /// </summary>
        /// <param name="_type">事件类型</param>
        public static void DelListener(Enum _type)
        {
            Instance?.DelEvent(_type);
        }

        /// <summary>
        /// 移除所有事件
        /// </summary>
        public static void RemoveAllListener()
        {
            Instance?.InitEvent();
        }

        #endregion//--------------------------StaticFunction-------------------------------

        #region  缓冲池
        internal Dictionary<Type, Queue<BaseEventArgs>> recycled; //缓存队列
        private int Capacity { get; set; } //池子有多大

        /// <summary>
        /// 分配指定参数类型的实例
        /// </summary>
        /// <typeparam name="T">指定类型</typeparam>
        /// <returns>分配的实例</returns>
        public static T Allocate<T>() where T : BaseEventArgs, new()//分配
        {
            Type type = typeof(T);//获取实际类型
            Queue<BaseEventArgs> args;
            if (Instance.recycled.TryGetValue(type, out args))//获取队列
            {
                if (null != args && args.Count == Instance.Capacity)//消息队列不等于空且等于容量
                {
                    T arg = args.Dequeue() as T;
                    arg.Dispose();//事件回收
                    return arg;
                }
            }
            return new T() as T;//没有且没到容量
        }

        /// <summary>
        /// 回收参数类型的实例
        /// </summary>
        /// <param name="target">指定的实例</param>
        private void Recycle(BaseEventArgs target)
        {
            Type type = target.GetType();
            Queue<BaseEventArgs> args;
            if (!Instance.recycled.TryGetValue(type, out args))//缓存中没有则新建队列,并获取类型队列
            {
                args = new Queue<BaseEventArgs>();
            }
            if (args.Count < Instance.Capacity && !args.Contains(target))//缓存中没有且没有超过容量
            {
                args.Enqueue(target);//进队
            }
            else//回收释放
            {
                target.Dispose();
            }
            Instance.recycled[type] = args;//类型添加消息队列
        }

        #endregion
    }

    #region 消息拓展方法

    public static class EventManagerEx
    {
        /// <summary>
        /// 使用该参数类型的实例分发事件
        /// 这个方法允许任何 BaseEventArgs 的实例直接调用 Invoke 方法,从而简化事件的触发过程。
        /// </summary>
        /// <param name="args">参数实例</param>
        public static void Invoke(this BaseEventArgs args)
        {
            EventManager.Invoke(args);
        }
    }
}

使用案例
消息定义,可以携带什么可以在拓展


using AN.Framework;
using UnityEngine;
/// <summary>
/// 笔或者鼠标事件类型
/// </summary>
public enum StylusEvent
{
    Enter,
    Press,
    Release,
    Exit
}
/// <summary>
/// 鼠标事件参数类
/// </summary>
public class StylusEventArgs : BaseEventArgs
{
    /// <summary>
    /// 光标下的游戏对象
    /// </summary>
    public GameObject Selected { private set; get; }
    /// <summary>
    /// 光标与游戏对象的触碰点
    /// </summary>
    public RaycastHit HitInfo { private set; get; } = default(RaycastHit);
    /// <summary>
    /// 鼠标或触笔的按键ID
    /// </summary>
    public int ButtonID { private set; get; }
    /// <summary>
    /// 鼠标或者触笔事件
    /// </summary>
    /// <param name="_t">事件类型</param>
    /// <param name="_sender">事件发送者</param>
    /// <param name="_selected">被选中的游戏对象</param>
    /// <param name="_buttonID">按键编号</param>
    /// <param name="_hit">碰撞点信息</param>
    public StylusEventArgs Config(StylusEvent _t, GameObject _sender, GameObject _selected, int _buttonID = -1, RaycastHit _hit = default(RaycastHit))
    {
        base.Config(_t, _sender);
        Selected = _selected;
        ButtonID = _buttonID;
        HitInfo = _hit;
        return this;

    }

    public override void Dispose()
    {
        base.Dispose();
        Selected = null;
        ButtonID = -1;
        HitInfo = default(RaycastHit);
    }
}

事件注册

using AN.Framework; 
using UnityEngine;

public class StylusEventReceive : MonoBehaviour
{
    void Awake()
    {
        EventManager.AddListener(StylusEvent.Enter, OnPointEnter);
        EventManager.AddListener(StylusEvent.Exit, OnPointExit);
        EventManager.AddListener(StylusEvent.Press, OnPointPress);
        EventManager.AddListener(StylusEvent.Release, OnPointRelease);

        #region 以下为多种应用场景的演示
        EventManager.AddListener(StylusEvent.Enter, OnPointEnter); //演示重复添加
        EventManager.AddListener(StylusEvent.Exit, OnPointExitAddition); //演示叠加添加
        EventManager.AddListener(StylusEvent.Press, OnPointPressAddition); //叠加事件,用于演示DelLitener(Enum _type)
        EventManager.DelListener(StylusEvent.Exit, NoRegisterEvent); //演示移除未注册的事件
        EventManager.DelListener(NoRegisterEventAndNoEventTypeAssigned); //演示不指定EventType移除未注册的事件
        #endregion
    }

    private void NoRegisterEventAndNoEventTypeAssigned(BaseEventArgs obj)
    {

    }

    private void OnPointPressAddition(BaseEventArgs obj)
    {
        print("OnPointPressAddition ---Press [D] will remove it as all of the press event removed");
    }

    private void NoRegisterEvent(BaseEventArgs obj)
    {
    }

    private void OnPointExitAddition(BaseEventArgs obj)
    {
        print("OnPointExitAddition ~ Press 【Q】 Remove This Listener");
    }

    private void OnPointPress(BaseEventArgs obj)
    {
        StylusEventArgs args = obj as StylusEventArgs;
        SetColor(args.Selected, Color.green);
        Debug.LogFormat("鼠标按下--selected:{0},buttonID:{1}", args.Selected.name, args.ButtonID);

    }

    private void OnPointRelease(BaseEventArgs obj)
    {
        StylusEventArgs args = obj as StylusEventArgs;
        SetColor(args.Selected, Color.white);
        Debug.LogFormat("鼠标释放--selected:{0},buttonID:{1}", args.Selected.name, args.ButtonID);
    }
    private void OnPointExit(BaseEventArgs obj)
    {
        StylusEventArgs args = obj as StylusEventArgs;
        SetColor(args.Selected, Color.white);
        Debug.LogFormat("光标退出--selected:{0},buttonID:{1}", args.Selected.name, args.ButtonID);
    }

    private void OnPointEnter(BaseEventArgs obj)
    {
        StylusEventArgs args = obj as StylusEventArgs;
        SetColor(args.Selected, Color.red);
        Debug.LogFormat("光标进入--selected:{0},buttonID:{1}---Press 【R】 Remove This Listener", args.Selected.name, args.ButtonID);
    }

    public void SetColor(GameObject obj, Color color)
    {
        obj.GetComponent<MeshRenderer>().material.color = color;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            Debug.Log("R");
            EventManager.DelListener(OnPointEnter); //不怎么建议使用,因为涉及到迭代字典并修改数据的需求,曲线救国未考虑性能问题。
        }
        if (Input.GetKeyDown(KeyCode.Q))
        {
            Debug.Log("Q");
            EventManager.DelListener(StylusEvent.Exit, OnPointExitAddition);
        }
        if (Input.GetKeyDown(KeyCode.D))
        {
            Debug.Log("D");
            EventManager.DelListener(StylusEvent.Press);
        }
        if (Input.GetKeyDown(KeyCode.A))
        {
            Debug.Log("A");
            EventManager.RemoveAllListener();
        }
    }




    //我们建议在此处写上移除指定的事件,但不建议移除全部哈
    void OnDestroy()
    {
        EventManager.DelListener(StylusEvent.Enter, OnPointEnter); //移除时,可以指定事件类型
        EventManager.DelListener(OnPointExit);//移除时也可以不指定事件类型
        EventManager.RemoveAllListener(); //可以使用该方法全部移除
    }
}

事件派发

using UnityEngine;
using AN.Framework;


public class EventDispatch : MonoBehaviour
{
    private GameObject selected;
    public LayerMask layerMask = 1;
    public float maxDistance = 100;
    void Update()
    {
        RaycastHit hit;
        if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, maxDistance, layerMask))
        {
            GameObject cached = hit.collider.gameObject;
            if (null == selected)//从无到有
            {
                selected = cached;
                EventManager.Allocate<StylusEventArgs>()
                    .Config(StylusEvent.Enter, gameObject, selected)
                    .Invoke();
            }
            else  //从有到有
            {
                if (selected != cached)
                {
                    EventManager.Allocate<StylusEventArgs>()
                         .Config(StylusEvent.Exit, gameObject, selected)
                         .Invoke();
                    selected = cached;
                    EventManager.Allocate<StylusEventArgs>()
                        .Config(StylusEvent.Enter, gameObject, selected)
                        .Invoke();
                }
            }

            if (Input.GetMouseButtonDown(0))
            {
                EventManager.Allocate<StylusEventArgs>()
                    .Config(StylusEvent.Press, gameObject, selected, 0)
                    .Invoke();
            }
            if (Input.GetMouseButtonUp(0))
            {
                EventManager.Allocate<StylusEventArgs>()
                    .Config(StylusEvent.Release, gameObject, selected, 0)
                    .Invoke();
            }

        }
        else
        {
            if (null != selected)
            {
                EventManager.Allocate<StylusEventArgs>()
                    .Config(StylusEvent.Exit, gameObject, selected)
                    .Invoke();
                selected = null;
            }
        }
    }
}

  • 15
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值