以下是一个基于 组件化设计 的 Unity EventDispatcher 实现方案,支持对象级事件管理和泛型参数传递:
EventDispatcher 完整实现代码
using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
[DisallowMultipleComponent]
public class EventDispatcher : MonoBehaviour
{
// 基础事件容器
private Dictionary<string, UnityEvent> _eventTable = new Dictionary<string, UnityEvent>();
// 泛型事件容器(支持带参数的事件)
private Dictionary<string, System.Object> _genericEventTable = new Dictionary<string, System.Object>();
//=== 基础事件方法 ===//
/// <summary>
/// 订阅无参数事件
/// </summary>
public void AddListener(string eventName, UnityAction handler)
{
if (string.IsNullOrEmpty(eventName))
{
Debug.LogError("事件名不能为空", this);
return;
}
if (!_eventTable.TryGetValue(eventName, out UnityEvent unityEvent))
{
unityEvent = new UnityEvent();
_eventTable.Add(eventName, unityEvent);
}
unityEvent.AddListener(handler);
}
/// <summary>
/// 触发无参数事件
/// </summary>
public void Dispatch(string eventName)
{
if (_eventTable.TryGetValue(eventName, out UnityEvent unityEvent))
{
unityEvent.Invoke();
}
else
{
Debug.LogWarning($"未注册的事件: {
eventName}", this);
}
}
//=== 泛型事件方法 ===//
/// <summary>
/// 订阅带参数事件
/// </summary>
public void AddListener<T>(string eventName, UnityAction<T> handler)
{
if (!_genericEventTable.TryGetValue(eventName, out System.Object objEvent))
{
var genericEvent = new UnityEvent<T>();
genericEvent.AddListener(handler);
_genericEventTable.Add(eventName, genericEvent);
}
else
{
if (objEvent is UnityEvent<T> genericEvent)
{
genericEvent.AddListener(handler);
}
else
{
Debug.LogError($"事件类型不匹配: {
eventName}", this);
}
}
}
/// <summary>
/// 触发带参数事件
/// </summary>
public void Dispatch<T>(string eventName, T eventData)
{
if (_genericEventTable.TryGetValue(eventName, out System.Object objEvent))
{
if (objEvent is UnityEvent<T> genericEvent)
{
genericEvent.Invoke(eventData);
}
else
{
Debug.LogError($"事件参数类型不匹配: {
eventName}", this);
}
}
else
{
Debug.LogWarning($"未注册的泛型事件: {
eventName}", this);
}
}
//=== 清理方法 ===//
/// <summary>
/// 移除指定事件的所有监听
/// </summary>
public void RemoveEvent(string eventName)
{
_eventTable.Remove(eventName);
_genericEventTable.Remove(eventName);
}
/// <summary>
/// 清空所有事件监听
/// </summary>
public void ClearAllListeners()
{
_eventTable.Clear();
_genericEventTable.Clear();
}
void OnDestroy()
{
ClearAllListeners(); // 对象销毁时自动清理
}
}
1. 无参数事件(如按钮点击)
// 订阅方
public class UIButton : MonoBehaviour
{
private EventDispatcher _dispatcher;
void Start()
{
_dispatcher = GetComponent<EventDispatcher>();
_dispatcher.AddListener("OnClick", HandleClick);
}
private void HandleClick()
{
Debug.Log("按钮被点击!");
}
}
// 触发方
public class ButtonTrigger : MonoBehaviour
{
public void OnInteract()
{
GetComponent<EventDispatcher>().Dispatch("OnClick");
}
}
2. 带参数事件(如敌人受伤)
// 订阅方
public class EnemyHealth : MonoBehaviour
{
void Start()
{
GetComponent<EventDispatcher>().AddListener<float>("TakeDamage", OnDamage);
}
private void OnDamage(float damage)
{
Debug.Log($"受到伤害: {
damage}");
}
}
// 触发方
public class Weapon : MonoBehaviour
{
public void Attack()
{
var dispatcher = target.GetComponent<EventDispatcher>();
dispatcher.Dispatch("TakeDamage", 25.5f);
}
}
高级功能扩展
方案1:多参数支持
// 添加多参数分发方法
public void Dispatch<T1, T2>(string eventName, T1 arg1, T2 arg2)
{
if (_genericEventTable.TryGetValue(eventName, out System.Object objEvent))
{
if (objEvent is UnityEvent<T1, T2> genericEvent)
{
genericEvent.Invoke(arg1, arg2);
}
}
}
// 使用示例:触发位置和伤害事件
dispatcher.Dispatch("ProjectileHit", transform.position, 30f);
方案2:自动取消订阅装饰器
// 为 MonoBehaviour 添加扩展方法
public static class EventDispatcherExtensions
{
public static void AutoUnsubscribe(this MonoBehaviour listener,
EventDispatcher dispatcher,
string eventName,
UnityAction handler)
{
dispatcher.AddListener(eventName, handler);
listener.gameObject.AddComponent<AutoUnsubscriber>()
.Setup(() => dispatcher.RemoveEvent(eventName));
}
}
// 自动取消订阅组件
public class AutoUnsubscriber : MonoBehaviour
{
private System.Action _unsubscribeAction;
public void Setup(System.Action action) => _unsubscribeAction = action;
void OnDestroy() => _unsubscribeAction?.Invoke();
}
方案3:可视化调试
[Header("Debug Settings")]
[SerializeField] private bool _showLogs = true;
public void Dispatch<T>(string eventName, T eventData)
{
if (_showLogs)
Debug.Log($"<color=green>[事件触发]</color> {
eventName} : {
eventData}", this);
// ...原有触发逻辑...
}
最佳实践建议
层级化命名规范
使用分类前缀避免命名冲突:
// UI系统事件
dispatcher.Dispatch("UI.Menu.Open");
// 战斗系统事件
dispatcher.Dispatch("Combat.Enemy.Spawn", enemyData);
结合 ScriptableObject
创建可配置的事件资产:
[CreateAssetMenu(menuName = "Events/Game Event")]
public class GameEvent : ScriptableObject
{
public UnityEvent OnRaised;
public void Raise() => OnRaised?.Invoke();
}
// 使用方式
[SerializeField] private GameEvent _playerDeathEvent;
_playerDeathEvent.OnRaised += HandleDeath;
性能优化策略
对高频事件(如每帧触发)使用 事件节流 技术
private float _lastEventTime;
public void DispatchWithThrottle(string eventName, float interval = 0.1f)
{
if (Time.time - _lastEventTime < interval) return;
Dispatch(eventName);
_lastEventTime = Time.time;
}
与 EventManager 的对比使用场景
场景 | 适用组件 |
---|---|
跨场景全局事件 | ✅ EventManager |
玩家技能连招触发 | ✅ EventDispatcher |
背包物品拖拽交互 | ✅ EventDispatcher |
游戏暂停/继续 | ✅ EventManager |
NPC对话选项选择 | ✅ EventDispatcher |
通过这种组件化的 EventDispatcher 实现,可以实现细粒度的事件控制,特别适合以下场景:
- UI系统:按钮点击、面板开关
- 实体交互:角色受击、道具拾取
- 动画事件:动画关键帧触发逻辑
- 状态同步:多对象间的状态变更通知