命令模式如何解决硬连线命令/请求的问题



我正在阅读Robert Nystrom的游戏编程模式,并对命令模式有疑问。

在配置输入部分的第一个示例中,if/else语句将游戏操作硬编码为控制台按钮:

void InputHandler::handleInput()
{
if (isPressed(BUTTON_X)) jump();
else if (isPressed(BUTTON_Y)) fireGun();
else if (isPressed(BUTTON_A)) swapWeapon();
else if (isPressed(BUTTON_B)) lurchIneffectively();
}

由于键映射在编译时是硬编码的,因此用户无法在运行时根据自己的偏好更改/配置它们。然后引入命令模式来解决这个问题:

// ***Command interface***
class Command
{
public:
virtual ~Command() {}
virtual void execute() = 0;
};
// ***Concrete commands***
class JumpCommand : public Command
{
public:
virtual void execute() { jump(); }
};
class FireCommand : public Command
{
public:
virtual void execute() { fireGun(); }
};
[...]

// ***Input handler***
class InputHandler
{
public:
void handleInput();
// Methods to bind commands...
private:
Command* buttonX_;
Command* buttonY_;
Command* buttonA_;
Command* buttonB_;
};
void InputHandler::handleInput()
{
if (isPressed(BUTTON_X)) buttonX_->execute();
else if (isPressed(BUTTON_Y)) buttonY_->execute();
else if (isPressed(BUTTON_A)) buttonA_->execute();
else if (isPressed(BUTTON_B)) buttonB_->execute();
}

问题

我不清楚命令模式如何帮助使输入映射在运行时可配置。GUI中必须有一些表允许用户为每个操作类型指定一个键:<commandName,键>,但是我们如何在运行时创建这些密钥绑定呢?

我认为我们需要使用new关键字来初始化指针,例如buttonX_ = new JumpCommand;,但我不确定如何创建绑定,也不明白为什么在运行时不能使用if/else来完成这项工作。

我对JS有一些经验,但对C++没有经验,所以如果有熟悉这两种语言的人能帮助我充实/理解这个例子中发生的事情,我将不胜感激。

实现

我把代码改了一点,这样就更简单了。因此,以下是命令:

using Command = void (*)();
void jump(); // TODO
void fire(); // TODO

和按钮:

enum class Button { x, y, a, b };
bool pressed(Button); // TODO

以下是如何存储[可自定义密钥->Command]映射并将其用于处理输入。它与您介绍的有点不同:Istatic_castButton&lt-&gt;CCD_ 6使用CCD_;键";;CCD_ 8 s是";值":

class Buttons {
std::array<Command, 4> commands{jump, jump, fire, fire};
public:
void rebind(Button button, Command command) {
commands[static_cast<std::size_t>(button)] = command;
}
void handle() {
for (std::size_t i{}; i < commands.size(); ++i)
if (pressed(static_cast<Button>(i)))
commands[i]();
}
};

因此,如果用户想要例如使用x来跳转,则应该调用rebind(Button::x, jump)。最初的解决方案不提供统一的重新绑定,因为每个Command都是一个单独的字段,所以我使用了一个数组。

为什么

但是为什么我们需要命令模式来在运行时进行这些绑定?

我们没有,这只是一种选择,一种常见的方法。

将此表转换为<commandName,键>然后用一个简单的if/else做if(isPressed(keymap["jump"]((jump((?

如果字符串名称改为命名的积分常量,字典改为数组,我更喜欢您的解决方案,因为它避免了间接/虚拟函数调用,后者速度较慢,并且template/重载不兼容。

相关内容

  • 没有找到相关文章

最新更新