4.事件模块

4.事件模块


4.1 知识点

事件模块的作用

降低程序耦合性,减小程序复杂度

事件监听者接口和实现

引入必要的命名空间:

使用了UnityEngine.Events命名空间,这是Unity中用于处理事件和委托的命名空间。

定义一个事件监听器接口:

创建了一个名为IBaseEventListener的接口,它是一个空接口,用于作为事件监听器的基本类型。

创建泛型事件监听器类:

创建了一个泛型类BaseEventListener,它实现了IBaseEventListener接口。
在泛型类中,定义了一个UnityAction类型的字段actions,用于存储事件的委托函数。
创建了一个构造函数,接受一个UnityAction类型的参数action,并将该参数添加到actions委托中,以便事件的监听和响应。

创建非泛型事件监听器类:

创建了一个非泛型类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

×

喜欢就点赞,疼爱就打赏