用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.hqwc.cn/news/292378.html

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

相关文章

众和策略股市行情分析:股市里什么叫外资?外资股是什么?

股市里什么叫外资&#xff1f; 外资就是指的国外本钱&#xff0c;即国外出资者参与到我国股市所投入的本钱。通常情况下&#xff0c;外资主要靠直接或直接持有我国股市的股票来达成注入本钱的意图。比较于内资&#xff0c;外资更多的注重于我国商场的整体情况&#xff0c;采用…

算法模板之栈图文详解

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;算法模板、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. ⛳️模拟栈1.1 &#x1f514;用数组模拟实现栈1.1.1 &#x1f47b;栈的定义1.1.…

华为端口隔离简单使用方法同vlan下控制个别电脑不给互通

必须得用access接口&#xff0c;hybrid口不行 dhcp enable interface Vlanif1 ip address 192.168.1.1 255.255.255.0 dhcp select interface interface MEth0/0/1 interface GigabitEthernet0/0/1 port link-type access port-isolate enable group 1 interface GigabitEther…

研究生课程 |《数值分析》复习

搭配往年真题册食用最佳。

推荐算法架构7:特征工程

1 概述 特征工程[1]&#xff08;Feature Engineering&#xff09;是推荐算法的基础&#xff0c;它对收集到的原始数据进行解析和变换&#xff0c;从而提取出模型所需要的信息。通过挖掘丰富和高质量的特征&#xff0c;并对其进行合理的处理&#xff0c;可以提升模型预估准确度…

MFC静态链接+libtiff静态链接提示LNK2005和LNK4098

编译报错 1>msvcrt.lib(ti_inst.obj) : error LNK2005: "private: __thiscall type_info::type_info(class type_info const &)" (??0type_infoAAEABV0Z) 已经在 libcmtd.lib(typinfo.obj) 中定义 1>msvcrt.lib(ti_inst.obj) : error LNK2005: "pr…

pytorch-模型预测概率值为负数

在进行ocr识别模型预测的时候&#xff0c;发现预测的结果是正确的&#xff0c;但是概率值是负数&#xff1a; net_out net(img) #torch.Size([70, 1, 41]) logit, preds net_out.max(2) #41是类别 需要对类别取最大值 preds preds.transpose(1, 0).contiguous().view(-1) …

Arduino平台软硬件原理及使用——Arduino Uno板卡的使用

本文目录 一、Arduino平台简介 二、Arduino Uno板卡简介 三、Arduino编译器简介 四、Arduino平台结合Arduino Uno板卡的使用 一、Arduino平台简介 Arduino 是一个灵活、易于使用的开源电子平台&#xff0c;这个平台包含软件和硬件。 Arduino能通过各种各样的传感器来感知环境&…

代码随想录刷题题Day19

刷题的第十九天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C Day19 任务 ● 669. 修剪二叉搜索树 ● 108.将有序数组转换为二叉搜索树 ● 538.把二叉搜索树转换为累加树 1 修剪二叉搜索树 669…

数据库编程大赛:一条SQL计算扑克牌24点

你是否在寻找一个平台&#xff0c;能让你展示你的SQL技能&#xff0c;与同行们一较高下&#xff1f;你是否渴望在实战中提升你的SQL水平&#xff0c;开阔你的技术视野&#xff1f;如果你对这些都感兴趣&#xff0c;那么本次由NineData主办的《数据库编程大赛》&#xff0c;将是…

Leetcode 406 根据身高重建队列

题意理解&#xff1a; people [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] 给定一个二维数组&#xff0c;&#xff08;h,k&#xff09;h表示此人身高&#xff0c;k表示前面有几个人比他高。 我们按照每个人的h,k两个维度的需求给每个人排在合适的位置。 如&#xff1a; [5,0][7,0]…

docker数据卷数据卷容器

前言 今天调休在家&#xff0c;随便玩玩&#xff0c;简单做下学习记录 1. 数据卷特点 数据卷在容器启动时初始化&#xff0c;如果容器使用的镜像在挂载点包含了数据&#xff0c;这些数据会被拷贝到新初始化的数据卷中数据卷可以在容器之间共享和重用可以对数据卷里的内容直接…