单例模式
/// <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;
}
}
}
}