前置知识:Unity 事件管理中心
本篇博客展示的是事件管理中心的另一种写法。如果大家想了解其中的原理,可以看看前置知识里的介绍。
之前那个版本的事件管理中心,利用 EventHandler 和 EventArgs 匹配任意参数的无返回值方法,具有很强的通用性。但是有一个稍微麻烦的地方:如果要匹配带参数的无返回值方法,需要将参数包装进一个继承自 EventArgs 的自定义类。然后每次触发这种类型的方法时,要 new 一个这种参数包装类;方法内部也要对 EventArgs 进行类型转换,转成匹配的参数包装类。
那么本篇博客提供另一种事件管理中心的写法,用的是对应的泛型来匹配方法的参数。虽然会牺牲一些通用性,但是能够避免上面提到的麻烦之处。
接下来要展示的事件管理中心代码能够匹配最多 4 个参数的无返回值方法,基本适用于日常的开发。如果实在要匹配 4 个参数以上的无返回值方法,可以对代码进行拓展。
上代码:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 事件中心 单例模式对象 (轻量级)
/// </summary>
public class EventCenter : SingletonBase<EventCenter>
{
//key —— 事件的名字(比如:怪物死亡,玩家死亡,通关 等等)
//value —— 对应的是 监听这个事件 对应的委托函数们
private Dictionary<string, List<Delegate>> eventDic = new Dictionary<string, List<Delegate>>();
/// <summary>
/// 监听事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="callback"></param>
public void AddListenerBase(string eventName, Delegate callback)
{
if (eventDic.ContainsKey(eventName))
{
eventDic[eventName].Add(callback);
}
else
{
eventDic.Add(eventName, new List<Delegate>() {
callback});
}
}
/// <summary>
/// 监听不需要参数传递的事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="callback"></param>
public void AddListener(string eventName, Action callback)
{
AddListenerBase(eventName, callback);
}
/// <summary>
/// 添加事件监听(1个参数)
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">准备用来处理事件 的委托函数</param>
public void AddListener<T>(string eventName, Action<T> callback)
{
AddListenerBase(eventName, callback);
}
/// <summary>
/// 添加事件监听(2个参数)
/// </summary>
/// <param name="eventName">事件的名字</param>
/// <param name="callback">准备用来处理事件 的委托函数</param>
public void AddListener<T1, T2>(string eventName, Action<T1, T2> callback)
{
AddListenerBase(eventName, callback);
}
/// <summary>
/// 添加事件监听(3个参数)
/// </summary>
/// <param name="eventName">事件的名字</param>
/// <param name="callback">准备用来处理事件 的委托函数</param>
public void AddListener<T1, T2, T3>(string eventName, Action<T1, T2, T3> callback)
{
AddListenerBase(eventName, callback);
}
/// <summary>
/// 添加事件监听(4个参数)
/// </summary>
/// <param name="eventName">事件的名字</param>
/// <param name="callback">准备用来处理事件 的委托函数</param>
public void AddListener<T1, T2, T3, T4>(string eventName, Action<T1, T2, T3, T4> callback)
{
AddListenerBase(eventName, callback);
}
/// <summary>
/// 移除事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="callback"></param>
public void RemoveListenerBase(string eventName, Delegate callback)
{
if (eventDic.TryGetValue(eventName, out List<Delegate> eventList))
{
eventList.Remove(callback);
//事件列表没有事件时,将该事件键值对从字典中移除
if (eventList.Count == 0)
{
eventDic.Remove(eventName);
}
}
}
/// <summary>
/// 移除不需要参数的事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="callback"></param>
public void RemoveListener(string eventName, Action callback)
{
RemoveListenerBase(eventName, callback);
}
/// <summary>
/// 移除对应的事件监听(1个参数)
/// </summary>
/// <param name="eventName">事件的名字</param>
/// <param name="callback">对应之前添加的委托函数</param>
public void RemoveListener<T>(string eventName, Action<T> callback)
{
RemoveListenerBase(eventName, callback);
}
/// <summary>
/// 移除对应的事件监听(2个参数)
/// </summary>
/// <param name="eventName">事件的名字</param>
/// <param name="callback">对应之前添加的委托函数</param>
public void RemoveListener<T1, T2>(string eventName, Action<T1, T2> callback)
{
RemoveListenerBase(eventName, callback);
}
/// <summary>
/// 移除对应的事件监听(3个参数)
/// </summary>
/// <param name="eventName">事件的名字</param>
/// <param name="callback">对应之前添加的委托函数</param>
public void RemoveListener<T1, T2, T3>(string eventName, Action<T1, T2, T3> callback)
{
RemoveListenerBase(eventName, callback);
}
/// <summary>
/// 移除对应的事件监听(4个参数)
/// </summary>
/// <param name="eventName">事件的名字</param>
/// <param name="callback">对应之前添加的委托函数</param>
public void RemoveListener<T1, T2, T3, T4>(string eventName, Action<T1, T2, T3, T4> callback)
{
RemoveListenerBase(eventName, callback);
}
/// <summary>
/// 事件触发(不需要参数的)
/// </summary>
/// <param name="eventName"></param>
public void TriggerEvent(string eventName)
{
if (eventDic.ContainsKey(eventName))
{
foreach (Delegate callback in eventDic[eventName])
{
(callback as Action)?.Invoke();
}
}
}
/// <summary>
/// 事件触发(1个参数)
/// </summary>
/// <param name="eventName">哪一个名字的事件触发了</param>
public void TriggerEvent<T>(string eventName, T info)
{
if (eventDic.ContainsKey(eventName))
{
foreach (Delegate callback in eventDic[eventName])
{
(callback as Action<T>)?.Invoke(info);
}
}
}
/// <summary>
/// 事件触发(2个参数)
/// </summary>
/// <param name="eventName">哪一个名字的事件触发了</param>
public void TriggerEvent<T1, T2>(string eventName, T1 info1, T2 info2)
{
if (eventDic.ContainsKey(eventName))
{
foreach (Delegate callback in eventDic[eventName])
{
(callback as Action<T1, T2>)?.Invoke(info1, info2);
}
}
}
/// <summary>
/// 事件触发(3个参数)
/// </summary>
/// <param name="eventName">哪一个名字的事件触发了</param>
public void TriggerEvent<T1, T2, T3>(string eventName, T1 info1, T2 info2, T3 info3)
{
if (eventDic.ContainsKey(eventName))
{
foreach (Delegate callback in eventDic[eventName])
{
(callback as Action<T1, T2, T3>)?.Invoke(info1, info2, info3);
}
}
}
/// <summary>
/// 事件触发(4个参数)
/// </summary>
/// <param name="eventName">哪一个名字的事件触发了</param>
public void TriggerEvent<T1, T2, T3, T4>(string eventName, T1 info1, T2 info2, T3 info3, T4 info4)
{
if (eventDic.ContainsKey(eventName))
{
foreach (Delegate callback in eventDic[eventName])
{
(callback as Action<T1, T2, T3, T4>)?.Invoke(info1, info2, info3, info4);
}
}
}
/// <summary>
/// 清空事件中心
/// </summary>
public void Clear()
{
eventDic.Clear();
}
}
事件触发拓展类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 便于触发事件的扩展类
/// </summary>
public static class EventTriggerExt
{
/// <summary>
/// 触发事件(无参数)
/// </summary>
/// <param name="sender">触发源</param>
/// <param name="eventName">事件名</param>
public static void TriggerEvent(this object sender, string eventName)
{
EventCenter.Instance.TriggerEvent(eventName);
}
/// <summary>
/// 触发事件(1个参数)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sender">触发源</param>
/// <param name="eventName">事件名</param>
/// <param name="info">参数</param>
public static void TriggerEvent<T>(this object sender, string eventName, T info)
{
EventCenter.Instance.TriggerEvent(eventName, info);
}
/// <summary>
/// 触发事件(2个参数)
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <param name="sender">触发源</param>
/// <param name="eventName">事件名</param>
/// <param name="info1">参数1</param>
/// <param name="info2">参数2</param>
public static void TriggerEvent<T1, T2>(this object sender, string eventName, T1 info1, T2 info2)
{
EventCenter.Instance.TriggerEvent(eventName, info1, info2);
}
/// <summary>
/// 触发事件(3个参数)
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <param name="sender">触发源</param>
/// <param name="eventName">事件名</param>
/// <param name="info1">参数1</param>
/// <param name="info2">参数2</param>
/// <param name="info3">参数3</param>
public static void TriggerEvent<T1, T2, T3>(this object sender, string eventName, T1 info1, T2 info2, T3 info3)
{
EventCenter.Instance.TriggerEvent(eventName, info1, info2, info3);
}
/// <summary>
/// 触发事件(4个参数)
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <typeparam name="T4"></typeparam>
/// <param name="sender">触发源</param>
/// <param name="eventName">事件名</param>
/// <param name="info1">参数1</param>
/// <param name="info2">参数2</param>
/// <param name="info3">参数3</param>
/// <param name="info4">参数4</param>
public static void TriggerEvent<T1, T2, T3, T4>(this object sender, string eventName, T1 info1, T2 info2, T3 info3, T4 info4)
{
EventCenter.Instance.TriggerEvent(eventName, info1, info2, info3, info4);
}
}
使用方法:
public class Player : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.J))
{
this.TriggerEvent("PlayerDead", gameObject.name);//运用到了扩展方法
}
}
}
public class GameoverUI : MonoBehaviour
{
private void Awake()
{
EventManager.Instance.AddListener<string>(EventName.PlayerDead, ShowGameOver);
}
private void OnDestroy()
{
EventManager.Instance.RemoveListener<string>(EventName.PlayerDead, ShowGameOver);
}
private void ShowGameOver(string playerName)
{
print($"游戏结束,{playerName}阵亡");
}
}
以上两段代码模拟了“玩家死亡,打开游戏结束 UI”的事件。
此版本的事件管理中心优点:
- 事件触发和事件监听方法内部的编写更加方便(不需要额外自定义参数包装类)
注意点:
- 如果要监听有参数的方法,记得添加对应类型,对应数量的泛型
- 触发一个事件时只有同一种类型的监听方法能被响应。比如一个事件绑定了一个无参无返回值方法和一个带 2 个参数,无返回值方法,在触发事件的时候如果不带参数,那么就只有无参无返回值的方法能被响应。
委托与事件系列:
C#委托(结合 Unity)
C#事件(结合 Unity)
观察者模式(结合C# Unity)
Unity 事件管理中心
事件番外篇:UnityEvent
Unity 事件番外篇:事件管理中心(另一种版本)