命令模式
- 命令(command) 将请求封装为对象,从而使用不同的请求将客户端参数化,同时支持请求撤销与恢复。
#include <iostream>
using namespace std;
class Receiver;
class Inveoker;
class BaseCommand;
/*接收者 执行者 */
class Receiver
{
public:
void CommandA()
{
cout << "CommandA" << endl;
}
void CommandB()
{
cout << "CommandB" << endl;
}
};
/*命令 */
class BaseCommand
{
protected:
Receiver *receiver;
public:
BaseCommand(Receiver *rec)
{
receiver = rec;
}
virtual void Action() = 0;
};
/*发放 */
class Invoker
{
private:
BaseCommand *command;
public:
void SetCommand(BaseCommand *command1)
{
command = command1;
}
void Invoke()
{
command->Action();
}
};
class CommandA : public BaseCommand
{
public:
CommandA(Receiver *re) : BaseCommand(re) {};
void Action() override
{
receiver->CommandA();
}
};
class CommandB : public BaseCommand
{
public:
CommandB(Receiver *re) : BaseCommand(re) {};
void Action() override
{
receiver->CommandB();
}
};
int main()
{
Receiver *re = new Receiver();
CommandA *a = new CommandA(re);
CommandB *b = new CommandB(re);
Invoker *invo = new Invoker();
invo->SetCommand(a);
invo->Invoke();
invo->SetCommand(b);
invo->Invoke();
}
按键映射
- 读取用户一系列输入,转换为游戏中的Action
class BaseRole
{
public:
void Jump()
{
cout << "Jump" << endl;
}
void MoveL()
{
cout << "MoveL" << endl;
}
void MoveR()
{
cout << "MoveR" << endl;
}
void Down()
{
cout << "Down" << endl;
}
};
class Command
{
protected:
BaseRole *role;
public:
Command(BaseRole *role1)
{
role = role1;
}
virtual void Action() = 0;
};
class JumpCommand : public Command
{
void Action() override
{
role->Jump();
}
};
class DownCommand : public Command
{
void Action() override
{
role->Down();
}
};
class MoveLCommand : public Command
{
void Action() override
{
role->MoveL();
}
};
class MoveRCommand : public Command
{
void Action() override
{
role->MoveR();
}
};
class InputHandle
{
private:
Command *w;
Command *s;
Command *a;
Command *d;
public:
void Input()
{
char i;
cin >> i;
if (i == 'w')
w->Action();
else if (i == 's')
s->Action();
else if (i == 'a')
a->Action();
else if (i == 'd')
d->Action();
}
};
此时我们成功增加了一个间接调用函数层,修改按键映射时 修改指针指向即可
但是 此时调用 Input前 需要对命令进行绑定 稍显麻烦了些 修改如下
class Command
{
public:
virtual void Action( Baserole *role) = 0;
};
/*对InputHandle 如下*/
class InputHandle
{
private:
Command *w;
Command *s;
Command *a;
Command *d;
public:
Command* Input()
{
char i;
cin >> i;
if (i == 'w')
return w;
else if (i == 's')
return s;
else if (i == 'a')
return a;
else if (i == 'd')
return d;
return nullptr;
}
};
/*使用如下*/
Command* command=InputHandle.Input();
if(Command)
{
command->Action(role);
}
如果role是玩家操控的对象 基于玩家的输入执行相应role的命令
但是我们可以将 InputHandle替换为 NPCHandle 照搬上面的命令模式 通过相应逻辑返回对应每次的命令
通过命令模式我们摆脱了直接调用函数这种解耦合 我们的代码将更加灵活
我们可以通过替换role 对不同角色进行操作 也可以替换输入对同一角色定义不同状态的操作
撤销与重做
撤销与重做常见于策略游戏,例如在方格地图中对对象作为移动行为,需要支持撤销操作,与重新执行命令重做。
比如在方格地图中进行上下左右四个方位的移动
撤销与重做需要命令流对其支持,或者说我们需要维护一个命令列表与当前命令的一个指针或者引用。
例如使用顺序表作为命令流 执行撤销操作时我们将指针向后移动,回到上一个命令,当执行重做操作时,我们将指针向前移动,重新执行。当执行了一个新命令时,将当前命令前面的命令全部删除。
对于支持撤销的命令来讲我们需要保留执行前的状态,以及撤销函数回到执行前状态
class UnitCommand
{
/*执行 */
virtual void Action() = 0;
/*撤销*/
virtual void UnDo() = 0;
};
class MoveCommand : public UnitCommand
{
private:
BaseRole *object;
int MapX;
int MapY;
int BeforeX;
int BeforeY;
public:
MoveCommand(BaseRole *role, int x, int y) : object(role), MapX(x), MapY(y), BeforeX(role->X), BeforeY(role->Y)
{
}
void Action() override
{
object->MoveTo(MapX, MapY);
}
void UnDo() override
{
object->MoveTo(BeforeY,BeforeY);
}
};
对于备忘录模式来讲并不有效,命令试图去修改对象状态的一小部分,而创建对象其他数据浪费内存,手动存储被修改的部分就节省的很多
我们也可以使用持久化数据,我们每次修改对象返回一个新的对象,保留原对象不变,新对象与原对象共享数据,比拷贝整个对象的代价小的多