简介
在Unity游戏开发中我们会遇到很多基于事件的交互,比如玩家拾取了某件物品、做了某件事,这些事件可能会造成一些后续效果,那么我们就要对能够造成后续效果的脚本传递对应的消息。当然就是通过传递事件来实现,那么我们就需要一个公共的事件中心来实现对事件触发、传递的功能。
事件管理类
这里使用单例模式来实现事件中心类,包含一个使用事件名存储对应事件的事件字典和私有的构造方法,具体可以参考下面的代码:
public class MyEventManager
{
private static MyEventManager _instance;
// public static MyEventManager Instance
// {
// get { return instance ??= new MyEventManager(); }
// }
// 静态的属性Instance,提供给外部调用对应的实例
public static MyEventManager Instance => _instance ??= new MyEventManager();
private readonly Dictionary<string, IEventInfo> _eventDic;
// 私有无参构造函数,防止外部调用构造函数
private MyEventManager()
{
_eventDic = new Dictionary<string, IEventInfo>();
}
}
添加事件
需要传入事件名,以及对应的回调。就是你希望触发这个时间后能够干些什么:
// 添加无参数回调
public void AddEventListener(string eventName, UnityAction action)
{
// 检查事件字典中是否存在对应事件
if (_eventDic.ContainsKey(eventName))
{
// 如果存在对应事件,则将回调函数添加到委托的调用列表中
(_eventDic[eventName] as EventInfo).Actions += action;
}
else
{
// 如果不存在对应事件,则将此事件添加到事件字典中
_eventDic.Add(eventName, new EventInfo(action));
}
}
// 添加有参数回调
public void AddEventListener<T>(string eventName, UnityAction<T> action)
{
if (_eventDic.ContainsKey(eventName))
{
(_eventDic[eventName] as EventInfo<T>).Actions += action;
}
else
{
_eventDic.Add(eventName, new EventInfo<T>(action));
}
}
触发事件
传入事件名来触发对应的事件
// 无参数的触发
public void EventTrigger(string eventName)
{
if (_eventDic.ContainsKey(eventName))
{
// 触发委托,依次调用委托列表内注册的函数
(_eventDic[eventName] as EventInfo).Actions?.Invoke();
}
}
// 带参数的触发
public void EventTrigger<T>(string eventName, T info)
{
if (_eventDic.ContainsKey(eventName))
{
(_eventDic[eventName] as EventInfo<T>).Actions?.Invoke(info);
}
}
如果需要的参数不止一个,可以通过实现带多参数UnityAction的EventInfo类,或者将你需要的参数作为一个类的字段成员包裹起来,当做一个参数来用下面的EventInfo事件类来传递参数。
移除事件
// 移除无参事件
public void RemoveEventListener(string eventName, UnityAction action)
{
if (_eventDic.ContainsKey(eventName))
{
// 如果存在对应的事件则移除对应的回调action
(_eventDic[eventName] as EventInfo).Actions -= action;
}
}
// 移除有参事件
public void RemoveEventListener<T>(string eventName, UnityAction<T> action)
{
if (_eventDic.ContainsKey(eventName))
{
(_eventDic[eventName] as EventInfo<T>).Actions -= action;
}
}
事件接口,类
这里的事件设置了两种事件类型,带参数和不带参数的事件。所以为了方便都让它们实现了同一个事件接口,方便事件中心来管理。
事件接口
public interface IEventInfo
{
// 空的接口,方便管理用
}
无参数事件类
public class EventInfo : IEventInfo
{
// 使用Unity定义的无参委托
public UnityAction Actions;
// 创建实例时需要传入对应的委托
public EventInfo(UnityAction action)
{
// 委托之间组合,形成新的委托
Actions += action;
}
}
带参数事件类
public class EventInfo<T> : IEventInfo
{
public UnityAction<T> Actions;
// 泛型类构造函数不需要带尖括号<>
public EventInfo(UnityAction<T> action)
{
Actions += action;
}
}
事件名类
添加一个静态类,专门管理事件对应的名称,具体如下代码所示:
namespace Managers
{
// 在这个类中添加事件名称
public static class MyEventConst
{
public const string PlayerAttackOver = "PlayerAttackOver";
public const string PlayerXInput = "PlayerXInput";
public const string PlayerKeyDownInput = "PlayerKeyDownInput";
}
}
完整代码参考
using System.Collections.Generic;
using UnityEngine.Events;
namespace Managers
{
public class MyEventManager
{
private static MyEventManager _instance;
// public static MyEventManager Instance
// {
// get { return instance ??= new MyEventManager(); }
// }
public static MyEventManager Instance => _instance ??= new MyEventManager();
private readonly Dictionary<string, IEventInfo> _eventDic;
private MyEventManager()
{
_eventDic = new Dictionary<string, IEventInfo>();
}
// 添加事件监听
public void AddEventListener(string eventName, UnityAction action)
{
if (_eventDic.ContainsKey(eventName))
{
(_eventDic[eventName] as EventInfo).Actions += action;
}
else
{
_eventDic.Add(eventName, new EventInfo(action));
}
}
// 删除事件监听
public void RemoveEventListener(string eventName, UnityAction action)
{
if (_eventDic.ContainsKey(eventName))
{
(_eventDic[eventName] as EventInfo).Actions -= action;
}
}
// 事件触发器
public void EventTrigger(string eventName)
{
if (_eventDic.ContainsKey(eventName))
{
(_eventDic[eventName] as EventInfo).Actions?.Invoke();
}
}
public void AddEventListener<T>(string eventName, UnityAction<T> action)
{
if (_eventDic.ContainsKey(eventName))
{
(_eventDic[eventName] as EventInfo<T>).Actions += action;
}
else
{
_eventDic.Add(eventName, new EventInfo<T>(action));
}
}
public void RemoveEventListener<T>(string eventName, UnityAction<T> action)
{
if (_eventDic.ContainsKey(eventName))
{
(_eventDic[eventName] as EventInfo<T>).Actions -= action;
}
}
public void EventTrigger<T>(string eventName, T info)
{
if (_eventDic.ContainsKey(eventName))
{
(_eventDic[eventName] as EventInfo<T>).Actions?.Invoke(info);
}
}
// 清除所有事件
public void ClearAllEvent()
{
_eventDic.Clear();
}
}
public interface IEventInfo
{
}
public class EventInfo : IEventInfo
{
public UnityAction Actions;
public EventInfo(UnityAction action)
{
Actions += action;
}
}
public class EventInfo<T> : IEventInfo
{
public UnityAction<T> Actions;
// 泛型类构造函数不需要带尖括号<>
public EventInfo(UnityAction<T> action)
{
Actions += action;
}
}
}
使用参考
比如我需要在玩家脚本中监听玩家攻击结束事件,为此添加事件。
无参数
private void BindEvent()
{
// 为事件PlayerAttackOver添加回调PlayerAttackOverCallback
MyEventManager.Instance.AddEventListener(MyEventConst.PlayerAttackOver, PlayerAttackOverCallback);
}
private void RemoveEvent()
{
MyEventManager.Instance.RemoveEventListener(MyEventConst.PlayerAttackOver, PlayerAttackOverCallback);
}
// PlayerAttackOver事件触发后需要执行的函数
private void PlayerAttackOverCallback()
{
_curComboCounter = ++_curComboCounter % _totalCombo;
Fsm.SwitchState(StateType.Idle);
}
带参数
private void BindEvent()
{
MyEventManager.Instance.AddEventListener<KeyCode>(MyEventConst.PlayerKeyDownInput, ChangeToOtherState);
}
private void RemoveEvent()
{
MyEventManager.Instance.RemoveEventListener<KeyCode>(MyEventConst.PlayerKeyDownInput, ChangeToOtherState);
}
private void ChangeToOtherState(KeyCode keyCode)
{
switch (keyCode)
{
case KeyCode.LeftShift:
Fsm.SwitchState(StateType.Dash);
break;
case KeyCode.Mouse0:
Fsm.SwitchState(StateType.Attack);
break;
default:
break;
}
}
触发事件
无参数
private void AttackOverTrigger()
{
// 玩家攻击结束时触发事件PlayerAttackOver
MyEventManager.Instance.EventTrigger(MyEventConst.PlayerAttackOver);
}
带参数
MyEventManager.Instance.EventTrigger(MyEventConst.PlayerKeyDownInput, _curKeyCode);
总结
把事件管理器的一种实现方法,保存在这里防止以后忘记了。