【行为型之中介者模式】游戏开发实战——Unity复杂系统协调与通信架构的核心秘诀
文章目录
- 🕊️ 中介者模式(Mediator Pattern)深度解析
- 一、模式本质与核心价值
- 二、经典UML结构
- 三、Unity实战代码(成就系统协调)
- 1. 定义中介者接口与同事基类
- 2. 实现具体同事类
- 3. 实现具体中介者
- 4. 客户端使用
- 四、模式进阶技巧
- 1. 事件总线集成
- 2. 状态依赖管理
- 3. 优先级路由
- 五、游戏开发典型应用场景
- 六、性能优化策略
- 七、模式对比与选择
- 八、最佳实践原则
- 九、常见问题解决方案
🕊️ 中介者模式(Mediator Pattern)深度解析
——以Unity实现跨系统通信与复杂UI协调为核心案例
一、模式本质与核心价值
核心目标:
✅ 解耦对象间的直接依赖,通过中介者集中管理交互
✅ 简化网状通信为星型结构,提升系统可维护性
✅ 统一控制交互逻辑,便于扩展新通信规则
关键术语:
- Mediator(中介者接口):定义对象间通信的接口
- ConcreteMediator(具体中介者):实现协调逻辑
- Colleague(同事类):需要交互的对象,依赖中介者
数学表达:
设有N个对象,原始耦合度为O(N²),引入中介者M后降为O(N)
二、经典UML结构
三、Unity实战代码(成就系统协调)
1. 定义中介者接口与同事基类
public interface IMediator {void Notify(object sender, string eventType, object data = null);
}public abstract class Colleague : MonoBehaviour {[SerializeField] protected IMediator mediator;public void SetMediator(IMediator mediator) {this.mediator = mediator;}
}
2. 实现具体同事类
// 成就系统
public class AchievementSystem : Colleague {public void Unlock(string achievementID) {Debug.Log($"解锁成就:{achievementID}");mediator?.Notify(this, "AchievementUnlocked", achievementID);}
}// UI弹窗系统
public class UIPopup : Colleague {public void ShowAchievementPopup(string title) {Debug.Log($"显示成就弹窗:{title}");}
}// 音效系统
public class SFXSystem : Colleague {public void Play(string clipName) {Debug.Log($"播放音效:{clipName}");}
}
3. 实现具体中介者
public class AchievementMediator : IMediator {private AchievementSystem _achievement;private UIPopup _popup;private SFXSystem _sfx;public AchievementMediator(AchievementSystem a, UIPopup u, SFXSystem s) {_achievement = a;_popup = u;_sfx = s;_achievement.SetMediator(this);_popup.SetMediator(this);_sfx.SetMediator(this);}public void Notify(object sender, string eventType, object data) {switch(eventType) {case "AchievementUnlocked":HandleAchievementUnlocked(data.ToString());break;case "PopupClosed":HandlePopupClosed();break;}}private void HandleAchievementUnlocked(string id) {_popup.ShowAchievementPopup(GetAchievementTitle(id));_sfx.Play("AchievementUnlocked");SaveSystem.SaveAchievement(id);}private void HandlePopupClosed() {_sfx.Play("ButtonClick");}
}
4. 客户端使用
public class GameManager : MonoBehaviour {[SerializeField] private AchievementSystem achievement;[SerializeField] private UIPopup popup;[SerializeField] private SFXSystem sfx;private IMediator _mediator;void Start() {_mediator = new AchievementMediator(achievement, popup, sfx);}void Update() {if(Input.GetKeyDown(KeyCode.A)) {achievement.Unlock("KILL_100_ENEMIES");}}
}
四、模式进阶技巧
1. 事件总线集成
public class EventMediator : IMediator {private Dictionary<Type, List<Action<object>>> _handlers = new();public void Subscribe<T>(Action<T> handler) {var type = typeof(T);if(!_handlers.ContainsKey(type)) {_handlers[type] = new List<Action<object>>();}_handlers[type].Add(obj => handler((T)obj));}public void Notify(object sender, string eventType, object data) {if(_handlers.TryGetValue(data.GetType(), out var handlers)) {foreach(var h in handlers) h(data);}}
}
2. 状态依赖管理
public class StateDependentMediator : IMediator {private Dictionary<GameState, Action<object>> _stateHandlers = new();public void RegisterStateHandler(GameState state, Action<object> handler) {_stateHandlers[state] = handler;}public void Notify(object sender, string eventType, object data) {if(_stateHandlers.TryGetValue(GameManager.CurrentState, out var handler)) {handler(data);}}
}
3. 优先级路由
public class PriorityMediator : IMediator {private List<(int priority, Action<object>)> _handlers = new();public void AddHandler(int priority, Action<object> handler) {_handlers.Add((priority, handler));_handlers.Sort((a,b) => b.priority.CompareTo(a.priority));}public void Notify(object sender, string eventType, object data) {foreach(var h in _handlers) {h.handler(data);}}
}
五、游戏开发典型应用场景
-
UI系统协调
public class UIMediator : IMediator {public void Notify(object sender, string eventType) {switch(eventType) {case "InventoryOpened":PauseGame();HideHUD();break;case "SettingsClosed":ResumeGame();ShowHUD();break;}} }
-
多人游戏同步
public class NetworkMediator : IMediator {public void Notify(object sender, string eventType, object data) {switch(eventType) {case "PlayerMove":BroadcastToAllClients(data);break;case "ChatMessage":ProcessChatMessage(data);break;}} }
-
任务系统协调
public class QuestMediator : IMediator {public void Notify(object sender, string eventType, Quest quest) {switch(eventType) {case "QuestStarted":UpdateQuestLog();PlayQuestStartSFX();break;case "QuestCompleted":GrantRewards();UpdateNPCdialogue();break;}} }
-
车辆控制系统
public class VehicleMediator : IMediator {public void Notify(object sender, string eventType) {if(sender is Engine && eventType == "Overheat") {ThrottleControl.ReducePower();CoolingSystem.ActivateEmergencyCooling();}} }
六、性能优化策略
策略 | 实现方式 | 适用场景 |
---|---|---|
事件过滤 | 提前终止不相关事件处理 | 高频事件系统 |
批处理 | 合并多个通知为单个操作 | 物理系统更新 |
缓存路由表 | 预生成事件处理映射 | 固定事件类型 |
异步处理 | 使用UniTask处理耗时操作 | 网络通信场景 |
七、模式对比与选择
维度 | 中介者模式 | 观察者模式 |
---|---|---|
关注点 | 集中协调 | 松散通知 |
耦合度 | 同事类依赖中介者 | 发布者/订阅者解耦 |
复杂度 | 较高(需维护中介者) | 较低 |
典型场景 | 复杂交互协调 | 简单事件通知 |
八、最佳实践原则
- 职责分离:避免中介者成为"上帝对象"
- 接口最小化:
public interface IGameMediator {void PlayerAction(PlayerActionType action);void UIEvent(UIEventType event); }
- 模块化中介者:
public class CompositeMediator : IMediator {private List<IMediator> _subMediators = new();public void AddMediator(IMediator m) => _subMediators.Add(m);public void Notify(object sender, string eventType, object data) {foreach(var m in _subMediators) m.Notify(sender, eventType, data);} }
- 异常隔离:
public void Notify(...) {try {// 处理逻辑} catch(Exception e) {Debug.LogError($"中介者处理异常:{e.Message}");} }
九、常见问题解决方案
Q1:如何防止中介者过度膨胀?
→ 实现分层中介者
public class HierarchyMediator : IMediator {private Dictionary<SystemType, IMediator> _subMediators = new();public void Notify(...) {if(_subMediators.TryGetValue(GetSystemType(sender), out var mediator)) {mediator.Notify(sender, eventType, data);}}
}
Q2:如何处理循环通知?
→ 实现事件标记
public class Notification {public string Type;public bool IsProcessed;
}public void Notify(...) {if(notification.IsProcessed) return;notification.IsProcessed = true;// 处理逻辑
}
Q3:如何调试复杂事件流?
→ 实现事件追踪器
public class DebugMediator : IMediator {public void Notify(object sender, string eventType, object data) {Debug.Log($"[{Time.time}] 事件:{eventType} 发送者:{sender.GetType().Name}");// 传递给实际中介者...}
}
上一篇 【行为型之迭代器模式】游戏开发实战——Unity高效集合遍历与场景管理的架构精髓
下一篇 【行为型之备忘录模式】游戏开发实战——Unity存档系统与状态管理的终极解决方案
相关文章:
【行为型之中介者模式】游戏开发实战——Unity复杂系统协调与通信架构的核心秘诀
文章目录 🕊️ 中介者模式(Mediator Pattern)深度解析一、模式本质与核心价值二、经典UML结构三、Unity实战代码(成就系统协调)1. 定义中介者接口与同事基类2. 实现具体同事类3. 实现具体中介者4. 客户端使用 四、模式…...
分布式微服务系统架构第125集:AI大模型
加群联系作者vx:xiaoda0423 仓库地址:https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ 一、user 表(用户表) sql 复制编辑 create table if not exists user (id bigint auto_increment comment id pri…...

MySQL 8.0 OCP 英文题库解析(三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题16~25 试题16:…...
MapReduce 模型
引言 MapReduce 是分布式计算领域的里程碑式模型,由 Google 在 2004 年论文中首次提出,旨在简化海量数据处理的复杂性。其核心思想是通过函数式编程的 Map (映射)和 Reduce (归约)阶段&#x…...

Docker容器启动失败?无法启动?
Docker容器无法启动的疑难杂症解析与解决方案 一、问题现象 Docker容器无法启动是开发者在容器化部署中最常见的故障之一。尽管Docker提供了丰富的调试工具,但问题的根源往往隐藏在复杂的配置、环境依赖或资源限制中。本文将从环境变量配置错误这一细节问题入手&am…...
mysql dump 导入导出用法
导出 指定库中指定的表 mysqldump -uroot -pmysql databasename table1 table2 > ./bak.sql 导入 mysql -uroot -p123456 databasename< ./bak.sql 导出指定数据库 mysqldump -uroot -p123456 databasename > ./databasename.sql 导入: mysql -uroot…...

MySQL 数据类型全面指南:从理论到实践
在数据库设计和开发中,数据类型的选择是构建高效、可靠系统的基石。MySQL作为最流行的关系型数据库之一,提供了丰富的数据类型以满足各种数据存储需求。本文将全面介绍MySQL的数据类型体系,通过理论讲解和实际示例,帮助开发者做出…...
第二课:ESP32 使用 PWM 渐变控制——实现模拟呼吸灯或音调变化
第二课:ESP32 使用 PWM 渐变控制——实现模拟呼吸灯或音调变化 🧠 一、PWM 占空比与亮度/音量控制原理 PWM(Pulse Width Modulation,脉宽调制)是一种常用的数字信号控制方式,广泛应用于 LED 灯光亮度、电…...
Quartus与Modelsim-Altera使用手册
目录 文章内容: 视频内容: Quartus: ModelSim: 顶层设计与子模块: 只是对所查阅的相关文章的总结与视频总结 文章内容: 这篇对基础操作很详细: 一、Quartus II软件的使用_quartus2软件上…...

uniapp(微信小程序)>关于父子组件的样式传递问题(自定义组件样式穿透)
在父组件中给子组件添加类名,子组件的样式由父组件决定 由于"微信小程序"存在【样式隔离机制】,且默认设置为isolated(启用样式隔离),因此这里给出以下两种解决方案: // 小程序编译机制 1. 当 <style scoped> 存在时&#…...

【HCIA】BFD
前言 前面我们介绍了浮动路由以及出口路由器的默认路由配置,可如此配置会存在隐患,就是出口路由器直连的网络设备并不是运营商的路由器,而是交换机。此时我们就需要感知路由器的存活状态,这就需要用到 BFD(Bidirectio…...

计算机视觉最不卷的方向:三维重建学习路线梳理
提到计算机视觉(CV),大多数人脑海中会立马浮现出一个字:“卷”。卷到什么程度呢?2022年秋招CV工程师岗位数下降了16%,但求职人数增加了23%,求职人数与招聘岗位的比例达到了恐怖的15:1࿰…...

android抓包踩坑记录
由于需要公司业务需求,需要抓取APP中摄像机插件的网络包,踩了两天坑,这里做个总结吧。 事先准备 android-studio emulatesdk 需要android模拟器和adb调试工具。如果已经有其他模拟器的话,可以只安装adb调试工具即可 mitmproxy…...

Webpack其他插件
安装html打包插件 const path require(path); const HtmlWebpackPlugin require(html-webpack-plugin) module.exports {entry: path.resolve(__dirname,src/login/index.js),output: {path: path.resolve(__dirname, dist),filename: ./login/index.js,clean:true},Plugin:…...
如何正确地写出单例模式
如何正确地写出单例模式 | Jarks Blog 枚举方式: public class SingletonObject {private SingletonObject() {}/*** 枚举类型是线程安全的,并且只会装载一次*/private enum Singleton {INSTANCE;private final SingletonObject instance;Singleton() {…...
常见相机焦段的分类及其应用
相机焦段是指镜头的焦距范围,决定了拍摄时的视角、画面范围和透视效果。不同焦段适合不同的拍摄场景和主题,以下是常见焦段的分类及其应用: 一、焦段的核心概念 焦距:镜头光学中心到成像传感器的距离(单位:…...

Python Matplotlib 库【绘图基础库】全面解析
让AI成为我们的得力助手:《用Cursor玩转AI辅助编程——不写代码也能做软件开发》 一、发展历程 Matplotlib 由 John D. Hunter 于 2003 年创建,灵感来源于 MATLAB 的绘图系统。作为 Python 生态中最早的可视化工具之一,它逐渐成为科学计算领…...

C++ string数据查找、string数据替换、string子串获取
string查找示例见下,代码见下,以及对应运行结果见下: #include<iostream>using namespace std;int main() {// 1string s1 "hellooooworld";cout << s1.find("oooo") << endl;// 2cout << (in…...
入侵检测SNORT系统部署过程记录
原理背景知识: 一、入侵检测系统介绍 1、入侵检测系统 入侵检测(Intrusion Detection) 指通过对计算机网络或计算机系统中的若干关键点收集信息并对其进行分析,从中发现网络或系统中是否有违反安全策略的行为和被攻击的迹象。 入侵检测系统(IDS) 是从网络和系统中收集…...
使用 Java 反射动态加载和操作类
Java 的反射机制(Reflection)是 Java 语言的一大特色,它允许程序在运行时检查、加载和操作类、方法、字段等元信息。通过 java.lang.Class 和 java.lang.reflect 包,开发者可以动态加载类、创建实例、调用方法,甚至在运行时构造新类。反射是 Java 灵活性的核心,广泛应用于…...

关于甲骨文(oracle cloud)丢失MFA的解决方案
前两年,申请了一个招商的多币种信用卡,然后就从网上撸了一个oracle的免费1h1g的服务器。 用了一段时间,人家要启用MFA验证。 啥叫MFA验证,类似与短信验证吧,就是绑定一个手机,然后下载一个app,每…...
vue3项目中使用CodeMirror组件的详细教程,中文帮助文档,使用手册
简介 这是基于 Vue 3 开发的 CodeMirror 组件。该组件基于 CodeMirror 5 开发,仅支持 Vue 3。 除了支持官方提供的各种语法模式外,还额外添加了日志输出展示模式,开箱即用,但不一定适用于所有场景。 如需完整文档和更多使用案例…...
C++ stl中的list的相关函数用法
文章目录 list的介绍list的使用定义方式 插入和删除迭代器的使用获取元素容器中元素个数和容量的控制其它操作函数 list的使用,首先要包含头文件 #include <list>list的介绍 1.list是一种可以在常数范围内在链表中的任意位置进行插入和删除的序列式容器&…...

【网络编程】七、详解HTTP 搭建HTTP服务器
文章目录 Ⅰ. HTTP协议的由来 -- 万维网Ⅱ. 认识URL1、URL的格式协议方案名登录信息 -- 忽略服务器地址服务器端口号文件路径查询字符串片段标识符 2、URL的编码和解码 Ⅲ. HTTP的报文结构1、请求协议格式2、响应协议格式🎏 写代码的时候,怎么保证请求和…...

[Java实战]Spring Boot 快速配置 HTTPS 并实现 HTTP 自动跳转(八)
[Java实战]Spring Boot 快速配置 HTTPS 并实现 HTTP 自动跳转(八) 引言 在当今网络安全威胁日益严峻的背景下,为 Web 应用启用 HTTPS 已成为基本要求。Spring Boot 提供了简单高效的方式集成 HTTPS 支持,无论是开发环境测试还是生产环境部署࿰…...
Spring Boot 中的重试机制
Retryable 注解简介 Retryable 注解是 Spring Retry 模块提供的,用于自动重试可能会失败的方法。在微服务架构和分布式系统中,服务之间的调用可能会因为网络问题、服务繁忙等原因失败。使用 Retryable 可以提高应用的稳定性和容错能力 1。 使用步骤 &…...
【React中useRef钩子详解】
一、useRef的核心特性 useRef是React提供的Hook,用于在函数组件中创建可变的持久化引用,具有以下核心特性: 持久化存储 返回的ref对象在组件整个生命周期内保持不变,即使组件重新渲染,current属性的值也不会丢失。无触发渲染 修改ref.current的值不会导致组件重新渲染,适…...
golang-ErrGroup用法以及源码解读笔记
介绍 ErrGroup可以并发执行多个goroutine,并可以很方便的处理错误 与sync.WaitGroup相比 错误处理 sync.WaitGroup只负责等待goroutine执行完成,而不处理返回值或者错误errgroup.Group目前虽然不能直接处理函数的返回值或错误。但是当goroutine返回错…...
17.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--微服务基础工具与技术--loki
在微服务中,日志是非常重要的组成部分。它不仅可以帮助我们排查问题,还可以帮助我们分析系统的性能和使用情况。 一、loki简介 loki是一个开源的日志聚合系统,它可以帮助我们高效地收集、存储和分析日志数据。loki的设计理念是“简单、快速…...

CVPR计算机视觉顶会论文解读:IPC-Dehaze 如何解决真实场景去雾难题
【CVPR 2025】迭代预测-评判编解码网络:突破真实场景去雾的极限 摘要 本文提出了一种名为IPC-Dehaze的创新去雾方法,通过迭代预测-评判框架和码本解码机制,有效解决了现有去雾算法在复杂场景下的性能瓶颈。该方法在多个基准测试中取得了SOT…...