先贴代码和用法
EventCenter
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public interface IEventInfo
{
}
public class EventInfo<T> : IEventInfo
{
public UnityAction<T> actions = delegate { };
public EventInfo(UnityAction<T> action)
{
actions += action;
}
}
public class EventInfo : IEventInfo
{
public UnityAction actions = delegate { };
public EventInfo(UnityAction action)
{
actions += action;
}
}
public class EventCenter : MonoBehaviour
{
public static EventCenter Instance;
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
Dictionary<GameEvent, IEventInfo> eventDict = new Dictionary<GameEvent, IEventInfo>();
//触发事件
public void EventTrigger(GameEvent gameEvent)
{
if (eventDict.ContainsKey(gameEvent))
{
(eventDict[gameEvent] as EventInfo).actions?.Invoke();
}
}
public void EventTrigger<T>(GameEvent gameEvent, T value)
{
if (eventDict.ContainsKey(gameEvent))
{
//Debug.Log(eventDict[gameEvent]);
(eventDict[gameEvent] as EventInfo<T>).actions?.Invoke(value);
}
}
#region 添加事件监听器
public void AddEventListener(GameEvent gameEvent, UnityAction action)
{
if (eventDict.ContainsKey(gameEvent))
{
(eventDict[gameEvent] as EventInfo).actions += action;
}
else
{
eventDict.Add(gameEvent, new EventInfo(action) as IEventInfo);
}
}
public void AddEventListener<T>(GameEvent gameEvent, UnityAction<T> action)
{
if (eventDict.ContainsKey(gameEvent))
{
(eventDict[gameEvent] as EventInfo<T>).actions += action;
}
else
{
eventDict.Add(gameEvent, new EventInfo<T>(action) as IEventInfo);
}
}
#endregion
#region 移除事件添加器
public void RemoveEventListener(GameEvent gameEvent, UnityAction action)
{
if (eventDict.ContainsKey(gameEvent))
{
(eventDict[gameEvent] as EventInfo).actions -= action;
}
}
public void RemoveEventListener<T>(GameEvent gameEvent, UnityAction<T> action)
{
if (eventDict.ContainsKey(gameEvent))
{
(eventDict[gameEvent] as EventInfo<T>).actions -= action;
}
}
#endregion
//清空事件
public void Clear()
{
eventDict.Clear();
}
}
需要搭配一个GameEvent的枚举一起使用
可以单独定义一个类用于存储枚举
GameEvent
public enum GameEvent
{
游戏失败,
角色阵亡,
游戏胜利,
}
具体用法
1.在需要触发的脚本中通过AddEventListener 添加对特定逻辑函数的订阅,将函数订阅到枚举的类型上
private void Start()
{
EventCenter.Instance.AddEventListener(GameEvent.游戏失败, GameOver);
}
private void OnDestroy()
{
EventCenter.Instance.RemoveEventListener(GameEvent.游戏失败, GameOver);
}
private void GameOver()
{
//写你想被触发的逻辑
//...
}
2.在想要触发的地方使用EventTrigger 即可触发添加到枚举中的事件
if(hp < 0)
{
EventCenter.Instance.EventTrigger(GameEvent.游戏失败);
}
if(player.transform.position.y < -5f)
{
EventCenter.Instance.EventTrigger(GameEvent.游戏失败);
}
这样可以在不同的地方都可以触发事件,简洁明了
核心结构解析
1. 事件信息容器(EventInfo类)
-
IEventInfo
接口:空接口,作为类型标识,用于统一管理不同参数类型的事件容器。 -
泛型容器
EventInfo<T>
:存储带参数的事件回调(UnityAction<T>
),构造函数将传入的回调添加到委托链。 -
无参容器
EventInfo
:存储无参数的事件回调(UnityAction
),设计逻辑与泛型版本一致。
2. 单例事件中心(EventCenter类)
-
单例模式:通过
Awake
方法确保全局唯一实例,跨场景不销毁(DontDestroyOnLoad
)。 -
事件字典
eventDict
:以GameEvent
(应为枚举类型)为键,存储对应的事件信息容器。
3. 事件触发与监听
-
触发事件:
EventTrigger
方法通过事件类型查找并调用对应的委托链,支持无参和泛型参数。 -
添加/移除监听:通过
AddEventListener
和RemoveEventListener
注册或解绑回调,自动处理委托链的增减。
关键设计点
1. 泛型与接口的灵活运用
-
通过
IEventInfo
接口统一管理不同参数类型的事件容器,利用类型转换在触发时调用具体委托,平衡了类型安全与灵活性。
2. 委托链的默认初始化
-
委托初始化为
delegate { }
,避免空引用异常,无需每次触发前检查null
。
3. 单例全局访问
-
事件中心作为单例,方便任何脚本通过
EventCenter.Instance
访问,确保事件系统的全局一致性
总结
该事件中心通过单例模式和泛型设计,提供了简洁高效的事件管理机制,适用于Unity项目的解耦需求。开发者需注意事件类型的一致性和监听的生命周期管理,以确保系统稳定运行。