4.事件模块
4.1 知识点
事件模块的作用
降低程序耦合性,减小程序复杂度
事件监听者接口和实现
引入必要的命名空间:
使用了UnityEngine.Events命名空间,这是Unity中用于处理事件和委托的命名空间。
定义一个事件监听器接口:
创建了一个名为IBaseEventListener的接口,它是一个空接口,用于作为事件监听器的基本类型。
创建泛型事件监听器类:
创建了一个泛型类BaseEventListener
在泛型类中,定义了一个UnityAction
创建了一个构造函数,接受一个UnityAction
创建非泛型事件监听器类:
创建了一个非泛型类BaseEventListener,同样实现了IBaseEventListener接口。
在非泛型类中,定义了一个UnityAction类型的字段actions,用于存储事件的委托函数。
创建了一个构造函数,接受一个UnityAction类型的参数action,并将该参数添加到actions委托中,以便事件的监听和响应。
事件监听管理器
继承自BaseSingletonInCSharp类:
BaseEventManager继承自BaseSingletonInCSharp,表明它是一个单例类,确保在应用程序中只有一个实例。
定义一个私有字段baseEventListenerDictionary:
baseEventListenerDictionary是一个Dictionary<string, IBaseEventListener>,用于存储事件名字与事件监听器的映射关系。键是事件的名字,值是对应的事件监听器。
实现AddEventListener方法(泛型和非泛型版本):
用于添加事件监听器,接受事件名字和委托函数作为参数。
如果事件字典baseEventListenerDictionary中已经包含了指定名字的事件监听器,就将新的委托函数添加到现有事件监听器的委托中。
如果事件字典中没有对应的事件监听器,就创建一个新的事件监听器,并将委托函数添加到它的委托中,然后将它添加到baseEventListenerDictionary中。
实现RemoveEventListener方法(泛型和非泛型版本):
用于移除事件监听器,接受事件名字和之前添加的委托函数作为参数。
如果事件字典baseEventListenerDictionary中包含了指定名字的事件监听器,就从该事件监听器的委托中移除指定的委托函数。
实现EventTrigger方法(泛型和非泛型版本):
用于触发事件,接受事件名字作为参数。
如果事件字典baseEventListenerDictionary中包含了指定名字的事件监听器,就安全地触发该事件监听器的委托函数。
使用null条件运算符(?.)来确保在没有事件监听器或委托函数的情况下不会引发空引用异常。
实现Clear方法:
用于清空事件管理器,通常在场景切换时使用。
清空事件字典baseEventListenerDictionary中的所有事件监听器。
进行测试
在 Start 方法中:
它使用事件管理器(BaseEventManager)添加了一个事件监听器,监听名为 “任意键按下” 的事件。这个事件监听器使用泛型,期望接收一个 KeyCode 参数,表示按下的键。
在事件监听器中,它检查是否有按下的键码(通过 Input.GetKeyDown(keyCode) 来判断)。如果有按下的键码,它将进一步检查按下的键是否是 Q、W、E 或 R 键,然后根据按下的键执行不同的操作:
如果按下的是 Q、W 或 E 键,它会输出 “小技能释放” 的调试日志。
如果按下的是 R 键,它会输出 “大招释放” 的调试日志。
如果按下的键不是以上列出的键,它会输出 “无效操作” 的调试日志。
void Start()
{
BaseEventManager.Instance.AddEventListener<KeyCode>("任意键按下", (keyCode) =>
{
if (Input.GetKeyDown(keyCode))
{
switch (keyCode)
{
case KeyCode.Q:
case KeyCode.W:
case KeyCode.E:
Debug.Log("小技能释放");
break;
case KeyCode.R:
Debug.Log("大招释放");
break;
default:
Debug.Log($"无效操作 keyCode:{keyCode}");
break;
}
}
});
}
在 Update 方法中:
它检查是否有任何键被按下(通过 Input.anyKeyDown)。如果有键被按下,它会遍历所有的 KeyCode 值,并检查每个键是否被按下(通过 Input.GetKeyDown(keyCode))。
如果找到一个被按下的键,它会使用事件管理器触发名为 “任意键按下” 的事件,并将被按下的键码作为参数传递给事件监听器。
void Update()
{
if (Input.anyKeyDown)
{
foreach (KeyCode keyCode in System.Enum.GetValues(typeof(KeyCode)))
{
if (Input.GetKeyDown(keyCode))
{
BaseEventManager.Instance.EventTrigger<KeyCode>("任意键按下", keyCode);
break; // 一旦检测到按键按下,就跳出循环
}
}
}
}
测试结果
4.2 知识点代码
IBaseEventListener
using UnityEngine.Events;
// 事件监听器接口
public interface IBaseEventListener
{
}
// 泛型事件监听器
public class BaseEventListener<T> : IBaseEventListener
{
public UnityAction<T> actions; // 存储事件的委托函数
// 构造函数,用于添加事件监听器
public BaseEventListener(UnityAction<T> action)
{
actions += action; // 添加事件监听器
}
}
// 非泛型事件监听器
public class BaseEventListener : IBaseEventListener
{
public UnityAction actions; // 存储事件的委托函数
// 构造函数,用于添加事件监听器
public BaseEventListener(UnityAction action)
{
actions += action; // 添加事件监听器
}
}
BaseEventManager
using System.Collections.Generic;
using UnityEngine.Events;
// 事件管理器
public class BaseEventManager : BaseSingletonInCSharp<BaseEventManager>
{
// key —— 事件的名字(比如:怪物死亡,玩家死亡,通关等等)
// value —— 对应的是监听这个事件对应的委托函数们
private Dictionary<string, IBaseEventListener> baseEventListenerDictionary = new Dictionary<string, IBaseEventListener>();
/// <summary>
/// 添加事件监听
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">准备用来处理事件的委托函数</param>
public void AddEventListener<T>(string name, UnityAction<T> action)
{
// 有没有对应的事件监听
// 有的情况
if (baseEventListenerDictionary.ContainsKey(name))
{
(baseEventListenerDictionary[name] as BaseEventListener<T>).actions += action;
}
// 没有的情况
else
{
baseEventListenerDictionary.Add(name, new BaseEventListener<T>(action));
}
}
/// <summary>
/// 监听不需要参数传递的事件
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">用来处理事件的委托函数</param>
public void AddEventListener(string name, UnityAction action)
{
// 有没有对应的事件监听
// 有的情况
if (baseEventListenerDictionary.ContainsKey(name))
{
(baseEventListenerDictionary[name] as BaseEventListener).actions += action;
}
// 没有的情况
else
{
baseEventListenerDictionary.Add(name, new BaseEventListener(action));
}
}
/// <summary>
/// 移除对应的事件监听
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">对应之前添加的委托函数</param>
public void RemoveEventListener<T>(string name, UnityAction<T> action)
{
if (baseEventListenerDictionary.ContainsKey(name))
(baseEventListenerDictionary[name] as BaseEventListener<T>).actions -= action;
}
/// <summary>
/// 移除不需要参数的事件
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">对应之前添加的委托函数</param>
public void RemoveEventListener(string name, UnityAction action)
{
if (baseEventListenerDictionary.ContainsKey(name))
(baseEventListenerDictionary[name] as BaseEventListener).actions -= action;
}
/// <summary>
/// 事件触发
/// </summary>
/// <param name="name">哪一个名字的事件触发了</param>
public void EventTrigger<T>(string name, T info)
{
// 有没有对应的事件监听
// 有的情况
if (baseEventListenerDictionary.ContainsKey(name))
{
// 使用 null 条件运算符安全地触发事件
(baseEventListenerDictionary[name] as BaseEventListener<T>)?.actions?.Invoke(info);
}
}
/// <summary>
/// 事件触发(不需要参数的)
/// </summary>
/// <param name="name">哪一个名字的事件触发了</param>
public void EventTrigger(string name)
{
// 有没有对应的事件监听
// 有的情况
if (baseEventListenerDictionary.ContainsKey(name))
{
// 使用 null 条件运算符安全地触发事件
(baseEventListenerDictionary[name] as BaseEventListener)?.actions?.Invoke();
}
}
/// <summary>
/// 清空事件中心
/// 主要用在场景切换时
/// </summary>
public void Clear()
{
baseEventListenerDictionary.Clear();
}
}
Lesson04_事件模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson04_事件模块 : MonoBehaviour
{
void Start()
{
BaseEventManager.Instance.AddEventListener<KeyCode>("任意键按下", (keyCode) =>
{
if (Input.GetKeyDown(keyCode))
{
switch (keyCode)
{
case KeyCode.Q:
case KeyCode.W:
case KeyCode.E:
Debug.Log("小技能释放");
break;
case KeyCode.R:
Debug.Log("大招释放");
break;
default:
Debug.Log($"无效操作 keyCode:{keyCode}");
break;
}
}
});
}
void Update()
{
if (Input.anyKeyDown)
{
foreach (KeyCode keyCode in System.Enum.GetValues(typeof(KeyCode)))
{
if (Input.GetKeyDown(keyCode))
{
BaseEventManager.Instance.EventTrigger<KeyCode>("任意键按下", keyCode);
break; // 一旦检测到按键按下,就跳出循环
}
}
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com