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

这篇具有很好参考价值的文章主要介绍了Unity 事件监听与广播(高度解耦合,观察者模式)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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. 测试

创建场景:
unity广播监听,Unity 设计模式,unity,观察者模式,c#,设计模式
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:文章来源地址https://www.toymoban.com/news/detail-517405.html

/*
 * 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;
    }
}

到了这里,关于Unity 事件监听与广播(高度解耦合,观察者模式)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C# 基于事件的观察者模式

    观察者模式是一种软件设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,它的所有依赖者(观察者)都将得到通知并自动更新。这种模式通过解耦合主题和观察者来提高对象的灵活性。 定义 观察者模式包括一个主题(Subject)和多个观察者(

    2024年02月03日
    浏览(31)
  • 【C#学习笔记】委托与事件 (从观察者模式看C#的委托与事件)

    转载请注明出处:🔗https://blog.csdn.net/weixin_44013533/article/details/134655722 作者:CSDN@|Ringleader| 主要参考: 委托(C# 编程指南) 事件介绍 C# 中的委托和事件简介 Delegate 类 Exploring the Observer Design Pattern微软技术文章翻译 委托是一种 引用类型 ,表示对具有特定参数列表和返回类型

    2024年02月04日
    浏览(38)
  • Unity 观察者模式(实例详解)

    在Unity中实现观察者模式,我们可以创建一个Subject(目标/主题)类,它负责维护订阅者列表,并且当其状态改变时通知所有观察者。下面通过5个代码示例来详细展示如何在Unity C#脚本中应用观察者模式: 示例1 - 简单的文本更新通知 示例2 - 多观察者监听游戏分数变化 示例

    2024年02月21日
    浏览(19)
  • Unity设计模式之观察者模式

      在平常玩游戏的时候会遇到这种情况,以简单的Rpg举例。 勇者击杀了怪物,怪物死了,勇者摆出胜利姿势,系统提示怪物死亡 。如果按照一般逻辑可能会在怪物死亡的方法中去获取Player、Dialog,这样看上去其实也不太难。但如果需要去关联的事件很多,就需要在类中去获

    2024年02月06日
    浏览(22)
  • 【解决】MissingReferenceException: The object of type ‘GameObject‘ has been destroyed 观察者模式 监听物体被销毁

    MissingReferenceException: The object of type ‘Text’ has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object. 该情况发生于我的 观察者模式 在 重新加载当前场景 时 监听的物体被 销毁 如上所示错误,通过分析,定位到错误是在观察

    2024年02月11日
    浏览(34)
  • 【Unity实战100例】人物状态栏UI数据刷新—MVC观察者模式

    目录 一.创建Model层数据模型 二.创建View层关联UI组件 三.创建Controller层使得V和M数据关联 源码:

    2024年02月13日
    浏览(22)
  • 【C++ 观察者模式 思想理解】C++中的观察者模式:松耦合设计与动态交互的艺术,合理使用智能指针观察者

    在进入技术细节之前,理解观察者模式(Observer Pattern)的基本概念和它在现代编程中的重要性是至关重要的。 观察者模式是一种设计模式,它定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。在C++中,这个

    2024年01月24日
    浏览(39)
  • 观察者模式(上):详解各种应用场景下观察者模式的不同实现方式

            从今天起,我们开始学习行为型设计模式。我们知道,创建型设计模式主要解决“对象的创建”问题,结构型设计模式主要解决“类或对象的组合或组装”问题,那行为型设计模式主要解决的就是“ 类或对象之间的交互 ”问题。 原理及应用场景剖析 在对象之间

    2024年02月16日
    浏览(32)
  • 02观察者模式

    让对象保持消息灵通 一个WeatherData对象负责追踪目前的天气状况(温度,湿度,气压)。希望你们能建立一个应用,有三种布告板,分别显示目前的状况、气象统计及简单的预报。当WeatherObject对象获得最新的测量数据时,三种布告板必须实时更新。而且,这是一个可以扩展的

    2023年04月12日
    浏览(30)
  • 观察者模式实战

    场景 假设创建订单后需要发短信、发邮件等其它的操作,放在业务逻辑会使代码非常臃肿,可以使用观察者模式优化代码 代码实现 自定义一个事件 发送邮件 发送短信 最后再创建订单的业务逻辑进行监听,创建订单 假设后面还需要做其它的监听,再重新定义一个监听类即可

    2024年02月13日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包