Unity3D 观察者模式

Unity3D 泛型事件系统

观察者模式

观察者模式是一种行为设计模式,通过订阅机制,可以让对象触发事件时,通知多个其他对象。

在游戏逻辑中,UI 界面通常会监听一些事件,当数据层发生变化时,通过触发事件,通知 UI 界面进行刷新。

定义事件类型

先进行简单的一步,创建 GameEventType.cs 脚本,定义一个枚举类型,可以在枚举中添加多个事件名。

public enum GameEventType
{PlayerAttack,  // 玩家攻击PlayerDeath,   // 玩家阵亡
}

事件管理器

接着,创建 EventManager.cs 脚本,定义多个泛型委托,这里声明了单参数和两个参数的委托,参数类型是泛型 T。

using System;
using System.Collections.Generic;// 单参数事件处理委托
public delegate void EventDelegate<T>(T param);// 两个参数的事件处理委托
public delegate void EventDelegate<T1, T2>(T1 param1, T2 param2);public class EventManager
{}

在 EventManager 类中,定义两个字典,分别存储单参数和两个参数的委托列表。

public class EventManager
{// 单参数事件的字典,键是事件类型,值是对应的事件处理器static Dictionary<int, Delegate> eventTableSingle = new Dictionary<int, Delegate>();// 两个参数事件的字典static Dictionary<int, Delegate> eventTableDouble = new Dictionary<int, Delegate>();
}

然后分别添加三个接口:订阅、取消订阅、触发。

  • 订阅,接收事件名和函数,判断字典中是否存在事件名,不存在则添加新的事件,然后把函数连接到委托中。
  • 取消订阅,接收事件名和函数,判断字典中是否存在事件名,存在则从委托中移除函数。
  • 触发,接收事件名和参数,判断字典中是否存在事件名,存在则取出委托并调用。

如果后续还需要三个参数,可以依此类推,添加字典和接口。

public class EventManager
{// ...// 订阅单参数事件public static void AddListener<T>(GameEventType gameEventType, EventDelegate<T> handler){int eventType = (int)gameEventType;if (!eventTableSingle.ContainsKey(eventType)){eventTableSingle.Add(eventType, null);}eventTableSingle[eventType] = (EventDelegate<T>)eventTableSingle[eventType] + handler;}// 取消订阅单参数事件public static void RemoveListener<T>(GameEventType gameEventType, EventDelegate<T> handler){int eventType = (int)gameEventType;if (eventTableSingle.ContainsKey(eventType)){eventTableSingle[eventType] = (EventDelegate<T>)eventTableSingle[eventType] - handler;}}// 触发单参数事件public static void Trigger<T>(GameEventType gameEventType, T param){int eventType = (int)gameEventType;if (eventTableSingle.ContainsKey(eventType)){var callback = eventTableSingle[eventType] as EventDelegate<T>;callback?.Invoke(param);}}// 订阅双参数事件public static void AddListener<T1, T2>(GameEventType gameEventType, EventDelegate<T1, T2> handler){int eventType = (int)gameEventType;if (!eventTableDouble.ContainsKey(eventType)){eventTableDouble.Add(eventType, null);}eventTableDouble[eventType] = (EventDelegate<T1, T2>)eventTableDouble[eventType] + handler;}// 取消订阅双参数事件public static void RemoveListener<T1, T2>(GameEventType gameEventType, EventDelegate<T1, T2> handler){int eventType = (int)gameEventType;if (eventTableDouble.ContainsKey(eventType)){eventTableDouble[eventType] = (EventDelegate<T1, T2>)eventTableDouble[eventType] - handler;}}// 触发双参数事件public static void Trigger<T1, T2>(GameEventType gameEventType, T1 param1, T2 param2){int eventType = (int)gameEventType;if (eventTableDouble.ContainsKey(eventType)){var callback = eventTableDouble[eventType] as EventDelegate<T1, T2>;callback?.Invoke(param1, param2);}}
}

添加和移除监听

创建 PlayerEvent.cs 脚本,在场景中也创建一个游戏物体,挂载该脚本。

添加事件监听

在 OnEnable 方法中,调用 EventManager.AddListener 添加事件监听。

在 OnDisable 方法中,调用 EventManager.RemoveListener 移除事件监听。

此时可以确定泛型参数的实际类型,并在回调函数中接收参数,进行逻辑处理。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerEvent : MonoBehaviour
{void OnEnable(){EventManager.AddListener<int>(GameEventType.PlayerAttack, OnPlayerAttack);EventManager.AddListener<string, int>(GameEventType.PlayerDeath, OnPlayerDeath);}void OnDisable(){EventManager.RemoveListener<int>(GameEventType.PlayerAttack, OnPlayerAttack);EventManager.RemoveListener<string, int>(GameEventType.PlayerDeath, OnPlayerDeath);}void OnPlayerAttack(int damage){Debug.Log($"玩家发起攻击,造成伤害 {damage}");}void OnPlayerDeath(string reason, int damage){Debug.Log($"玩家阵亡,原因 {reason},受到伤害 {damage}");}
}

触发事件

创建 PlayerEventTest.cs 脚本,在 Update 方法中,根据键盘按键,触发不同的事件。

这里定义 PlayerEvent 变量,按下 E 键对其游戏物体进行显示隐藏,是为了测试事件的添加和移除。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerEventTest : MonoBehaviour
{public PlayerEvent playerEvent;void Update(){if (Input.GetKeyDown(KeyCode.Q)){EventManager.Trigger(GameEventType.PlayerAttack, 100);}else if (Input.GetKeyDown(KeyCode.W)){EventManager.Trigger(GameEventType.PlayerDeath, "Boss攻击", 999);}else if (Input.GetKeyDown(KeyCode.E)){bool isActive = playerEvent.gameObject.activeInHierarchy;playerEvent.gameObject.SetActive(!isActive);}}
}

在场景中添加游戏物体,并挂载该脚本,拖拽引用。

触发事件

运行游戏:

  • 按下 Q 键触发了 PlayerAttack 事件
  • 按下 W 键触发了 PlayerDeath 事件
  • 按下 E 键隐藏了 PlayerEvent 游戏物体,同时事件被移除,不会再响应 Q 和 W 键,除非再次按下 E 键,显示游戏物体并添加事件。

如图所示:

运行效果

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

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

相关文章

网络参考模型总结

物理层&#xff1a; 电信号&#xff0c;有中继器、集线器等设备。 数据链路层&#xff1a;帧&#xff0c;有网桥、交换机等设备&#xff0c;通过mac地址。 网络层&#xff1a;包&#xff0c;有路由器等设备&#xff0c;通过IP地址。 传输层&#xff1a;段&#xff0c;有网…

权限管理系统的详细解析与实现

1. 权限管理的基础概念 1.1 什么是权限管理&#xff1f; 权限管理是指通过对用户身份的验证和权限的分配&#xff0c;控制用户对系统资源的访问范围和操作权限。它主要包括&#xff1a; 认证&#xff1a;确认用户的身份是否合法&#xff0c;通常通过用户名/密码或多因子验证…

rootless模式下istio ambient的流量管理测试

接上一篇rootless模式下istio ambient鉴权策略&#xff0c;本次测试管理流量的功能。 服务流量分割 Bookinfo应用程序有三个版本的reviews服务&#xff0c;接下来对这些版本进行分配流量控制测试。 longtdsubuntu:~$ kubectl get pod |grep reviews reviews-v1-746f96c9d4-2…

多入口+vite+vue3预渲染方案

如果你的项目要求加载速度要快,我们如果使用传统的vue3+sfc模式去开发,因为只有一个根节点,空白页面加载出来之后js才回去加载组件渲染,这样页面总是有一个短暂的空白。我们这里不讨论服务器端ssr和预渲染方案,仅仅是为了满足比较极端的优化需求,在这种情况下我的这套方案…

出类拔萃的四款录屏工具你pick哪一个?

在这个屏幕时代&#xff0c;录屏已经成为我们日常工作和娱乐中不可或缺的一部分。无论是为了记录游戏精彩瞬间&#xff0c;还是为了分享教程和经验&#xff0c;一个好的录屏工具都是必不可少的。今天&#xff0c;就让我为大家推荐几款电脑免费录屏工具&#xff0c;并分享一下使…

DDD通用语言、多尿和尿频-《分析模式》漫谈41

“Analysis Patterns”的第3章有这么一句&#xff1a; A patient with observations of the presence of thirst, weight loss, and polyuria indicates diabetes. 2004&#xff08;机械工业出版社&#xff09;中译本的译文为&#xff1a; 虽然没有完全使用类图&#xff08;类…

Ajax是什么?

Ajax是什么&#xff1f; Ajax是创建交互式网页应用的网页开发技术。简单来说就是网页在不加载的情况下&#xff0c;可以跟服务器交换数据&#xff0c;并更新页面的内容。 原理&#xff1a; 1. 创建xhr&#xff08;xmlHttpRequest&#xff09;对象; 2, 通过xhr对象的open()方法和…

【C++_string类练习】仅仅反转字母

题目链接&#xff1a;仅仅反转字母 解题思路&#xff1a; 这种反转字符的题目我第一个想到的方法就是&#xff1a;双指针 一个指针在前start&#xff0c;一个指针在后back&#xff0c; 如果指针所指向的位置的值是字母&#xff0c;那么两个指针位置的值就进行交换&#xff0…

矿山企业电能智能化管理怎么实现

0引言 电力工业是国民经济和社会发展的基础&#xff0c;随着高质量发展要求的提升&#xff0c;提高能源效率对工业企业至关重要。有效提高电力能效的方法包括技术创新、监测用能行为和探索技术解决方案。智能化的电能管理是应对资源和环境压力的关键措施&#xff0c;对提高用电…

点餐小程序实战教程20广告管理

目录 1 创建数据源2 添加轮播容器3 创建变量4 绑定变量5 预览应用总结 一般餐厅需要有一些宣传&#xff0c;在我们的点餐页面可以在顶部加载广告位。广告主要是用轮播图的形式进行展示&#xff0c;本节我们介绍一下如果显示广告。 1 创建数据源 打开控制台&#xff0c;点击应用…

10-Docker安装Redis

10-Docker安装Redis Docker安装Redis 以 Redis 6.0.8 为例&#xff1a; docker pull redis:6.0.8直接pull会出现以下错误 [rootdocker ~]# docker pull redis:6.0.8 Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request can…

ubuntu安装docker及docker compose

在Ubuntu上安装Docker通常包括以下几个步骤&#xff1a; 更新包索引&#xff1a; 打开终端并运行以下命令来更新你的包索引&#xff1a; sudo apt update安装必要的依赖&#xff1a; 安装apt-transport-https、ca-certificates、curl、software-properties-common、gnupg等软件…

Java基础-8新特性

Lambda 表达式 Lambda 表达式&#xff0c;也可称为闭包。Lambda 表达式是一个匿名函数&#xff0c;我们可以这样理解 Lambda 表达式&#xff1a;Lambda 是一段可以传递的代码&#xff08;能够做到将代码像数据一样进行传递&#xff09;。格式如下&#xff1a; (parameters) -&g…

【Cadence27】HDL拷贝工程➕Allegro导出DXF和3D文件STP

【转载】Cadence Design Entry HDL 使用教程 【Cadence01】Cadence PCB Edit相对延迟与绝对延迟的显示问题 【Cadence02】Allegro引脚焊盘Pin设置为透明 【Cadence03】cadence不小心删掉钢网层怎么办&#xff1f; 【Cadence04】一般情况下Allegro PCB设计时的约束规则设置&a…

【入门篇】2.8 时钟(三)

目录 5.3.1 配置时钟源相关参数:调用函数 HAL_RCC_OscConfig() 5.3.2 配置系统时钟源以及 SYSCLK、AHB、APB1 和 APB2 的分频系数:调用函数HAL_RCC_ClockConfig() 六,STM32F4 时钟使能和配置 6.1 使能时钟 6.2 禁用时钟 5.3.1 配置时钟源相关参数:调用函数 HAL_RCC_Os…

【力扣 | SQL题 | 每日4题】力扣2308,2324,2346,2372

4 mid&#xff0c;还是比较常规的。 1. 力扣2308&#xff1a;按性别排列表格 1.1 题目&#xff1a; 表: Genders ---------------------- | Column Name | Type | ---------------------- | user_id | int | | gender | varchar | ---------------------- u…

卸载 Git

目录 打开 Geek找到 Git右键卸载 Git卸载完成等待扫描注册表点击完成清除Git在本地的配置文件 打开 Geek 找到 Git 右键卸载 Git 卸载完成 等待扫描注册表 点击完成 已经删除完毕 清除Git在本地的配置文件 进入 C:\Users\用户名 删除框起来的配置文件 &#x1f447; 源码资料…

Map转Map

将一个Map<String,Integer> 转换成Map<String,String> //创建一个创建Map的公共方法 private static <T> Map<String,T> newMap(String key,T val,Object...kv){Map<String,T> ans new HashMap<>(8);ans.put(key,val);for(int i0,size k…

webpack自定义插件 ChangeScriptSrcPlugin

插件文件 class ChangeScriptSrcPlugin {apply(compiler) {const pluginName "ChangeScriptSrcPlugin";compiler.hooks.compilation.tap(pluginName, (compilation, callback) > {compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(pluginName,(html…

DES对称加密算法

DES&#xff08;Data Encryption Standard&#xff0c;数据加密标准&#xff09;是一种对称加密算法。 算法概述 加密类型&#xff1a;对称加密&#xff08;同一密钥用于加密和解密&#xff09;。密钥长度&#xff1a;64位&#xff08;8字节&#xff09;&#xff0c;其中有效…