当前位置: 首页 > news >正文

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.lryc.cn/news/463598.html

相关文章:

  • vue从0开始的项目搭建(含环境配置)
  • 力扣61~65题
  • API接口开发流程与指南
  • 如何在Android中进行日志打印和调试?
  • Linux基本使用和程序部署
  • 照片编辑成动态视频用什么软件好
  • JavaWeb合集-SpringBoot项目配套知识
  • Electron入门笔记
  • python 不相交集简介(并查集算法)【Introduction to Disjoint Set (Union-Find Algorithm)】
  • 23种设计模式之工厂方法模式
  • Redis——事务
  • Redis非关系型数据库操作命令大全
  • 基于SpringBoot+Vue+uniapp微信小程序的澡堂预订的微信小程序的详细设计和实现
  • Linux mips架构链接库函数调用plt表汇编代码分析
  • python 作业1
  • Apache 出现 “403 forbidden“ 排查方法
  • vue video播放m3u8监控视频
  • uniapp 获取签名证书 SHA1 自有证书签名打包
  • Open3d开发点云标注工具问题总结(二)
  • 【FreeRTOS】
  • 洛谷 P4995:跳跳! ← 贪心算法
  • 代理 IP 在 AI 爬虫中的关键应用
  • 【Vercel】Vercel静态部署踩坑
  • 【Spring】关于Spring中aware相关接口的作用
  • 动态内存管理及RAII的简单应用
  • 7、Vue2(一)
  • Chapter11
  • LLAMA2入门(一)-----预训练
  • 使用poi-tl动态写入目录更新问题解决
  • OpenCV高级图形用户界面(9)更改指定窗口的位置函数moveWindow()的使用