用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 = null
    private 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): void
    receiveCommand(commandText: string): void
}

服务指令工作者 

// 服务指令工作者
export class ServerCommandWorker implements ICommandWorker {
    mediator: IMediator
    constructor(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 = null
    fromUnitItem: UnitItem<any> = null
    toUnitItem: UnitItem<any> = null

    mediator: IMediator

    constructor(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中的内容为单位项ID
            const commandId = match[2]; // 捕获组2中的内容为命令ID
            if (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: UnitCommandUnitCommandWorker
    constructor() {
        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: JCQGame
    game: 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上。但前面的有些模式没有完全融合到框架内,可能还需要几天时间才能放出源码。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Go 随机密码

一.Go实现随机密码 随机密码 package mainimport ("fmt""math/rand""os""strconv""time" )func RandomPassword(num int) {length : numif len(os.Args) > 1 {arg : os.Args[1]i, err : strconv.ParseInt(arg, 10, 6…

aws-waf-cdn 基于规则组的永黑解决方案

1. 新建waf 规则组 2. 为规则组添加规则 根据需求创建不同的规则 3. waf中附加规则组 &#xff08;此时规则组所有规则都会附加到waf中&#xff0c;但是不会永黑&#xff09; 此刻&#xff0c;可以选择测试下规则是否生效&#xff0c;测试前确认保护资源绑定无误 4. 创建堆…

[前端优化]项目优化--Lighthouse

[前端优化]项目优化--Lighthouse 前端优化的分类Lighthouse 优化工具优化维度--性能(Performance)性能指标概览白屏时间--FP首字节时间--TTFB首次输入延迟--FID累积布局偏移--CLS 性能指标分析Lighthouse的性能优化方案性能优化实战解析Serve images in next-gen formatsEnable…

阿里云吴结生:云计算是企业实现数智化的阶梯

云布道师 近年来&#xff0c;越来越多人意识到&#xff0c;我们正处在一个数据爆炸式增长的时代。IDC 预测 2027 年全球产生的数据量将达到 291 ZB&#xff0c;与 2022 年相比&#xff0c;增长了近 2 倍。其中 75% 的数据来自企业&#xff0c;每一个现代化的企业都是一家数据公…

LVM-系统

# Linux常见的文件系统&#xff1a;ext4&#xff0c;xfs&#xff0c;vfat(linux和window都能够识别) mkfs.ext4 /dev/sdb1 # 格式化为ext4文件系统 mkfs.xfs /dev/sdb2 # 格式化为xfs文件系统 mkfs.vfat /dev/sdb1 # 格式化为vfat文件系统 mksw…

第3节 二分、复杂度、动态数组、哈希表

二分法 入门题目 有序数组中找到num package class03;import java.util.Arrays; // 有序数组中找到num public class Code_BSExist {// arr保证有序public static boolean find(int[] arr, int num) { // 二分法&#xff0c;有缺陷if (arr null || arr.length 0) { // 边界…

Open3D (C++) 距离计算

目录 一、算法原理1、欧氏距离二、代码实现三、结果展示一、算法原理 1、欧氏距离 在数学中,欧几里得距离或欧几里得度量是欧几里得空间中两点间“普通”(即直线)距离。欧几里得距离有时候有称欧氏距离,在数据分析及挖掘中经常会被使用到,例如聚类或计算相似度。 如果我…

blender径向渐变材质-着色编辑器

要点&#xff1a; 1、用纹理坐标中的物体输出连接映射中的矢量输入 2、物体选择一个空坐标&#xff0c;将空坐标延z轴上移一段距离 3、空坐标的大小要缩放到和要添加材质的物体大小保持一致

【Spring Security】认证密码加密Token令牌CSRF的使用详解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Spring Security》。&#x1f3af;&#x1f3af; …

Ubuntu 常用命令之 sed 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 sed是一个在Linux和其他Unix-like系统中常用的流编辑器&#xff0c;用于对输入流&#xff08;文件或管道&#xff09;进行基本的文本转换。它可以非常方便地进行文本替换、插入、删除等操作。 sed命令的基本格式为 sed [options…

图像处理—小波变换

小波变换 一维小波变换 因为存在 L 2 ( R ) V j 0 ⊕ W j 0 ⊕ W j 0 1 ⊕ ⋯ L^{2}(\boldsymbol{R})V_{j_{0}}\oplus W_{j_{0}}\oplus W_{j_{0}1}\oplus\cdots L2(R)Vj0​​⊕Wj0​​⊕Wj0​1​⊕⋯&#xff0c;所以存在 f ( x ) f(x) f(x)可以在子空间 V j 0 V_{j_0} Vj0…

通讯录应用程序开发指南

目录 一、前言 二、构建通讯录应用程序 2.1通讯录框架 (1)打印菜单 (2) 联系人信息的声明 (3)创建通讯录 (4)初始化通讯录 2.2功能实现 (1)增加联系人 (2)显示联系人 (3)删除联系人 (4)查找联系人 (5)修改联系人 (6)排序联系人 三、通讯录的优化 3.1 文件存储 …

机器学习——分类评价指标

【说明】文章内容来自《机器学习——基于sklearn》&#xff0c;用于学习记录。若有争议联系删除。 1、评价指标 对于模型的评价往往会使用损失函数和评价指标&#xff0c;两者的本质是一致的。一般情况下&#xff0c;损失函数应用于训练过程&#xff0c;而评价指标应用于测试过…

代码随想录-刷题第三十四天

1005. K 次取反后最大化的数组和 题目链接&#xff1a;1005. K 次取反后最大化的数组和 思路&#xff1a;取反k次&#xff0c;保证每次取反的数值是数组中的最小值&#xff0c;最后数组和就是最大的。 class Solution {public int largestSumAfterKNegations(int[] nums, in…

pdf 在线编辑

https://smallpdf.com/edit-pdf#rapp 参考 https://zh.wikihow.com/%E5%B0%86%E5%9B%BE%E5%83%8F%E6%8F%92%E5%85%A5PDF

直排轮滑教程4

蹬地 1&#xff0c;前面练习了蹬地的结构&#xff0c;知道蹬地方向&#xff0c;如何用力。下面来练习具体的蹬地的方法&#xff0c;轮滑蹬地有自己特点。 2&#xff0c;技术方法和特点&#xff1a;蹬地速度快&#xff0c;蹬地有弹性。似跳非跳蹬。 3&#xff0c;四轮着地。轮…

GitHub打不开或者访问慢解决方法

一、获取IP地址 首先进入下面的网站 IP/DNS Detect 获取到当前github.com对应的IP地址 可以多search几次, github.com对应的IP地址不止一个,都记录下来 二、修改hosts文件内容 找到文件夹路径&#xff1a;C:\Windows\System32\drivers\etc\ 打开hosts文件&#xff0c;将刚才…

simulink代码生成(一)——环境搭建

一、安装C2000的嵌入式环境&#xff1b; 点击matlab附加功能&#xff0c; 然后搜索C2000&#xff0c;安装嵌入式硬件支持包&#xff1b;点击安装即可&#xff1b;&#xff08;目前还不知道破解版的怎么操作&#xff0c;目前我用的是正版的这样&#xff0c;完全破解的可能操作…

达梦到达梦的外部链接dblink(DM-DM DBLINK)

一. 使用场景&#xff1a; 部链接对象&#xff08;LINK&#xff09;是 DM 中的一种特殊的数据库实体对象&#xff0c;它记录了远程数据库的连接和路径信息&#xff0c;用于建立与远程数据的联系。通过多台数据库主库间的相互通讯&#xff0c;用户可以透明地操作远程数据库的数…

EDA实验-----直流电机驱动设计(Quartus II )

目录 一、实验目的 二、实验仪器设备 三、实验的重点和难点 四、实验原理 五、实验步骤 六、实验报告 七、实验过程 1.分频器代码 2.方向选择器 3.直流电动机工作原理 4.电路连接图 5.文件烧录 一、实验目的 了解直流电机控制的工作原理和实现的方法。掌握PWM波控…