Unity 事件监听与广播(高度解耦合,观察者模式)

news2024/9/27 5:54:09

文章目录

  • 1. 目的
  • 2. 主要思路
  • 3. 基础类
  • 4. EventCenter 事件中心类
  • 5. 测试

1. 目的

使用观察者模式降低模块间的耦合性

2. 主要思路

  1. 通过C# 的 Dictionary 存放事件码和事件的委托
  2. 添加事件:
    • 判断字典是否有该事件码,没有添加
    • 判断当前委托类型与添加的事件码的类型是否一致
    • 最后订阅该事件
  3. 移除事件:
    • 先判断事件码是否存在
    • 取消订阅
    • 最后判断事件码是否为空,是null则移除事件码
  4. 广播事件:
    • 若事件委托不为null,广播事件

3. 基础类

  • SingletonBase 单例模式基类
/*
 * FileName:    SingletonBase
 * Author:      ming
 * CreateTime:  2023/6/28 11:46:00
 * Description: 单例模式基类
 * 
*/
using UnityEngine;
using System.Collections;

public class SingletonBase<T> where T : new() {
    private static T _Instance;

    public static T GetInstance()
    {
        if (_Instance == null) _Instance = new T();
        return _Instance;
    }
}
  • EventEnum 事件枚举类
public enum EventEnum
{
    
}
  • CallBack 委托类
/*
 * FileName:    CallBack
 * Author:      ming
 * CreateTime:  2023/6/28 14:22:38
 * Description: 委托
 * 
*/

// 无参委托
public delegate void CallBack();

// 带参委托
public delegate void CallBack<T>(T arg);
public delegate void CallBack<T, X>(T arg1, X arg2);
public delegate void CallBack<T, X, Y>(T arg1, X arg2, Y arg3);
public delegate void CallBack<T, X, Y, Z>(T arg1, X arg2, Y arg3, Z arg4);
public delegate void CallBack<T, X, Y, Z, W>(T arg1, X arg2, Y arg3, Z arg4, W arg5);

4. EventCenter 事件中心类

/*
 * FileName:    EventCenter
 * Author:      ming
 * CreateTime:  2023/6/28 14:15:39
 * Description: 事件中心类,添加、删除、分发事件
 * 
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

public class EventCenter : MonoBehaviour
{
    // 字典,用于存放事件码和委托对应
    private static Dictionary<EventEnum, Delegate> m_EventTable = new Dictionary<EventEnum, Delegate>();

    #region 添加和移除事件前的判断

    // 添加事件监听前的判断
    private static void OnAddListenerJudge(EventEnum eventEnum, Delegate callBack) {
        // 先判断事件码是否存在于字典中,不存在则先添加
        if (!m_EventTable.ContainsKey(eventEnum)) {
            // 先给字典添加事件码,委托设置为空
            m_EventTable.Add(eventEnum, null);
        }

        // 判断当前事件码的委托类型和要添加的委托类型是否一致,不一致不能添加,抛出异常
        Delegate d = m_EventTable[eventEnum];
        if (d != null && d.GetType() != callBack.GetType()) {
            throw new Exception(string.Format("尝试为事件码{0}添加不同事件的委托,当前事件所对应的委托是{1},要添加的委托类型{2}", eventEnum, d.GetType(), callBack.GetType()));
        }
    }

    // 移除事件码前的判断
    private static void OnRemoveListenerBeforeJudge(EventEnum eventEnum) {
        // 判断是否包含指定事件码
        if (!m_EventTable.ContainsKey(eventEnum)) {
            throw new Exception(string.Format("移除监听错误;没有事件码", eventEnum));
        }
    }

    // 移除事件码后的判断,用于移除字典中空的事件码
    private static void OnRemoveListenerLaterJudge(EventEnum eventEnum) {
        if (m_EventTable[eventEnum] == null) {
            m_EventTable.Remove(eventEnum);
        }
    }

    #endregion

    #region 添加监听
    // 无参
    public static void AddListener(EventEnum eventEnum, CallBack callBack) {
        OnAddListenerJudge(eventEnum, callBack);
        m_EventTable[eventEnum] = (CallBack)m_EventTable[eventEnum] + callBack;
    }
    // 带参数
    public static void AddListener<T>(EventEnum eventEnum, CallBack<T> callBack) {
        OnAddListenerJudge(eventEnum, callBack);
        m_EventTable[eventEnum] = (CallBack<T>)m_EventTable[eventEnum] + callBack;
    }
    public static void AddListener<T, X>(EventEnum eventEnum, CallBack<T, X> callBack) {
        OnAddListenerJudge(eventEnum, callBack);
        m_EventTable[eventEnum] = (CallBack<T, X>)m_EventTable[eventEnum] + callBack;
    }
    public static void AddListener<T, X, Y>(EventEnum eventEnum, CallBack<T, X, Y> callBack) {
        OnAddListenerJudge(eventEnum, callBack);
        m_EventTable[eventEnum] = (CallBack<T, X, Y>)m_EventTable[eventEnum] + callBack;
    }
    public static void AddListener<T, X, Y, Z>(EventEnum eventEnum, CallBack<T, X, Y, Z> callBack) {
        OnAddListenerJudge(eventEnum, callBack);
        m_EventTable[eventEnum] = (CallBack<T, X, Y, Z>)m_EventTable[eventEnum] + callBack;
    }
    public static void AddListener<T, X, Y, Z, W>(EventEnum eventEnum, CallBack<T, X, Y, Z, W> callBack) {
        OnAddListenerJudge(eventEnum, callBack);
        m_EventTable[eventEnum] = (CallBack<T, X, Y, Z, W>)m_EventTable[eventEnum] + callBack;
    }

    #endregion

    #region 移除监听

    public static void RemoveListener(EventEnum eventEnum, CallBack callBack) {
        OnRemoveListenerBeforeJudge(eventEnum);
        m_EventTable[eventEnum] = (CallBack)m_EventTable[eventEnum] - callBack;
        OnRemoveListenerLaterJudge(eventEnum);
    }

    public static void RemoveListener<T>(EventEnum eventEnum, CallBack<T> callBack) {
        OnRemoveListenerBeforeJudge(eventEnum);
        m_EventTable[eventEnum] = (CallBack<T>)m_EventTable[eventEnum] - callBack;
        OnRemoveListenerLaterJudge(eventEnum);
    }

    public static void RemoveListener<T, X>(EventEnum eventEnum, CallBack<T, X> callBack) {
        OnRemoveListenerBeforeJudge(eventEnum);
        m_EventTable[eventEnum] = (CallBack<T, X>)m_EventTable[eventEnum] - callBack;
        OnRemoveListenerLaterJudge(eventEnum);
    }

    public static void RemoveListener<T, X, Y>(EventEnum eventEnum, CallBack<T, X, Y> callBack) {
        OnRemoveListenerBeforeJudge(eventEnum);
        m_EventTable[eventEnum] = (CallBack<T, X, Y>)m_EventTable[eventEnum] - callBack;
        OnRemoveListenerLaterJudge(eventEnum);
    }

    public static void RemoveListener<T, X, Y, Z>(EventEnum eventEnum, CallBack<T, X, Y, Z> callBack) {
        OnRemoveListenerBeforeJudge(eventEnum);
        m_EventTable[eventEnum] = (CallBack<T, X, Y, Z>)m_EventTable[eventEnum] - callBack;
        OnRemoveListenerLaterJudge(eventEnum);
    }

    public static void RemoveListener<T, X, Y, Z, W>(EventEnum eventEnum, CallBack<T, X, Y, Z, W> callBack) {
        OnRemoveListenerBeforeJudge(eventEnum);
        m_EventTable[eventEnum] = (CallBack<T, X, Y, Z, W>)m_EventTable[eventEnum] - callBack;
        OnRemoveListenerLaterJudge(eventEnum);
    }

    #endregion

    #region 广播事件
    public static void Broadcast(EventEnum eventEnum) {
        Delegate d;
        if (m_EventTable.TryGetValue(eventEnum, out d)) {
            CallBack callBack = d as CallBack;
            if (callBack != null) {
                callBack();
            }
        }
    }

    public static void Broadcast<T>(EventEnum eventEnum, T arg1) {
        Delegate d;
        if (m_EventTable.TryGetValue(eventEnum, out d)) {
            CallBack<T> callBack = d as CallBack<T>;
            if (callBack != null) {
                callBack(arg1);
            }
        }
    }

    public static void Broadcast<T, X>(EventEnum eventEnum, T arg1, X arg2) {
        Delegate d;
        if (m_EventTable.TryGetValue(eventEnum, out d)) {
            CallBack<T, X> callBack = d as CallBack<T, X>;
            if (callBack != null) {
                callBack(arg1, arg2);
            }
        }
    }

    public static void Broadcast<T, X, Y>(EventEnum eventEnum, T arg1, X arg2, Y arg3) {
        Delegate d;
        if (m_EventTable.TryGetValue(eventEnum, out d)) {
            CallBack<T, X, Y> callBack = d as CallBack<T, X, Y>;
            if (callBack != null) {
                callBack(arg1, arg2, arg3);
            }
        }
    }

    public static void Broadcast<T, X, Y, Z>(EventEnum eventEnum, T arg1, X arg2, Y arg3, Z arg4) {
        Delegate d;
        if (m_EventTable.TryGetValue(eventEnum, out d)) {
            CallBack<T, X, Y, Z> callBack = d as CallBack<T, X, Y, Z>;
            if (callBack != null) {
                callBack(arg1, arg2, arg3, arg4);
            }
        }
    }

    public static void Broadcast<T, X, Y, Z, W>(EventEnum eventEnum, T arg1, X arg2, Y arg3, Z arg4, W arg5) {
        Delegate d;
        if (m_EventTable.TryGetValue(eventEnum, out d)) {
            CallBack<T, X, Y, Z, W> callBack = d as CallBack<T, X, Y, Z, W>;
            if (callBack != null) {
                callBack(arg1, arg2, arg3, arg4, arg5);
            }
        }
    }

    #endregion
}

5. 测试

创建场景:
在这里插入图片描述
BtnClick:

/*
 * FileName:    BtnClick
 * Author:      ming
 * CreateTime:  2023/6/28 17:41:23
 * Description:
 * 
*/
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class BtnClick : MonoBehaviour
{
    private Button noParameter;
    private Button oneParameter;
    private Button twoParameter;
    private Button threeParameter;
    private Button fourParameter;
    private Button fiveParameter;

    void Awake() 
    {
        noParameter = transform.Find("NoParameter").GetComponent<Button>();
        oneParameter = transform.Find("OneParameter").GetComponent<Button>();
        twoParameter = transform.Find("TwoParameter").GetComponent<Button>();
        threeParameter = transform.Find("ThreeParameter").GetComponent<Button>();
        fourParameter = transform.Find("FourParameter").GetComponent<Button>();
        fiveParameter = transform.Find("FiveParameter").GetComponent<Button>();
    }

    void Start()
    {
        noParameter.onClick.AddListener(() => {
            EventCenter.Broadcast(EventEnum.ShowText0);
        });
        oneParameter.onClick.AddListener(() => {
            EventCenter.Broadcast(EventEnum.ShowText1, "hello world!!!");
        });
        twoParameter.onClick.AddListener(() => {
            EventCenter.Broadcast(EventEnum.ShowText2, "hello world!!!", 2.0f);
        });
        threeParameter.onClick.AddListener(() =>
        {
            EventCenter.Broadcast(EventEnum.ShowText3, "hello world!!!", 2.0f, 3);
        });
        fourParameter.onClick.AddListener(() =>
        {
            EventCenter.Broadcast(EventEnum.ShowText4, "hello world!!!", 2.0f, 3, "ming");
        });
        fiveParameter.onClick.AddListener(() =>
        {
            EventCenter.Broadcast(EventEnum.ShowText5, "hello world!!!", 2.0f, 3, "ming", 5);
        });
    }
}

ShowText:

/*
 * FileName:    ShowText
 * Author:      ming
 * CreateTime:  2023/6/28 18:07:46
 * Description:
 * 
*/
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class ShowText : MonoBehaviour
{
    void Awake() 
    {
        gameObject.SetActive(false);
        EventCenter.AddListener(EventEnum.ShowText0, ShowText0);
        EventCenter.AddListener<string>(EventEnum.ShowText1, ShowText1);
        EventCenter.AddListener<string, float>(EventEnum.ShowText2, ShowText2);
        EventCenter.AddListener<string, float, int>(EventEnum.ShowText3, ShowText3);
        EventCenter.AddListener<string, float, int, string>(EventEnum.ShowText4, ShowText4);
        EventCenter.AddListener<string, float, int, string, int>(EventEnum.ShowText5, ShowText5);
    }

    void OnDestroy()
    {
        EventCenter.RemoveListener(EventEnum.ShowText0, ShowText0);
        EventCenter.RemoveListener<string>(EventEnum.ShowText1, ShowText1);
        EventCenter.RemoveListener<string, float>(EventEnum.ShowText2, ShowText2);
        EventCenter.RemoveListener<string, float, int>(EventEnum.ShowText3, ShowText3);
        EventCenter.RemoveListener<string, float, int, string>(EventEnum.ShowText4, ShowText4);
        EventCenter.RemoveListener<string, float, int, string, int>(EventEnum.ShowText5, ShowText5);
    }

    void ShowText0() 
    {
        gameObject.SetActive(true);
        transform.GetComponent<Text>().text = "noParameter";
    }

    void ShowText1(string str) 
    {
        gameObject.SetActive(true);
        transform.GetComponent<Text>().text = str;
    }

    void ShowText2(string str, float a) 
    {
        gameObject.SetActive(true);
        transform.GetComponent<Text>().text = str + a;
    }

    public void ShowText3(string str, float a, int b)
    {
        gameObject.SetActive(true);
        GetComponent<Text>().text = str + a + b;
    }

    public void ShowText4(string str, float a, int b, string str2)
    {
        gameObject.SetActive(true);
        GetComponent<Text>().text = str + a + b + str2;
    }

    public void ShowText5(string str, float a, int b, string str2, int c)
    {
        gameObject.SetActive(true);
        GetComponent<Text>().text = str + a + b + str2 + c;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/696023.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微客云接口支持话费充值,大批量充值更方便

目前话费作为生活中的必需品&#xff0c;只要使用手机就一定会用到话费充值。 但是目前市场上可供话费充值的渠道比如&#xff1a;支付宝、微信、各大银行app&#xff0c;只能单笔充值&#xff0c;如果有几百几千个号码&#xff0c;只能单笔去充值&#xff0c;根本是不可能实现…

Qt实现拖拽功能(支持拖放文件、拖放操作)

目录 拖放Qt程序接受其他程序的拖拽部件/控件之间相互拖放总结 拖放 拖放是在一个应用程序内或者多个应用程序之间传递信息的一种直观的现代操作方式。除了为剪贴板提供支持外,通常它还提供数据移动和复制的功能。 拖放操作包括两个截然不同的动作:拖动和放下。Qt窗口部件可以…

津津乐道设计模式 - 享元模式详解(以影院座位举例让你快速掌握)

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

itext 7批量生成pdf文件并以压缩包形式下载

itext 7批量生成pdf文件并以压缩包形式下载 引入jar <dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.0.3</version><type>pom</type></dependency>代码实现–生成…

逻辑漏洞小结之SRC篇(值得收藏,反复看!)

​​​​​最近在挖各大src&#xff0c;主要以逻辑漏洞为主&#xff0c;想着总结一下我所知道的一些逻辑漏洞分享一下以及举部分实际的案例展示一下&#xff0c;方便大家理解。 主要从两个方面看&#xff0c;业务方面与漏洞方面。&#xff08;接下来就从拿到网站的挖掘步骤进行…

如何使用模板化消息进行客户服务?(参考salesmartly)

如何使用模板化消息进行客户服务&#xff1f;&#xff08;参考salesmartly&#xff09; 一整天一遍又一遍地重复相同的答案可能会很乏味&#xff0c;尤其是对于您的客户服务团队而言。模板化消息&#xff0c;也称为预制回复或回复模板&#xff0c;已成为许多客户服务团队必备功…

Kong(Without DB)的安装和基本使用

下载和安装 Docs 这里以Centos为例 sudo yum install kong-enterprise-edition-3.3.0.0.rpm配置 ​ Kong的官网提供了两个配置模式一个是 Using a database 另一个是使用 yaml配置文件的形式&#xff0c;安装好后默认配置文件默认是/etc/kong/kong.conf.default 二者对比 …

React V6实现类似与vue的eventBus

功能背景 想要实现类似于vue的eventBus的功能&#xff0c;由一个组件通知其他一个或多个组件。应用场景&#xff1a;比如一个可视化大屏的界面&#xff0c;当筛选条件变化的时候&#xff0c;要同时通知到大屏中所有图表一起变化。&#xff08;当然使用store也是可以的&#xff…

逻辑回归算法实现

目录 1.关于逻辑回归的原理解析和准备工作 2.关于激活函数 3.关于数据集 4.编写LogisticsRegression类 5.逻辑回归测试 6.结果 1.关于逻辑回归的原理解析和准备工作 逻辑回归原理相关内容&#xff0c;请参考博主的另一篇文章&#xff1a;机器学习&#xff08;二&#xff…

菜鸟重磅推出多款科技新品,“工业大脑”PLC国产化获突破

“决策参谋”供应链计划、“工业大脑”PLC、“智能制造”科技解决方案……6月28日&#xff0c;在2023全球智慧物流峰会上&#xff0c;菜鸟自研的一批新产品、新方案正式曝光。菜鸟物流科技深耕制造业的成绩单也在峰会期间公布&#xff0c;华晨宝马等一批头部汽车企业已与其展开…

六种提高自己工作效率的方法!

为什么同样的时间&#xff0c;同样的都在休息都在玩&#xff0c;而别人工作却在玩的同时已经完成了一大半了。究竟是怎么做到的呢&#xff1f; 不仅仅是因为别人的工作效率高&#xff0c;而是因为他们会巧用工具。 那么你肯定想知道&#xff0c;这款工具是什么样的呢&#xf…

hutool工具类实现excel上传 支持03和07

一直感觉excel表的导入有很多代码&#xff0c;写一次忘一次&#xff0c;类太多&#xff0c;要知道怎么获取Workbook、Sheet、Cell、row等等&#xff0c;这么多类不可能一直记的住&#xff0c;都是写过之后保存&#xff0c;使用的时候拿出来改改&#xff0c;更烦人的是针对offic…

Vue Router replace 编程式导航 缓存路由组件

6.9.路由跳转的 replace 方法 作用&#xff1a;控制路由跳转时操作浏览器历史记录的模式浏览器的历史记录有两种写入方式&#xff1a;push和replace push是追加历史记录replace是替换当前记录&#xff0c;路由跳转时候默认为push方式 开启replace模式 <router-link :replac…

松翰单片机keil环境芯片包

松翰单片机keil环境芯片包&#xff08;SN8F5700系列&#xff09;&#xff1a;安装时与Keil安装位置相同可以直接使用。 安装后依次点击可查看芯片包具体型号&#xff1a; 芯片包下载链接&#xff1a;阿里云盘分享https://www.aliyundrive.com/s/TnHchMhYeh1

Baumer工业相机堡盟工业相机如何通过BGAPISDK进行定序器编程:根据每次触发信号移动感兴趣区域(C++)

Baumer工业相机堡盟工业相机如何通过BGAPISDK进行定序器编程:根据每次触发信号移动感兴趣区域&#xff08;C&#xff09; Baumer工业相机Baumer工业相机BGAPISDK和定序器编程的技术背景Baumer工业相机通过BGAPISDK进行定序器编程功能1.引用合适的类文件2.Baumer工业相机通过BGA…

laravel+vue共用一个域名,使用目录区分接口和项目的nginx配置

1、打包好的项目&#xff1a; 首先将打包好的项目放置public下&#xff0c;如下图 2、nginx配置文件 不带注释的伪静态&#xff08;推荐&#xff09; 备注&#xff1a;若在 location /admin 中的 admin 后面不加 “斜杠/”&#xff0c;则会出现访问 /admin-user 路由&#x…

Oracle Net Services 配置:LISTENER:没有为主机 VM-16-7-centos 返回有效的 IP 地址

问题描述&#xff1a; [rootVM-16-7-centos oracle]# /etc/init.d/oracledb_ORCLCDB-21c configure Configuring Oracle Database ORCLCDB. 准备执行数据库操作 已完成 8% 复制数据库文件 已完成 31% 已完成 100% [FATAL] 正在对命令行参数进行语法分析: 参数"silent&quo…

CLIP论文详细解析

论文链接&#xff1a;Learning Transferable Visual Models From Natural Language Supervision&#xff08;通过自然语言处理的监督信号&#xff0c;学习可迁移的视觉模型&#xff09;. 代码链接&#xff1a;CLIP. CLIP 摘要1.引言2.方法3.实验4.与人对比实验5.数据集去重6.L…

汽车远程启动程序APP的设计与实现(源码+文档+报告+任务书)

以 CAN (Controller Local Network&#xff0c;简称 CAN&#xff09;为基础的车辆遥控起动技术&#xff0c;通过将车辆的 PBD接口与车辆的 CAN总线相连&#xff0c;并与相应的控制系统相连&#xff0c;实现对车辆遥控启动。 此系统主要使用了Java、Android Studio、 MySQL数据…

Visual studio(VS)运行障碍指北

文章目录 VS: ....Microsoft.CppCommon.targets: error MSB6006: “CL.exe”已退出-VS2017许可证过期VS下Visual Assist X番茄插件安装失败子工程引用&#xff08;无法解析的外部符号&#xff09;无法打开.ui文件&#xff08;qt&#xff09;VS中qt子工程无法加载 VS: …Microso…