【Unity】高性能的事件分发系统
1. 脚本代码完整介绍
(1) EventDef.cs
功能
- 定义事件名称常量,作为全局事件标识符。
- 示例中定义了
EVENT_GENERATE_CHESS
(生成棋子事件)。
代码
using UnityEngine;public class EventDef : MonoBehaviour
{/// <summary>/// 生成棋子/// </summary>public const string EVENT_GENERATE_CHESS = "EVENT_GENERATE_CHESS";
}
特点
- 简单清晰,仅用于集中管理事件名称。
- 通过
const
确保事件名称不可修改。
(2) EventHandlerPool.cs
功能
- 管理事件处理器的对象池,优化高频事件的性能。
- 支持懒加载、预加载和资源回收。
代码
using System.Collections.Generic;public class EventHandlerPool
{private Dictionary<string, Stack<GameplayEventHandler>> pools = new Dictionary<string, Stack<GameplayEventHandler>>();public GameplayEventHandler GetHandler(string eventName, GameplayEventHandler originalHandler){if (!pools.ContainsKey(eventName)){pools.Add(eventName, new Stack<GameplayEventHandler>());PreloadPool(eventName, 10, originalHandler);}var pool = pools[eventName];return pool.Count > 0 ? pool.Pop() : originalHandler;}// 预加载独立对象池private void PreloadPool(string eventName, int count, GameplayEventHandler handler){var pool = pools[eventName];for (int i = 0; i < count; i++)pool.Push(handler);}public void ReturnHandler(string eventName, GameplayEventHandler handler){if (pools.ContainsKey(eventName))pools[eventName].Push(handler);}public void ClearPool(string eventName){if (pools.ContainsKey(eventName))pools[eventName].Clear();}public void ClearAll(){pools.Clear();}
}
特点
- 懒加载:首次调用
GetHandler
时初始化对象池。 - 预加载:通过
PreloadPool
提前分配处理器实例。
(3) EventDispatcher.cs
功能
- 核心事件分发系统,支持订阅、发布、注销事件。
- 集成对象池优化,提供单例访问。
代码
using System.Collections.Generic;
using UnityEngine;public delegate void GameplayEventHandler(params object[] args);
public interface IGameplayObserver
{void OnEventTriggered(string eventName, params object[] args);
}
public class EventDispatcher
{// 存储事件与独立对象池的映射private EventHandlerPool eventPools = new EventHandlerPool();private Dictionary<string, Dictionary<int, GameplayEventHandler>> listeners = new Dictionary<string, Dictionary<int, GameplayEventHandler>>();private readonly string szErrorMessage = "DispatchEvent Error, Event:{0}, Error:{1}, {2}";private static EventDispatcher s_instance;public static EventDispatcher instance{get{if (null == s_instance)s_instance = new EventDispatcher();return s_instance;}}// 订阅事件接口观察者模式public void Regist(string eventName, IGameplayObserver observer){if (observer == null) return;Regist(eventName, (args) => observer.OnEventTriggered(eventName, args));}/// <summary>/// 订阅事件/// </summary>/// <param name="eventName">事件名</param>/// <param name="gameplayEventHandler">事件</param>public void Regist(string eventName, GameplayEventHandler gameplayEventHandler){if (gameplayEventHandler == null) return;if (!listeners.ContainsKey(eventName))listeners.Add(eventName, new Dictionary<int, GameplayEventHandler>());var handlerDic = listeners[eventName];var handlerHash = gameplayEventHandler.GetHashCode();if (handlerDic.ContainsKey(handlerHash))handlerDic.Remove(handlerHash);listeners[eventName].Add(gameplayEventHandler.GetHashCode(), gameplayEventHandler);}/// <summary>/// 订阅事件(独立对象池)/// </summary>/// <param name="eventName"></param>/// <param name="gameplayEventHandler"></param>public void RegistWithPool(string eventName, GameplayEventHandler gameplayEventHandler){if (gameplayEventHandler == null) return;var pooledHandler = eventPools.GetHandler(eventName, gameplayEventHandler);Regist(eventName, pooledHandler);}/// <summary>/// 注销事件/// </summary>/// <param name="eventName">事件名</param>/// <param name="gameplayEventHandler">事件</param>public void UnRegist(string eventName, GameplayEventHandler gameplayEventHandler){if (null == gameplayEventHandler) return;if (listeners.ContainsKey(eventName)){listeners[eventName].Remove(gameplayEventHandler.GetHashCode());if (null == listeners[eventName] || 0 == listeners[eventName].Count){listeners.Remove(eventName);}}}/// <summary>/// 注销事件(归还对象池)/// </summary>public void UnRegistWithPool(string eventName, GameplayEventHandler gameplayEventHandler){if (gameplayEventHandler == null) return;if (listeners.TryGetValue(eventName, out var handlerDic) &&handlerDic.TryGetValue(gameplayEventHandler.GetHashCode(), out var handler)){handlerDic.Remove(gameplayEventHandler.GetHashCode());eventPools.ReturnHandler(eventName, handler); // 归还对象池if (handlerDic.Count == 0){listeners.Remove(eventName);}}}/// <summary>/// 发布事件(合并方法)/// </summary>/// <param name="eventName">事件名</param>/// <param name="usePool">是否使用对象池</param>/// <param name="objects">事件参数</param>public void DispatchEvent(string eventName, bool usePool = false, params object[] objects){Dictionary<int, GameplayEventHandler> handlerDic;// 获取事件处理器字典if (usePool){if (!listeners.TryGetValue(eventName, out handlerDic) || handlerDic == null || handlerDic.Count == 0)return;}else{if (!listeners.ContainsKey(eventName) || listeners[eventName] == null || listeners[eventName].Count == 0)return;handlerDic = listeners[eventName];}// 复制一份避免遍历时修改var dic = new Dictionary<int, GameplayEventHandler>(handlerDic);foreach (var handler in dic.Values){try{handler(objects);}catch (System.Exception ex){Debug.LogErrorFormat(szErrorMessage, eventName, ex.Message, ex.StackTrace);}}}/// <summary>/// 清空事件/// </summary>/// <param name="eventName">事件名</param>public void ClearEvents(string eventName){if (listeners.ContainsKey(eventName)){listeners.Remove(eventName);}}// 清空特定事件的对象池public void ClearEventPool(string eventName){eventPools.ClearPool(eventName);}/// <summary>/// 清空所有事件和对象池/// </summary>public void ClearAll(){listeners.Clear();eventPools.ClearAll();}
}
特点
- 单例模式:全局唯一实例。
- 多订阅方式:支持委托和接口订阅。
- 对象池集成:通过
RegistWithPool
和UnRegistWithPool
管理资源。 - 异常处理:捕获事件处理中的异常并记录日志。
2. 系统使用的编程模式分析
(1) 观察者模式(Observer Pattern)
- 核心思想:定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象自动收到通知。
- 实现方式:
IGameplayObserver
接口定义了观察者的统一行为。EventDispatcher
维护订阅列表,通过DispatchEvent
通知所有观察者。
(2) 单例模式(Singleton Pattern)
- 核心思想:确保一个类只有一个实例,并提供全局访问点。
- 实现方式:
EventDispatcher
通过静态instance
属性提供单例访问。
(3) 对象池模式(Object Pool Pattern)
- 核心思想:通过复用对象减少资源分配和垃圾回收开销。
- 实现方式:
EventHandlerPool
管理GameplayEventHandler
的栈式对象池。- 预加载和懒加载结合,平衡性能和内存。
3. 系统优势与适用场景
优势
- 高性能:对象池减少GC压力,适合高频事件(如游戏中的伤害计算)。
- 解耦:观察者模式分离事件发布者和订阅者。
- 灵活性:支持委托和接口两种订阅方式。
- 可维护性:集中管理事件名称和分发逻辑。
适用场景
- 游戏开发(技能触发、UI交互)。
- 实时系统(如物联网设备状态通知)。
- 需要解耦的模块间通信。
总结
当前系统通过 观察者模式 和 对象池模式 实现了高性能的事件分发,结合 单例模式 提供全局访问点,并通过 发布-订阅模式 解耦模块。适用于需要高效、灵活事件处理的场景,如游戏开发和实时交互系统。