用23种设计模式打造一个cocos creator的游戏框架----(二十三)中介者模式

1、模式标准

模式名称:中介者模式

模式分类:行为型

模式意图:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

结构图:

适用于:

1、一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以
理解。
2、一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
3、想定制一个分布在多个类中的行为,而又不想生成太多的子类。 

2、分析与设计 

游戏分单机和联机,单机模式下我们将命令直达操作对象。我让他动,他就得动。联机开发就不一样了,一般情况下我们的命令需要到服务器端走一下,和其他玩家的命令一起排个队再回来。这个时候你让他动,可能需要延迟个几十毫秒。在这里我们的通讯可能是这样的:

单机:本地指令发送--->本地指令执行

联机:本地指令发送--->上报服务器--->服务指令接受--->本地指令执行

一般情况下,单机开发和联机开发差别是比较大的。

这里我们思考能不能用中介者模式

单机:本地指令发送--->中介者发送(指令来自本地)--->本地指令接受执行

联机:

1、本地指令发送--->中介者发送(指令来自本地)--->服务端指令接受执行

2、服务指令发送--->中介者发送(指令来自服务)--->本地指令接受执行

这样一个单机游戏只需要修改中介者就变成了一个联机游戏

意图:用一个中介对象来封装一系列的(业务指令)对象交互。

3、开始打造

 中介者接口

// 中介者接口
export interface IMediator {sendCommand(commandWorker: ICommandWorker, commandText: string): void;
}

联机游戏命令中介者 

// 联机游戏命令中介者
export class NetworkMediator implements IMediator {private serverCommandWork: ICommandWorker = nullprivate localCommandWorks: ICommandWorker[] = []setServerCommandWork(serverCommandWork: ICommandWorker) {this.serverCommandWork = serverCommandWork}addLocalCommandWorks(localCommandWork: ICommandWorker) {this.localCommandWorks.push(localCommandWork)}sendCommand(commandWorker: ICommandWorker, commandText: string): void {if (commandWorker instanceof ServerCommandWorker) {console.log('来自ServerCommandWorker,则转到本地')this.localCommandWorks.forEach((_localCommandWork: ICommandWorker) => {_localCommandWork.receiveCommand(commandText)})} else {console.log('其他本地的全部转发到ServerCommandWorker')this.serverCommandWork.receiveCommand(commandText)}}
}

 指令工作者接口

export interface ICommandWorker {sendCommand(commandText: string): voidreceiveCommand(commandText: string): void
}

服务指令工作者 

// 服务指令工作者
export class ServerCommandWorker implements ICommandWorker {mediator: IMediatorconstructor(mediator: IMediator) {this.mediator = mediator}// 服务指令发送命令,通过中介者转发到本地指令sendCommand(commandText: string): void {this.mediator.sendCommand(this, commandText)}// 接受命令将它们发送到服务器端receiveCommand(commandText: string): void {// 发送到服务器console.log('发送到服务器,假设5秒后返回')setTimeout(() => {this.sendCommand(commandText)}, 5000)}
}

 本地指令之一

// 单位 操作命令 另一个单位
export class UnitCommandUnitCommandWorker implements ICommandWorker {command: ICommand = nullfromUnitItem: UnitItem<any> = nulltoUnitItem: UnitItem<any> = nullmediator: IMediatorconstructor(mediator: IMediator) {this.mediator = mediator}send(fromUnitItem: UnitItem<any>, commandId: string, toUnitItem: UnitItem<any>) {let commandText = '[[' + fromUnitItem.unitId + ']]' + '{{' + commandId + '}}' + '[[' + toUnitItem.unitId + ']]'this.sendCommand(commandText)}sendCommand(commandText: string) {this.mediator.sendCommand(this, commandText)}receiveCommand(commandText: string) {// 构建抽象语法树const expressions: IExpression[] = [];const regex = /\[\[([^\]]+)\]\]|\{\{([^\}]+)\}\}|\[\{([^\}]+)\}\]|\[<([^\>]+)>\]/g;let match;while ((match = regex.exec(commandText)) !== null) {console.log(match)const token = match[0];const unitItemId = match[1]; // 捕获组1中的内容为单位项IDconst commandId = match[2]; // 捕获组2中的内容为命令IDif (unitItemId !== undefined) {expressions.push(new UnitItemExpression(unitItemId));} else if (commandId !== undefined) {expressions.push(new CommandExpression(commandId));}}console.log('expressions', expressions)// “遍历解析”表达式const commandSequence = new CommandSequenceExpression(expressions);commandSequence.interpret(this);}.......
}

4、开始使用

const commandText = "[[UnitItem.20]]{{attackgroup}}[[UnitItem.21]]";// 创建中介者和服务指令和本地指令
const mediator = new NetworkMediator();
const serverCommandWorker = new ServerCommandWorker(mediator);
const unitCommandUnitCommandWorker = new UnitCommandUnitCommandWorker(mediator);mediator.setServerCommandWork(serverCommandWorker)
mediator.addLocalCommandWorks(unitCommandUnitCommandWorker)// 将一个本地指令发送出去(自动被中介者转发到服务指令,服务指令执行推送到服务器端)
unitCommandUnitCommandWorker.sendCommand(commandText)

5、升级优化

功能已经完成了,现在整合优化一下。

因为指令中介者,服务指令及本地指令都属于command。所以我们新建一个CommandManger来统一管理

/** 指令管理者 */
export class CommandManager {/** 指令中介者 */mediator: NetworkMediator/** 服务指令 */serverCommandWorker: ServerCommandWorker/** 常用的本地指令 */unitCommandUnitCommandWorker: UnitCommandUnitCommandWorkerconstructor() {this.mediator = new NetworkMediator();this.serverCommandWorker = new ServerCommandWorker(this.mediator);this.unitCommandUnitCommandWorker = new UnitCommandUnitCommandWorker(this.mediator)this.mediator.setServerCommandWork(this.serverCommandWorker)}/** 添加其他扩充的本地指令 */addLocalCommandWorks(otherCommandWorker: ICommandWorker) {this.mediator.addLocalCommandWorks(otherCommandWorker)}
}

在全局单例管理中添加

export class SingletonInstance {// 设计模式5(单例模式)private static _instance: SingletonInstance = new this()static get instance(): SingletonInstance {return this._instance}static getInstance() {return this._instance}// game: TCSGame// game: JCQGamegame: DemoGame......// 指令管理commandManager: CommandManager = new CommandManager()
}

在外观模式中添加一个快速入口

export class xhgame {// 设计模式10(外观模式)/** 当前游戏 */static get game() {return gameInstance.game};........../** 指令管理 */static get command() {return gameInstance.commandManager}
}

现在,我们游戏中方便的使用我们的指令了。比如玩家点击释放技能按钮事件触发

onClickSkill(){const commandText = "((player1Hero)){{skill1}}";xhgame.command.unitCommandUnitCommandWorker.sendCommand(commandText)
}

当然也可能游戏就是单机游戏不需要老转换指令(毕竟指令还是有点损耗的)。这个时候我们只需修改方法(也适合联网游戏)

            
onClickSkill(){xhgame.command.unitCommandUnitCommandWorker.sendCommandByObject(this.unitItem, 'skill', this.targetUnitItem)
}

 ok.

其他:

打算写好23种设计模式后,就将代码放到cocos store上。但前面的有些模式没有完全融合到框架内,可能还需要几天时间才能放出源码。

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

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

相关文章

Spring AOP入门指南:轻松掌握面向切面编程的基础知识

面向切面编程 1&#xff0c;AOP简介1.1 什么是AOP?1.2 AOP作用1.3 AOP核心概念 2&#xff0c;AOP入门案例2.1 需求分析2.2 思路分析2.3 环境准备2.4 AOP实现步骤步骤1:添加依赖步骤2:定义接口与实现类步骤3:定义通知类和通知步骤4:定义切入点步骤5:制作切面步骤6:将通知类配给…

1664:【例 2】取石子游戏 2

【题目描述】 有一种有趣的游戏&#xff0c;玩法如下&#xff1a; 玩家&#xff1a; 2 人&#xff1b; 道具&#xff1a; N 堆石子&#xff0c;每堆石子的数量分别为 X1,X2,…,Xn &#xff1b; 规则&#xff1a; 1、游戏双方轮流取石子&#xff1b; 2、每人每次选一堆石子…

蛋白质测序|蛋白测序-卡梅德生物

蛋白质测序是生物学研究中的一项重要技术&#xff0c;旨在揭示蛋白质的氨基酸序列&#xff0c;从而深入了解其结构和功能。蛋白质作为生物体内细胞结构和功能的主要执行者&#xff0c;对于解析细胞信号传导、代谢调控、细胞周期等生物学过程至关重要。通过蛋白质测序&#xff0…

DPDK单步跟踪(2)-如何编译debug版

第一步是要编译出debug版本的dpdk. 如何下载和编译&#xff0c;这里我不讲了。我们只讲debug https://doc.dpdk.org/guides/prog_guide/build-sdk-meson.htmlsetup meson setup --buildtypedebug debugbuild config meson configure -Dbuildtypedebug 就是这么很简单。 例…

【MYSQL】-数据类型

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

微波射频元件厂商恒利泰授权世强硬创代理,实现滤波器国产替代

据机构数据显示&#xff0c;2018-2022年全球射频元件市场规模从50亿美元增长至150亿美元&#xff0c;平均复合增长率为31.6%&#xff0c;预计到2025年市场规模有望超过302亿美元。其中&#xff0c;滤波器是射频前端各领域产值占比最高的产品。 为扩大产品覆盖率&#xff0c;提…

【密码学】使用mkcert安装CA、自签名ssl证书,配置nginx的https 证书笔记

文章目录 证书&#xff08;CA证书&#xff0c;服务端证书&#xff0c;客户端证书&#xff09;1 安装CA1.1 下载mkcert1.2 mkcert安装和配置根证书1.3设备安装根证书 2 自签名ssl证书2.1 CA 证书过程:2.2 自签名流程2.3签发证书2.4给已有的证书请求&#xff08;.csr&#xff09;…

C/C++工程中错误码定义总结

本文结合实际经验&#xff0c;参考ros2 rclcpp库中错误码定义及使用方式&#xff0c;梳理了一种基于C或C开发的接口下错误码的定义及处理方式。{本文不涉及跨系统、跨服务的定义} 1.错误码如何定义&#xff1f; 系统一般是按模块划分的&#xff0c;模块与模块之间通过调用与被…

PMP证书的PDU如何获得?

首先&#xff0c;让我们来了解一下PDU的含义。PDU代表专业发展单元&#xff08;Professional Development Unit&#xff09;&#xff0c;是指在获得认证后&#xff0c;您可以通过学习、授课或提供志愿服务来积累专业项目管理领域的学习时间。PDU以小时为单位计算&#xff0c;每…

idea 远程调试linux上的代码

背景介绍 开发过程中&#xff0c;我们经常会遇到部署的代码运行出问题、看日志由不是很直观、我们希望可以像调试本地代码一样去调试远程代码; IDEA提供了Remote工具,基于JVM的跨平台能力&#xff0c;我们可以远程调试部署的代码。 前提 保证远程和本地跑的代码是一致的 操…

音视频类App广告变现如何破局,最大化广告变现收益,让应用增收?

音视频App已然成为了我们日常获取、发布和交换信息的重要方式&#xff0c;在音视频行业不断的拓展中&#xff0c;用户的渗透率提升。 据数据显示&#xff0c;我国网络视听用户的规模已达9亿人次&#xff0c;网民使用率也突破了90%。庞大的市场规模和用户需求吸引了大批开发者和…

GBASE南大通用数据库提供的高可用负载均衡功能

GBASE南大通用GBase 8a ODBC 提供的高可用负载均衡功能是指&#xff0c;GBase 8a ODBC 会将客户 端请求的数据库集群连接平均分摊到集群所有可用的节点上。 GBASE南大通用数据库负载均衡的使用方法 GBASE南大通用GBase 8a ODBC 提供两种方式来使用高可用负载均衡。一种是配置数…

4. 行为模式 - 中介者模式

亦称&#xff1a; 调解人、控制器、Intermediary、Controller、Mediator 意图 中介者模式是一种行为设计模式&#xff0c; 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互&#xff0c; 迫使它们通过一个中介者对象进行合作。 问题 假如你有一个创建…

Linux ulimit配置

Linux ulimit配置 1. 简介 在Linux系统中&#xff0c;ulimit是一个强大的工具&#xff0c;用于控制用户进程可以使用的资源。然而&#xff0c;有时候我们可能会遇到需要关闭或者调整这些资源限制的情况&#xff0c;以满足特定需求。本文将介绍如何关闭Linux系统中的ulimit限制…

Java:打印当前线程的堆栈信息到错误流(error stream)

使用java.lang.Thread的静态方法dumpStack()&#xff0c;可以打印当前线程的堆栈信息到错误流&#xff08;error stream&#xff09;。 代码示例&#xff1a; package com.thb;public class Test5 {public static void main(String[] args) {Thread.dumpStack();}}运行输出&a…

Alpha突触核蛋白A53T 突变型PFF

Alpha 突触核蛋白A53T 突变型PFF Alpha 突触核蛋白 A53T PFFs (目录号 SPR-326) 在培养的原代大鼠神经元中诱导丝氨酸 129 磷酸化 培养基中 1 g/ml 超声处理的 StressMarq’s Alpha 突触核蛋白 A53T 突变 PFF&#xff08;目录号 SPR-326&#xff09; 可诱导原代大鼠神经元中 …

金融信贷场景的风险“要素”与主要“风险点”

目录 要素一:贷款对象 风险点1:为不具备主体资格或主体资格有瑕疵的借款人发放贷款 风险表现: 防控措施: 风险点2:向国家限控行业发放贷款 风险表现: 防控措施: 风险点3:受理不符合准入条件的客户申请 风险表现: 防控措施: 要素二:金额 风险点4:过渡授…

【已解决】使用fastjson返回给echarts的时候怎么不忽略null值?

问题复现&#xff1a; 在使用fastjson的将对象序列化成json字符串的时候&#xff0c;如果对象属性为null的话&#xff0c;会被忽略掉的。有时候&#xff0c;这些null值还是很有用的。比如我们在使用echarts的时候&#xff0c;返回给前端就不能因为null而忽略&#xff0c;如果忽…

二分查找法详解(6种变形)

前言 在之前的博客中&#xff0c;我给大家介绍了最基础的二分查找法&#xff08;没学的话点我点我&#xff01;&#xff09; 今天我将带大家学习二分法的六种变形如何使用&#xff0c;小伙伴们&#xff0c;快来开始今天的学习吧&#xff01; 文章目录 1&#xff0c;查找第一个…

求交错序列前N项和 C语言xdoj149

题目描述&#xff1a;编写程序&#xff0c;计算交错序列1-2/33/5-4/75/9-6/11…的前N项之和。 输入格式&#xff1a;输入一个正整数 输出格式&#xff1a;输出计算结果&#xff0c;结果保留三位小数 示例&#xff1a; 输入&#xff1a;5 输出&#xff1a;0.917 #include <st…