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

1、模式标准

模式名称:中介者模式

模式分类:行为型

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

结构图:

适用于:

1、一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以

理解。

2、一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。

3、想定制一个分布在多个类中的行为,而又不想生成太多的子类。

2、分析与设计

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

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

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

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

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

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

联机:

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

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

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

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

3、开始打造

中介者接口

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

联机游戏命令中介者

TypeScript 复制代码
// 联机游戏命令中介者
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)
        }
    }
}

指令工作者接口

TypeScript 复制代码
export interface ICommandWorker {
    sendCommand(commandText: string): void
    receiveCommand(commandText: string): void
}

服务指令工作者

TypeScript 复制代码
// 服务指令工作者
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)
    }
}

本地指令之一

TypeScript 复制代码
// 单位 操作命令 另一个单位
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、开始使用

TypeScript 复制代码
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来统一管理

TypeScript 复制代码
/** 指令管理者 */
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)
    }
}

在全局单例管理中添加

TypeScript 复制代码
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()
}

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

TypeScript 复制代码
export class xhgame {
    // 设计模式10(外观模式)
    /** 当前游戏 */
    static get game() {
        return gameInstance.game
    };
     
    ..........

    /** 指令管理 */
    static get command() {
        return gameInstance.commandManager
    }
}

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

TypeScript 复制代码
onClickSkill(){
    const commandText = "((player1Hero)){{skill1}}";
    xhgame.command.unitCommandUnitCommandWorker.sendCommand(commandText)
}

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

TypeScript 复制代码
            
onClickSkill(){
    xhgame.command.unitCommandUnitCommandWorker.sendCommandByObject(this.unitItem, 'skill', this.targetUnitItem)
}

ok.

其他:

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

相关推荐
捕鲸叉3 小时前
C++设计模式之组合模式中适用缓存机制提高遍历与查找速度
c++·设计模式·组合模式
夏旭泽4 小时前
设计模式-工厂模式
设计模式·简单工厂模式
渊渟岳4 小时前
设计模式--原型模式及其编程思想
设计模式
春风十里不如你95275 小时前
【设计模式】【行为型模式(Behavioral Patterns)】之观察者模式(Observer Pattern)
观察者模式·设计模式
春风十里不如你95275 小时前
【设计模式】【创建型模式(Creational Patterns)】之建造者模式(Builder Pattern)
设计模式·建造者模式
春风十里不如你95278 小时前
【设计模式】【结构型模式(Structural Patterns)】之代理模式(Proxy Pattern)
设计模式·代理模式
XR-AI-JK8 小时前
UE5肉鸽游戏教程学习
学习·游戏·ue5·ue4·新手学习·游戏教程·ue教程
请你打开电视看看11 小时前
观察者模式
java·观察者模式·设计模式
Mr.朱鹏11 小时前
设计模式之策略模式-工作实战总结与实现
java·spring·设计模式·kafka·maven·策略模式·springbbot
春风十里不如你952712 小时前
【设计模式】【结构型模式(Structural Patterns)】之组合模式(Composite Pattern)
设计模式·组合模式