5.事件中心模块
5.1 知识点
事件中心模块的作用
在游戏开发中,我们经常遇到这样的问题:不同模块之间需要相互通信,但直接引用会造成高耦合。
核心问题:
- 模块间直接引用导致高耦合,难以维护
- 一个模块的修改可能影响多个其他模块
- 代码复用性差,测试困难
解决方案:
事件中心模块通过观察者模式实现模块间的松耦合通信,让模块之间不需要直接引用,而是通过事件进行通信。



事件中心模块的基本原理
设计理念:
- 观察者模式:事件发布者和订阅者解耦
- 集中管理:通过事件中心统一管理所有事件
- 类型安全:支持泛型参数传递,避免装箱拆箱
- 统一命名:通过枚举管理事件名称
工作流程:
- 注册监听:模块向事件中心注册感兴趣的事件
- 触发事件:某个模块触发事件,传递相关数据
- 事件分发:事件中心将事件分发给所有监听者
- 执行回调:监听者执行相应的处理逻辑


事件中心模块基础实现
using System.Collections.Generic;
using UnityEngine.Events;
/// <summary>
/// 事件中心模块(初步版本 - 存在参数传递问题)
/// </summary>
public class EventCenter : Singleton<EventCenter>
{
#region 字段
/// <summary>
/// 事件字典
/// 键为事件名称(字符串),值为对应的委托
/// </summary>
private Dictionary<string, UnityAction> _eventDict = new Dictionary<string, UnityAction>();
#endregion
#region 构造函数
private EventCenter()
{
}
#endregion
#region 事件管理
/// <summary>
/// 添加事件监听者
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="func">事件处理函数</param>
public void AddEventListener(string eventName, UnityAction func)
{
if (_eventDict.ContainsKey(eventName))
{
_eventDict[eventName] += func;
}
else
{
_eventDict.Add(eventName, func);
}
}
/// <summary>
/// 移除事件监听者
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="func">要移除的事件处理函数</param>
public void RemoveEventListener(string eventName, UnityAction func)
{
if (_eventDict.ContainsKey(eventName))
{
_eventDict[eventName] -= func;
}
}
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
public void EventTrigger(string eventName)
{
if (_eventDict.ContainsKey(eventName))
{
_eventDict[eventName]?.Invoke();
}
}
#endregion
}
初步测试代码:Monster.cs 和 Player.cs
// Monster.cs
public class Monster : MonoBehaviour
{
public string monsterName = "哥布林";
public int monsterID = 1;
public void Dead()
{
Debug.Log("怪物死亡了");
// 触发怪物死亡事件
EventCenter.Instance.EventTrigger("MonsterDead");
// 问题:无法传递怪物信息给监听者
}
}
// Player.cs
public class Player : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件
EventCenter.Instance.AddEventListener("MonsterDead", OnMonsterDead);
}
private void OnMonsterDead()
{
Debug.Log("玩家获得奖励");
// 输出:玩家获得奖励
// 问题:无法获取具体是哪个怪物死亡,无法获取怪物信息
}
private void OnDestroy()
{
EventCenter.Instance.RemoveEventListener("MonsterDead", OnMonsterDead);
}
}
事件中心传递参数
之前存在什么问题
通过上面的测试代码,我们发现了一个严重的问题:事件触发时无法传递参数,监听者无法获取具体的事件信息。
问题分析:
- 无法传递数据:事件触发时无法传递相关数据
- 信息缺失:监听者无法获取具体的事件信息
- 功能受限:无法实现复杂的事件处理逻辑
如何解决参数传递问题
为了支持事件参数传递,我们需要修改事件中心的实现。最初的想法是使用object类型来传递参数。
解决方案:
- 使用object类型:利用里式替换原则,父类容器装子类对象
- 修改委托类型:使用
UnityAction<object>支持参数传递 - 类型转换:在监听者中进行类型转换
解决后的源码实现:EventCenter.cs (支持object参数传递)
using System.Collections.Generic;
using UnityEngine.Events;
/// <summary>
/// 事件中心模块(支持object参数传递,存在装箱拆箱问题)
/// </summary>
public class EventCenter : Singleton<EventCenter>
{
#region 字段
/// <summary>
/// 事件字典
/// 键为事件名称(字符串),值为对应的委托
/// </summary>
private Dictionary<string, UnityAction<object>> _eventDict = new Dictionary<string, UnityAction<object>>();
#endregion
#region 构造函数
private EventCenter()
{
}
#endregion
#region 事件管理
/// <summary>
/// 添加事件监听者
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="func">事件处理函数</param>
public void AddEventListener(string eventName, UnityAction<object> func)
{
if (_eventDict.ContainsKey(eventName))
{
_eventDict[eventName] += func;
}
else
{
_eventDict.Add(eventName, func);
}
}
/// <summary>
/// 移除事件监听者
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="func">要移除的事件处理函数</param>
public void RemoveEventListener(string eventName, UnityAction<object> func)
{
if (_eventDict.ContainsKey(eventName))
{
_eventDict[eventName] -= func;
}
}
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="info">事件参数</param>
public void EventTrigger(string eventName, object info)
{
if (_eventDict.ContainsKey(eventName))
{
_eventDict[eventName]?.Invoke(info);
}
}
#endregion
}
测试代码:Monster.cs 和 Player.cs (更新)
// Monster.cs (更新后,支持参数传递)
public class Monster : MonoBehaviour
{
public string monsterName = "哥布林";
public int monsterID = 1;
public void Dead()
{
Debug.Log("怪物死亡了");
// 触发怪物死亡事件,传递怪物信息
EventCenter.Instance.EventTrigger("MonsterDead", this);
// 输出:怪物死亡了
}
}
// Player.cs (更新后,支持接收参数)
public class Player : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件
EventCenter.Instance.AddEventListener("MonsterDead", OnMonsterDead);
}
private void OnMonsterDead(object monsterObj)
{
// 类型转换获取怪物信息
Monster monster = monsterObj as Monster;
if (monster != null)
{
Debug.Log($"玩家获得奖励:{monster.monsterName}");
// 输出:玩家获得奖励:哥布林
}
}
private void OnDestroy()
{
EventCenter.Instance.RemoveEventListener("MonsterDead", OnMonsterDead);
}
}
事件中心参数类型自定义
之前存在什么问题
通过上面的实现,我们解决了参数传递问题,但引入了新的问题:使用object类型传递值类型数据时存在装箱拆箱,增加性能开销。
问题分析:
- 装箱拆箱:值类型传递时发生装箱拆箱,影响性能
- 类型不安全:需要手动类型转换,容易出错
- 代码冗余:每次都需要进行类型转换
如何解决参数类型自定义问题
为了支持任意类型的参数传递,同时避免装箱拆箱问题,我们需要使用泛型来实现类型安全的事件参数传递。
解决方案:
- 使用泛型:支持任意类型的参数传递
- 里式替换原则:通过基类容器管理不同类型的EventInfo
- 类型安全:编译时确定类型,避免运行时类型转换
解决后的源码实现:EventInfoBase.cs 和 EventInfo.cs
// EventInfoBase.cs
/// <summary>
/// 事件信息基类
/// 主要用于里式替换原则,父类容器装子类对象
/// </summary>
public abstract class EventInfoBase
{
}
// EventInfo.cs
using UnityEngine.Events;
/// <summary>
/// 有参数事件信息类
/// 用来包裹对应观察者函数委托的类
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
public class EventInfo<T> : EventInfoBase
{
/// <summary>
/// 真正观察者对应的函数信息
/// </summary>
public UnityAction<T> Actions;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="action">事件处理函数</param>
public EventInfo(UnityAction<T> action)
{
Actions += action;
}
}
/// <summary>
/// 无参数事件信息类
/// 主要用来记录无参无返回值委托
/// </summary>
public class EventInfo : EventInfoBase
{
/// <summary>
/// 无参数事件处理函数
/// </summary>
public UnityAction Actions;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="action">事件处理函数</param>
public EventInfo(UnityAction action)
{
Actions += action;
}
}
解决后的源码实现:EventCenter.cs (支持泛型参数传递)
using System.Collections.Generic;
using UnityEngine.Events;
/// <summary>
/// 事件中心模块(支持泛型参数传递,类型安全)
/// </summary>
public class EventCenter : Singleton<EventCenter>
{
#region 字段
/// <summary>
/// 事件字典
/// 键为事件名称(字符串),值为对应的事件信息基类
/// </summary>
private Dictionary<string, EventInfoBase> _eventDict = new Dictionary<string, EventInfoBase>();
#endregion
#region 构造函数
private EventCenter()
{
}
#endregion
#region 事件管理
/// <summary>
/// 添加有参数事件监听者
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
/// <param name="eventName">事件名称</param>
/// <param name="func">事件处理函数</param>
public void AddEventListener<T>(string eventName, UnityAction<T> func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo<T>).Actions += func;
}
else
{
_eventDict.Add(eventName, new EventInfo<T>(func));
}
}
/// <summary>
/// 添加无参数事件监听者
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="func">事件处理函数</param>
public void AddEventListener(string eventName, UnityAction func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo).Actions += func;
}
else
{
_eventDict.Add(eventName, new EventInfo(func));
}
}
/// <summary>
/// 移除有参数事件监听者
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
/// <param name="eventName">事件名称</param>
/// <param name="func">要移除的事件处理函数</param>
public void RemoveEventListener<T>(string eventName, UnityAction<T> func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo<T>).Actions -= func;
}
}
/// <summary>
/// 移除无参数事件监听者
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="func">要移除的事件处理函数</param>
public void RemoveEventListener(string eventName, UnityAction func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo).Actions -= func;
}
}
/// <summary>
/// 触发有参数事件
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
/// <param name="eventName">事件名称</param>
/// <param name="info">事件参数</param>
public void EventTrigger<T>(string eventName, T info)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo<T>)?.Actions?.Invoke(info);
}
}
/// <summary>
/// 触发无参数事件
/// </summary>
/// <param name="eventName">事件名称</param>
public void EventTrigger(string eventName)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo)?.Actions?.Invoke();
}
}
#endregion
}
测试代码:Monster.cs 和 Player.cs (最终版本)
// Monster.cs (最终版本,支持泛型参数传递)
public class Monster : MonoBehaviour
{
public string monsterName = "哥布林";
public int monsterID = 1;
public void Dead()
{
Debug.Log("怪物死亡了");
// 触发怪物死亡事件,传递怪物信息(类型安全)
EventCenter.Instance.EventTrigger<Monster>("MonsterDead", this);
// 输出:怪物死亡了
}
}
// Player.cs (最终版本,支持泛型参数接收)
public class Player : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件(类型安全)
EventCenter.Instance.AddEventListener<Monster>("MonsterDead", OnMonsterDead);
}
private void OnMonsterDead(Monster monster)
{
// 直接使用怪物对象,无需类型转换
Debug.Log($"玩家获得奖励:{monster.monsterName}");
// 输出:玩家获得奖励:哥布林
}
private void OnDestroy()
{
EventCenter.Instance.RemoveEventListener<Monster>("MonsterDead", OnMonsterDead);
}
}
事件中心事件名优化
之前存在什么问题
通过上面的实现,我们解决了参数传递和类型安全问题,但还存在一个问题:使用字符串作为事件名称容易拼写错误,导致事件无法正确触发或监听。
问题分析:
- 拼写错误:字符串容易拼写错误,导致事件失效
- 难以维护:事件名称分散在各个地方,难以统一管理
- IDE支持差:无法享受IDE的智能提示和重构功能
如何解决事件名优化问题
为了统一管理事件名称,避免拼写错误,我们需要使用枚举来管理所有事件类型。
解决方案:
- 使用枚举:统一管理所有事件类型
- 类型安全:编译时检查事件名称
- IDE支持:享受智能提示和重构功能
解决后的源码实现:E_EventType.cs
/// <summary>
/// 事件类型枚举
/// 定义游戏中所有可能触发的事件类型
/// </summary>
public enum E_EventType
{
#region 游戏逻辑事件
/// <summary>
/// 怪物死亡事件
/// 参数:Monster
/// </summary>
E_Monster_Dead,
/// <summary>
/// 玩家获取奖励事件
/// 参数:int
/// </summary>
E_Player_GetReward,
/// <summary>
/// 测试用事件
/// 参数:无
/// </summary>
E_Test,
/// <summary>
/// 场景切换时进度变化事件
/// 参数:float
/// </summary>
E_SceneLoadChange,
#endregion
#region 输入系统事件
/// <summary>
/// 输入系统触发技能1行为
/// 参数:无
/// </summary>
E_Input_Skill1,
/// <summary>
/// 输入系统触发技能2行为
/// 参数:无
/// </summary>
E_Input_Skill2,
/// <summary>
/// 输入系统触发技能3行为
/// 参数:无
/// </summary>
E_Input_Skill3,
/// <summary>
/// 水平轴输入事件
/// 参数:float (-1~1)
/// </summary>
E_Input_Horizontal,
/// <summary>
/// 垂直轴输入事件
/// 参数:float (-1~1)
/// </summary>
E_Input_Vertical,
#endregion
}
解决后的源码实现:EventCenter.cs (使用枚举管理事件名称)
using System.Collections.Generic;
using UnityEngine.Events;
/// <summary>
/// 事件中心模块(使用枚举管理事件名称,类型安全)
/// </summary>
public class EventCenter : Singleton<EventCenter>
{
#region 字段
/// <summary>
/// 事件字典
/// 键为事件类型枚举,值为对应的事件信息基类
/// </summary>
private Dictionary<E_EventType, EventInfoBase> _eventDict = new Dictionary<E_EventType, EventInfoBase>();
#endregion
#region 构造函数
private EventCenter()
{
}
#endregion
#region 事件管理
/// <summary>
/// 添加有参数事件监听者
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
/// <param name="eventName">事件类型</param>
/// <param name="func">事件处理函数</param>
public void AddEventListener<T>(E_EventType eventName, UnityAction<T> func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo<T>).Actions += func;
}
else
{
_eventDict.Add(eventName, new EventInfo<T>(func));
}
}
/// <summary>
/// 添加无参数事件监听者
/// </summary>
/// <param name="eventName">事件类型</param>
/// <param name="func">事件处理函数</param>
public void AddEventListener(E_EventType eventName, UnityAction func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo).Actions += func;
}
else
{
_eventDict.Add(eventName, new EventInfo(func));
}
}
/// <summary>
/// 移除有参数事件监听者
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
/// <param name="eventName">事件类型</param>
/// <param name="func">要移除的事件处理函数</param>
public void RemoveEventListener<T>(E_EventType eventName, UnityAction<T> func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo<T>).Actions -= func;
}
}
/// <summary>
/// 移除无参数事件监听者
/// </summary>
/// <param name="eventName">事件类型</param>
/// <param name="func">要移除的事件处理函数</param>
public void RemoveEventListener(E_EventType eventName, UnityAction func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo).Actions -= func;
}
}
/// <summary>
/// 触发有参数事件
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
/// <param name="eventName">事件类型</param>
/// <param name="info">事件参数</param>
public void EventTrigger<T>(E_EventType eventName, T info)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo<T>)?.Actions?.Invoke(info);
}
}
/// <summary>
/// 触发无参数事件
/// </summary>
/// <param name="eventName">事件类型</param>
public void EventTrigger(E_EventType eventName)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo)?.Actions?.Invoke();
}
}
#endregion
#region 清理方法
/// <summary>
/// 清空所有事件的监听
/// </summary>
public void Clear()
{
_eventDict.Clear();
}
/// <summary>
/// 清除指定某一个事件的所有监听
/// </summary>
/// <param name="eventName">要清除的事件类型</param>
public void Clear(E_EventType eventName)
{
if (_eventDict.ContainsKey(eventName))
{
_eventDict.Remove(eventName);
}
}
#endregion
}
测试代码:Monster.cs 和 Player.cs (最终版本)
// Monster.cs (最终版本,使用枚举管理事件名称)
public class Monster : MonoBehaviour
{
public string monsterName = "哥布林";
public int monsterID = 1;
public void Dead()
{
Debug.Log("怪物死亡了");
// 触发怪物死亡事件,使用枚举类型(类型安全)
EventCenter.Instance.EventTrigger<Monster>(E_EventType.E_Monster_Dead, this);
// 输出:怪物死亡了
}
}
// Player.cs (最终版本,使用枚举管理事件名称)
public class Player : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件,使用枚举类型(类型安全)
EventCenter.Instance.AddEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
// 监听测试事件
EventCenter.Instance.AddEventListener(E_EventType.E_Test, OnTest);
}
private void OnMonsterDead(Monster monster)
{
Debug.Log($"玩家获得奖励:{monster.monsterName}");
// 输出:玩家获得奖励:哥布林
}
private void OnTest()
{
Debug.Log("无参事件监听者");
// 输出:无参事件监听者
}
private void OnDestroy()
{
EventCenter.Instance.RemoveEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
EventCenter.Instance.RemoveEventListener(E_EventType.E_Test, OnTest);
}
}
测试和使用
创建多模块事件监听测试
创建一个完整的测试场景,展示事件中心如何实现模块间的松耦合通信。
步骤说明:
- 创建多个模块(任务系统、奖励系统、其他系统)
- 让这些模块都监听同一个事件
- 触发事件,验证所有模块都能正确响应
测试代码:
// Task.cs - 任务系统
public class Task : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件
EventCenter.Instance.AddEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
private void OnMonsterDead(Monster monster)
{
Debug.Log($"任务记录:{monster.monsterName}");
// 输出:任务记录:哥布林
}
private void OnDestroy()
{
EventCenter.Instance.RemoveEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
}
// RewardSystem.cs - 奖励系统
public class RewardSystem : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件
EventCenter.Instance.AddEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
private void OnMonsterDead(Monster monster)
{
Debug.Log($"奖励系统:获得经验值 {monster.monsterID * 10}");
// 输出:奖励系统:获得经验值 10
}
private void OnDestroy()
{
EventCenter.Instance.RemoveEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
}
// OtherSystem.cs - 其他系统
public class OtherSystem : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件
EventCenter.Instance.AddEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
private void OnMonsterDead(Monster monster)
{
Debug.Log($"其他系统:怪物 {monster.monsterID} 已处理");
// 输出:其他系统:怪物 1 已处理
}
private void OnDestroy()
{
EventCenter.Instance.RemoveEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
}
创建事件触发测试
创建一个测试脚本来触发事件,验证事件中心的功能。
步骤说明:
- 创建测试脚本
- 在Start方法中触发各种事件
- 验证所有监听者都能正确响应
测试代码:
// EventCenterTest.cs
public class EventCenterTest : MonoBehaviour
{
void Start()
{
Debug.Log("事件中心测试:开始");
// 创建怪物对象
Monster monster = new Monster();
monster.monsterName = "测试怪物";
monster.monsterID = 999;
// 触发怪物死亡事件
monster.Dead();
// 输出:怪物死亡了
// 输出:玩家获得奖励:测试怪物
// 输出:任务记录:测试怪物
// 输出:奖励系统:获得经验值 9990
// 输出:其他系统:怪物 999 已处理
// 触发无参数测试事件
EventCenter.Instance.EventTrigger(E_EventType.E_Test);
// 输出:无参事件监听者
// 触发玩家获得奖励事件
EventCenter.Instance.EventTrigger<int>(E_EventType.E_Player_GetReward, 100);
// 输出:玩家获得奖励:100
Debug.Log("事件中心测试:完成");
}
}
拓展和进阶
可以使用struct继承事件接口定义事件,struct里写好这个事件需要的参数,这样一个struct可以描述事件类型和所需要的参数了。
其次可以定义个订阅Token类,取消订阅传Token。
5.2 知识点代码
EventCenter.cs(事件中心模块)
using System.Collections.Generic;
using UnityEngine.Events;
/// <summary>
/// 事件中心模块
/// 实现观察者模式,管理游戏中的事件监听和触发
/// 支持有参数和无参数的事件处理
/// </summary>
public class EventCenter : Singleton<EventCenter>
{
#region 字段
/// <summary>
/// 事件字典
/// 用于记录对应事件关联的对应逻辑
/// </summary>
private Dictionary<E_EventType, EventInfoBase> _eventDict = new Dictionary<E_EventType, EventInfoBase>();
#endregion
#region 构造函数
private EventCenter()
{
}
#endregion
#region 事件触发
/// <summary>
/// 触发有参数事件
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
/// <param name="eventName">事件名称</param>
/// <param name="info">事件参数</param>
public void EventTrigger<T>(E_EventType eventName, T info)
{
// 存在关心此事件的监听者才通知处理逻辑
if (_eventDict.ContainsKey(eventName))
{
// 执行对应的逻辑
(_eventDict[eventName] as EventInfo<T>)?.Actions?.Invoke(info);
}
}
/// <summary>
/// 触发无参数事件
/// </summary>
/// <param name="eventName">事件名称</param>
public void EventTrigger(E_EventType eventName)
{
// 存在关心此事件的监听者才通知处理逻辑
if (_eventDict.ContainsKey(eventName))
{
// 执行对应的逻辑
(_eventDict[eventName] as EventInfo)?.Actions?.Invoke();
}
}
#endregion
#region 事件监听管理
/// <summary>
/// 添加有参数事件监听者
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
/// <param name="eventName">事件名称</param>
/// <param name="func">事件处理函数</param>
public void AddEventListener<T>(E_EventType eventName, UnityAction<T> func)
{
// 如果已经存在关心事件的委托记录,直接添加即可
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo<T>).Actions += func;
}
else
{
_eventDict.Add(eventName, new EventInfo<T>(func));
}
}
/// <summary>
/// 添加无参数事件监听者
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="func">事件处理函数</param>
public void AddEventListener(E_EventType eventName, UnityAction func)
{
// 如果已经存在关心事件的委托记录,直接添加即可
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo).Actions += func;
}
else
{
_eventDict.Add(eventName, new EventInfo(func));
}
}
/// <summary>
/// 移除有参数事件监听者
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
/// <param name="eventName">事件名称</param>
/// <param name="func">要移除的事件处理函数</param>
public void RemoveEventListener<T>(E_EventType eventName, UnityAction<T> func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo<T>).Actions -= func;
}
}
/// <summary>
/// 移除无参数事件监听者
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="func">要移除的事件处理函数</param>
public void RemoveEventListener(E_EventType eventName, UnityAction func)
{
if (_eventDict.ContainsKey(eventName))
{
(_eventDict[eventName] as EventInfo).Actions -= func;
}
}
#endregion
#region 清理方法
/// <summary>
/// 清空所有事件的监听
/// </summary>
public void Clear()
{
_eventDict.Clear();
}
/// <summary>
/// 清除指定某一个事件的所有监听
/// </summary>
/// <param name="eventName">要清除的事件名称</param>
public void Clear(E_EventType eventName)
{
if (_eventDict.ContainsKey(eventName))
{
_eventDict.Remove(eventName);
}
}
#endregion
}
EventInfoBase.cs(事件信息基类)
/// <summary>
/// 事件信息基类
/// 主要用于里式替换原则,父类容器装子类对象
/// </summary>
public abstract class EventInfoBase
{
}
EventInfo.cs(事件信息类)
using UnityEngine.Events;
/// <summary>
/// 有参数事件信息类
/// 用来包裹对应观察者函数委托的类
/// </summary>
/// <typeparam name="T">事件参数类型</typeparam>
public class EventInfo<T> : EventInfoBase
{
/// <summary>
/// 真正观察者对应的函数信息
/// </summary>
public UnityAction<T> Actions;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="action">事件处理函数</param>
public EventInfo(UnityAction<T> action)
{
Actions += action;
}
}
/// <summary>
/// 无参数事件信息类
/// 主要用来记录无参无返回值委托
/// </summary>
public class EventInfo : EventInfoBase
{
/// <summary>
/// 无参数事件处理函数
/// </summary>
public UnityAction Actions;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="action">事件处理函数</param>
public EventInfo(UnityAction action)
{
Actions += action;
}
}
E_EventType.cs(事件类型枚举)
/// <summary>
/// 事件类型枚举
/// 定义游戏中所有可能触发的事件类型
/// </summary>
public enum E_EventType
{
#region 游戏逻辑事件
/// <summary>
/// 怪物死亡事件
/// 参数:Monster
/// </summary>
E_Monster_Dead,
/// <summary>
/// 玩家获取奖励事件
/// 参数:int
/// </summary>
E_Player_GetReward,
/// <summary>
/// 测试用事件
/// 参数:无
/// </summary>
E_Test,
/// <summary>
/// 场景切换时进度变化事件
/// 参数:float
/// </summary>
E_SceneLoadChange,
#endregion
#region 输入系统事件
/// <summary>
/// 输入系统触发技能1行为
/// 参数:无
/// </summary>
E_Input_Skill1,
/// <summary>
/// 输入系统触发技能2行为
/// 参数:无
/// </summary>
E_Input_Skill2,
/// <summary>
/// 输入系统触发技能3行为
/// 参数:无
/// </summary>
E_Input_Skill3,
/// <summary>
/// 水平轴输入事件
/// 参数:float (-1~1)
/// </summary>
E_Input_Horizontal,
/// <summary>
/// 垂直轴输入事件
/// 参数:float (-1~1)
/// </summary>
E_Input_Vertical,
#endregion
}
Monster.cs(怪物类)
using UnityEngine;
/// <summary>
/// 怪物类
/// 演示如何触发事件
/// </summary>
public class Monster : MonoBehaviour
{
public string monsterName = "哥布林";
public int monsterID = 1;
/// <summary>
/// 怪物死亡方法
/// </summary>
public void Dead()
{
Debug.Log("怪物死亡了");
// 触发怪物死亡事件,传递怪物信息
EventCenter.Instance.EventTrigger<Monster>(E_EventType.E_Monster_Dead, this);
// 输出:怪物死亡了
}
}
Player.cs(玩家类)
using UnityEngine;
/// <summary>
/// 玩家类
/// 演示如何监听事件
/// </summary>
public class Player : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件
EventCenter.Instance.AddEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
// 监听测试事件
EventCenter.Instance.AddEventListener(E_EventType.E_Test, OnTest);
}
/// <summary>
/// 怪物死亡事件处理
/// </summary>
/// <param name="monster">死亡的怪物</param>
private void OnMonsterDead(Monster monster)
{
Debug.Log($"玩家获得奖励:{monster.monsterName}");
// 输出:玩家获得奖励:哥布林
}
/// <summary>
/// 测试事件处理
/// </summary>
private void OnTest()
{
Debug.Log("无参事件监听者");
// 输出:无参事件监听者
}
private void OnDestroy()
{
// 移除事件监听,避免内存泄露
EventCenter.Instance.RemoveEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
EventCenter.Instance.RemoveEventListener(E_EventType.E_Test, OnTest);
}
}
Task.cs(任务系统)
using UnityEngine;
/// <summary>
/// 任务系统
/// 演示多模块事件监听
/// </summary>
public class Task : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件
EventCenter.Instance.AddEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
/// <summary>
/// 怪物死亡事件处理
/// </summary>
/// <param name="monster">死亡的怪物</param>
private void OnMonsterDead(Monster monster)
{
Debug.Log($"任务记录:{monster.monsterName}");
// 输出:任务记录:哥布林
}
private void OnDestroy()
{
// 移除事件监听
EventCenter.Instance.RemoveEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
}
RewardSystem.cs(奖励系统)
using UnityEngine;
/// <summary>
/// 奖励系统
/// 演示多模块事件监听
/// </summary>
public class RewardSystem : MonoBehaviour
{
private void Awake()
{
// 监听怪物死亡事件
EventCenter.Instance.AddEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
/// <summary>
/// 怪物死亡事件处理
/// </summary>
/// <param name="monster">死亡的怪物</param>
private void OnMonsterDead(Monster monster)
{
Debug.Log($"奖励系统:获得经验值 {monster.monsterID * 10}");
// 输出:奖励系统:获得经验值 10
}
private void OnDestroy()
{
// 移除事件监听
EventCenter.Instance.RemoveEventListener<Monster>(E_EventType.E_Monster_Dead, OnMonsterDead);
}
}
EventCenterUsageTest.cs(事件中心使用测试)
using UnityEngine;
/// <summary>
/// 事件中心使用测试
/// 演示事件中心的完整功能
/// </summary>
public class EventCenterUsageTest : MonoBehaviour
{
void Start()
{
Debug.Log("事件中心使用测试:开始");
// 创建怪物对象
Monster monster = new Monster();
monster.monsterName = "测试怪物";
monster.monsterID = 999;
// 触发怪物死亡事件
monster.Dead();
// 输出:怪物死亡了
// 输出:玩家获得奖励:测试怪物
// 输出:任务记录:测试怪物
// 输出:奖励系统:获得经验值 9990
// 触发无参数测试事件
EventCenter.Instance.EventTrigger(E_EventType.E_Test);
// 输出:无参事件监听者
// 触发玩家获得奖励事件
EventCenter.Instance.EventTrigger<int>(E_EventType.E_Player_GetReward, 100);
// 输出:玩家获得奖励:100
Debug.Log("事件中心使用测试:完成");
}
}
ComprehensiveEventTest.cs(综合事件测试)
using UnityEngine;
/// <summary>
/// 综合事件测试
/// 演示事件中心的所有功能
/// </summary>
public class ComprehensiveEventTest : MonoBehaviour
{
void Start()
{
Debug.Log("综合事件测试:开始");
// 测试有参数事件
TestParameterizedEvents();
// 测试无参数事件
TestParameterlessEvents();
// 测试事件清理
TestEventClearing();
}
/// <summary>
/// 测试有参数事件
/// </summary>
private void TestParameterizedEvents()
{
Debug.Log("测试有参数事件");
// 创建测试怪物
Monster monster = new Monster();
monster.monsterName = "综合测试怪物";
monster.monsterID = 888;
// 触发怪物死亡事件
monster.Dead();
// 输出:怪物死亡了
// 输出:玩家获得奖励:综合测试怪物
// 输出:任务记录:综合测试怪物
// 输出:奖励系统:获得经验值 8880
}
/// <summary>
/// 测试无参数事件
/// </summary>
private void TestParameterlessEvents()
{
Debug.Log("测试无参数事件");
// 触发测试事件
EventCenter.Instance.EventTrigger(E_EventType.E_Test);
// 输出:无参事件监听者
// 触发技能事件
EventCenter.Instance.EventTrigger(E_EventType.E_Input_Skill1);
// 输出:技能1被触发
}
/// <summary>
/// 测试事件清理
/// </summary>
private void TestEventClearing()
{
Debug.Log("测试事件清理");
// 清理特定事件
EventCenter.Instance.Clear(E_EventType.E_Test);
Debug.Log("已清理测试事件");
// 清理所有事件
EventCenter.Instance.Clear();
Debug.Log("已清理所有事件");
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com