《游戏编程模式》Ch1-Ch2

2019-08-12  本文已影响0人  昼阴夜阳

1 架构、性能和游戏

1.1 什么是软件架构

软件架构:代码的组织方式。

好的架构:适度抽象,在轻松应对快速变化的同时不过于复杂冗余。

核心思想:解耦,最小化解决单个问题需要的代码学习和知识储备。

1.2 有什么代价

添加更多代码并对抽象层进行开发、调试和维护。

添加补丁的同时需要谨慎的组织代码维护架构。

增加了解决问题时理解代码的难度。

1.3 性能和速度

灵活的模式通常依赖于运行成本较大的机制。例如虚函数派发、指针、消息、接口。

折中方法:先保持灵活性,直到设计稳定下来再去除抽象提高性能。

1.4 设计的灵活性

为了验证想法的代码大部分需要抛弃。因此原型是一个完全正确的编程实践,只要保证以后重写。

1.5 寻求平衡

开发本身也需要权衡利弊。

1.6 简单性

一个思路是努力试着编写最干净、直接的函数来解决问题。

用一小块逻辑解决一大片用例永远是最优雅的。其本质是寻找到了用例背后的模式关系。

If I had more time, I would have written you a short letter. —— Blaise Pascal

1.7 TIPS


2.命令模式

2.0 概述

将一个request封装成一个对象,从而允许使用不同的请求、队列或日志将客户端参数化,同时支持请求操作的撤销与恢复。 —— GoF

A command is a reified method call. 命令就是一个具象化(实例化)的方法调用。 —— Robert Nystrom

2.1 配置输入

按钮与游戏行为(方法)的映射,为了支持自定义配置,就需要将直接调用转换为可以swap out的变量分配。

首先对将不同游戏行为的调用(回调)具象为命令。虽然函数指针就可以做到这一点,但并不完整,所以这里定义命令基类class Command。

class Command{
public:
    virtual ~Command() {};
    virtual void execute() = 0;
}

为不同游戏行为(方法)分别创建不同命令子类,并对执行操作虚函数进行重载绑定。

class JumpCommand : public Command {
public:
    virtual void execute() { jump(); }
};
// ...

最后就可以通过间接调用层的命令指针处理输入。

class InputHandler{
public:
    void handleInput();
    // Methods to bind commands to buttons
private:
    Command* buttonX_;
    // ...
}
void InputHandler::handleInput(){
    if (isPressed(BUTTON_X)) buttonX_->execute();
    // else if...
}

// 这种方式是将执行功能集成在了命令内部,应该是比较低级的命令,之前写过的完全抽象成数据的命令,需要装载数据到执行模块才能用的比较高级。不过提供了添加一个装载到执行器并执行的基类方法的想法。

// 早知道不敲这么多憨憨代码了,诚招打字员

2.2 关于角色的说明

命令模式更普遍的应用场景是作为游戏AI引擎和角色之间的接口。通过命令模式,AI模块和角色行为实现了解耦。AI模块只需要简单的提供命令流以供角色(引擎)执行。同样,对于多人网络游戏,命令序列化、传送、再回放的过程是关键部分。

2.3 撤销和重做

如果一个命令对象可以do,也应该可以轻松的undo,尤其是在回合策略类游戏中。为此,输入处理程序需要对每次输入的动作创建一次性命令实例。

class Command{
public:
    virtual ~Command() {};
    virtual void execute() = 0;
    virtual void undo() = 0;
};

为了撤销状态,可以在 execute() 执行的时候先记录当前状态。通过在命令里增加记录状态的变量实现。

为了支持多次撤销,我们需要维护一个命令列表和一个对当前命令的引用。在执行、撤销、重做或选择新命令后根据情况对命令列表和当前命令引用进行更新。

2.4 类风格化还是函数风格化

函数式编程在解决许多问题时拥有高效性,例如用 js 实现命令模式。

function makeMoveUnitCommand(unit, nextStatus) {
    var beforeStatus;
    // return command object
    return {
        execute: function() {
            beforeStatus = unit.status();
            unit.moveTo(nextStatus);
        },
        undo: function() {
            unit.moveTo(beforeStatus);
        }
    }
}

上一篇下一篇

猜你喜欢

热点阅读