# 游戏中的命令模式

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

回放在游戏中很常见，一个很简单的实现方法就是记录每一帧的游戏状态以便能够回放，但是这样会使用大量的内存。

实际上，许多游戏会记录每一帧每个实体所执行的一系列命令，为了回放游戏，引擎只需要模拟正常游戏的运行，执行预先录制的命令即可。

如果我们把命令序列化，我们便可以通过网络发送数据流，可以把玩家的输入，通过网络发送到另外一台机器上，然后进行回放，这是多人网络游戏很重要的一部分。

```cpp
class Command
{
public:
    virtual ~Command();
    virtual void execute(GameActor& actor) = 0;
};
```

然后为每个不同的游戏动作创建子类。

```cpp
class JumpCommand : public Command
{
public:
    virtual void execute(GameActor& actor){
        actor.jump();
    }
};
class FireCommand : public Command
{
public:
    virtual void execure(GameActor& actor){
        actor.fireGun();
    }
};
```

```bash
人工智能----->命令流----->角色
```

undo撤销的支持。为Command抽象undo方法。

```cpp
class MoveUnitCommand : public Command
{
public:
    MoveUnitCommand(Unit* unit, int x, int y): unit_(unit), x_(x), y_(y)
    {
    }
    virtual void execute()
    {
        xBefore_ = unit_->x();
        yBefore_ = unit_->y();
        unit_->moveTo(x_, y_);
    }
    virtual void undo()
    {
        unit_->moveTo(xBefore_, yBefore_);
    }
private:
    Unit* unit_;
    int x_, y_;
    int xBefore_, yBefore_;
};
```

可以拉成双向链表

```cpp
older CMD<-->CMD<-->CMD<-->CMD<-->CMD<-->CMD newer
                    undo  current redo 
```

不同的语言可能有不同的风格，选择OOP还是函数式编程也是不同的，例如在JavaScript中可以这么做。

```js
function makeMoveUnitCommand(unit, x, y)
{
    // ...
    return function(){
        unit.moveTo(x, y);
    }
}
```

通过闭包来添加对撤销的支持

```js
function makeMoveUnitCommand(unit, x, y){
    var xBefore, yBefore;
    return{
        execute: function(){
            xBefore = unit.x();
            yBefore = unit.y();
            unit.moveTo(x, y);
        },
        undo: function(){
            unit.moveTo(xBefore, yBefore);
        }
    };
}
```
