命令模式·Design Patterns
1、命令模式的定义
命令模式的定义:
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。让两者之间通过命令对象进行沟通,这样方便对命令对象进行存储、传递、调用、增加与管理。
命令模式主要包含以下主要角色:
1、抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法Execute。
2、具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接受者的功能来完成命令要执行的操作。
3、实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
4、调用者/请求这(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,不直接访问接收者。
其结构如下图所示:
该部分主要参考:命令模式详解,详情请点击该链接。
二、自定义按键功能
在《游戏设计模式》中,有提到使用命令模式来实现自定义游戏按键的功能。
详情请点击该链接,命令模式·Design Patterns
该功能的需求大致如下:
(1)玩家可以自定义按键,如:A键原本绑定的是“跳跃”功能,可改为“开火”功能。
(2)玩家可以撤销自己上一步的命令效果,如重复撤销,则会重做上一步的命令效果。如,玩家执行前进一格操作后,一次撤销会让他后退一格,二次撤销则会让他前进一格。如在撤销后在执行一次操作,则会清理储存好的命令列表。
这里的话,强烈建议阅读完命令模式·Design Patterns之后,再看下一章。
三、具体代码实现
3.1、前置准备部分
按键操作部分:
用于模拟按键被按下。
//按键枚举
enum BUTTON{
BUTTON_X,
BUTTON_Y,
BUTTON_A,
BUTTON_B,
BUTTON_CTRL_Z
};
//监测目标按键是否被按下
bool IsPressed(BUTTON pressedButton,BUTTON targetButton){
if(pressedButton==targetButton)
{
switch (targetButton)
{
case BUTTON_X:
std::cout<<"Pressed the BUTTON_X"<<std::endl;
break;
case BUTTON_Y:
std::cout<<"Pressed the BUTTON_Y"<<std::endl;
break;
case BUTTON_A:
std::cout<<"Pressed the BUTTON_A"<<std::endl;
break;
case BUTTON_B:
std::cout<<"Pressed the BUTTON_B"<<std::endl;
break;
case BUTTON_CTRL_Z:
std::cout<<"Pressed the BUTTON_CTRL_Z"<<std::endl;
break;
default:
break;
}
return true;
}
return false;
}
Actor类:
可以理解为Unreal中的Actor,就是一个作用物对象,后面用来创建多个玩家实例。
类图如下:
代码如下:
//作用物类
class Actor{
public:
Actor(){
}
Actor(std::string name){
this->name=name;
}
Actor(const Actor& other){
//拷贝构造函数(需name不为空时调用)
this->name=other.name;
}
virtual ~Actor(){
//虚析构函数,防止派生类不释放基类成员变量
}
const std::string GetName(){
return name;
}
Actor* Clone(){
//原型模型用于深拷贝
return new Actor(*this);
}
private:
std::string name;
};
3.2、命令类部分
抽象命令类:
用于派生具体命令类。为了让命令类可以作用于更多的actor,因此在Execute部分,选择传入一个Actor*。为了内存管理,重写了拷贝构造函数和赋值函数。因此代码会有些长。
类图如下:
代码如下:
//抽象命令类
class Command{
public:
Command(){
}
Command(Actor* actor){
executeObject=actor;
}
//因为类中有指针,所以需要重写拷贝构造函数和赋值构造函数
Command(const Command &other){
executeObject=other.executeObject->Clone();
}
Command & operator =(const Command &other){
if(this==&other){
return *this;
}
delete executeObject;
executeObject=other.executeObject->Clone();
return *this;
}
virtual ~Command(){
delete executeObject;
}
//执行函数
virtual void Execute(Actor* actor){
executeObject=new Actor(*actor);
};
virtual Actor* GetExecuteObject(){
return executeObject;
}
//撤销
virtual void Undo()=0;
//原型模式,克隆
virtual Command* Clone(){
return nullptr;}
protected:
Actor* executeObject;
};
具体命令类:
抽象命令类的具体实现,现有:跳跃、开枪、切换武器、倾斜等四个命令。为了方便,这里将实现者角色直接使用了一个函数来实现。
类图和抽象命令类基本一致,因此不画出。
代码如下:
//跳跃命令 具体命令
class JumpCommand:public Command{
public:
JumpCommand()
{
}
~JumpCommand(){
}
JumpCommand(const JumpCommand &other):Command(){
executeObject=other.executeObject->Clone();
}
JumpCommand & operator =(const JumpCommand &other){
if(this==&other){
return *this;
}
delete executeObject;
executeObject=other.executeObject->Clone();
return *this;
}
public:
virtual void Execute(Actor* actor){
Command::Execute(actor);
Jump(actor);
}
//实施方
void Jump(Actor* actor){
std::cout<<actor->GetName()<<" -> Jumping"<<std::endl;
}
void Undo(){
std::cout<<executeObject->GetName()<<" Undo Jumping"<<std::endl;
}
Command* Clone() override
{
return new JumpCommand(*this);
}
};
//开枪命令 具体命令
class FireCommand:public Command{
public:
FireCommand(){
}
~FireCommand(){
}
FireCommand(const FireCommand &other):Command(){
executeObject=other.executeObject->Clone();
}
FireCommand & operator =(const FireCommand &other){
if(this==&other){
return *this;
}
delete executeObject;
executeObject=other.executeObject->Clone();
return *this;
}
public:
virtual void Execute(Actor* actor){
Command::Execute(actor);
FireGun(actor);
}
//实施方
void FireGun(Actor* actor){
std::cout<<actor->GetName()<<" -> FireGuning"<<std::endl;
}
void Undo(){
std::cout<<executeObject->GetName()<<" Undo FireGuning"<<std::endl;
}
Command* Clone() override
{
return new FireCommand(*this);
}
};
//切换武器命令 具体命令
class SwapWeaponCommand:public Command{
public:
SwapWeaponCommand(){
}
~SwapWeaponCommand(){
}
SwapWeaponCommand(const SwapWeaponCommand &other):Command(){
executeObject=other.executeObject->Clone();
}
SwapWeaponCommand & operator =(const SwapWeaponCommand &other){
if(this==&other){
return *this;
}
delete executeObject;
executeObject=other.executeObject->Clone();
return *this;
}
public:
virtual void Execute(Actor* actor){
Command::Execute(actor);
SwapWeapon(actor);
}
//实施方
void SwapWeapon(Actor* actor){
std::cout<<actor->GetName()<<" -> SwapWeaponing"<<std::endl;
}
void Undo(){
std::cout<<executeObject->GetName()<<" Undo SwapWeaponing"<<std::endl;
}
Command* Clone() override
{
return new SwapWeaponCommand(*this);
}
};
//倾斜命令 具体命令
class LurchCommand:public Command{
public:
LurchCommand(){
}
~LurchCommand(){
}
LurchCommand(const LurchCommand &other):Command(){
executeObject=other.executeObject->Clone();
}
LurchCommand & operator =(const LurchCommand &other){
if(this==&other){
return *this;
}
delete executeObject;
executeObject=other.executeObject->Clone();
return *this;
}
public:
virtual void Execute(Actor* actor){
Command::Execute(actor);
LurchIneffectively(actor);
}
//实施方
void LurchIneffectively(Actor* actor){
std::cout<<actor->GetName()<<" -> LurchIneffectivelying"<<std::endl;
}
void Undo(){
std::cout<<executeObject->GetName()<<" Undo LurchIneffectivelying"<<std::endl;
}
Command* Clone() override
{
return new LurchCommand(*this);
}
};
3.3、输入控制部分
输入控制类:
该类主要负责根据按键获取对应的命令,执行该函数。然后将该命令保存至命令列表,可以执行撤销和重做动作。
类图如下:
代码如下:
/输入控制类 调用方
class InputHandler{
public:
InputHandler(){
//默认按键绑定的方法
buttonX_=new JumpCommand();
buttonY_=new FireCommand();
buttonA_=new SwapWeaponCommand();
buttonB_=new LurchCommand();
currentCommandIndex=0;
}
virtual ~InputHandler(){
delete buttonX_;
delete buttonY_;
delete buttonA_;
delete buttonB_;
};
//触发可变指令输入,获取对应指令
Command* HandleCommandInput(BUTTON pressedButton){
//pressedButton为正在被按下的按钮
if(IsPressed(pressedButton,BUTTON_X)) return buttonX_;
else if(IsPressed(pressedButton,BUTTON_Y)) return buttonY_;
else if(IsPressed(pressedButton,BUTTON_A)) return buttonA_;
else if(IsPressed(pressedButton,BUTTON_B)) return buttonB_;
//如按下的键不是可处理按键,则返回nullptr
return nullptr;
}
//触发固定输入
void HandleFixedInput(BUTTON pressedButton){
if(IsPressed(pressedButton,BUTTON_CTRL_Z)) RollBack();
}
//命令施加于对象
void CommandInflictionToActor(Command* command,Actor* actor){
if(currentCommandIndex!=(commandsVec.size())){
std::cout<<"have been clear Commandvec"<<std::endl;
for(auto it:commandsVec){
delete it;
}
commandsVec.clear();
currentCommandIndex=0;
}
command->Execute(actor);
commandsVec.push_back(command->Clone());
currentCommandIndex+=1;
}
//撤销与重做操作
void RollBack(){
if(currentCommandIndex!=0){
if(currentCommandIndex==commandsVec.size())//撤销操作
{
std::cout<<"RollBacking"<<std::endl;
commandsVec.back()->Undo();
currentCommandIndex-=1;
}
else//重做操作
{
std::cout<<"reforming"<<std::endl;
commandsVec.back()->Execute(commandsVec.back()->GetExecuteObject());
}
}
}
//设置按键对应的命令
void SetButtonX(Command* command){
if(command){
delete buttonX_;
buttonX_=command->Clone();
}
}
void SetButtonY(Command* command){
if(command){
delete buttonY_;
buttonY_=command->Clone();
}
}
void SetButtonA(Command* command){
if(command){
delete buttonA_;
buttonA_=command->Clone();
}
}
void SetButtonB(Command* command){
if(command){
delete buttonB_;
buttonB_=command->Clone();
}
}
//用于绑定对应指令的指针
private:
Command* buttonX_;
Command* buttonY_;
Command* buttonA_;
Command* buttonB_;
std::vector<Command*> commandsVec;
long long unsigned int currentCommandIndex;
};
3.4、主函数部分
主函数:
先是测试了四个按键功能的执行,后是测试是否可以撤销和重做,最后测试是否能够清除命令列表。
代码如下:
int main(void)
{
InputHandler* inputHandler=new InputHandler();
Actor player_1("player_1");
Actor player_2("player_2");
std::cout<<"测试四个按键:"<<std::endl<<"-----------------"<<std::endl;
//依次按下四个按键
for(int i=0;i<4;i++){
//std::cout<<inputHandler->HandleInput((BUTTON)i)<<std::endl;
Command* myCommand=inputHandler->HandleCommandInput((BUTTON)i);//int可以转为对应枚举值
if(myCommand)
{
if(i<2){
inputHandler->CommandInflictionToActor(myCommand,&player_1);//使用指针,是为了可以扩展Actor
}
else{
inputHandler->CommandInflictionToActor(myCommand,&player_2);
}
}
}
std::cout<<std::endl<<"测试撤销与重做操作:"<<std::endl<<"----------------"<<std::endl;
inputHandler->HandleFixedInput(BUTTON_CTRL_Z);
inputHandler->HandleFixedInput(BUTTON_CTRL_Z);
std::cout<<std::endl<<"测试清理命令列表:"<<std::endl<<"--------------------------"<<std::endl;
Command* tempCommand=inputHandler->HandleCommandInput((BUTTON)0);//int可以转为对应枚举值
inputHandler->CommandInflictionToActor(tempCommand,&player_1);//使用指针,是为了可以扩展Actor
return 0;
}
四、输出结果
输出截图如下:
今天的文章c++ 按键_如何用c++做游戏分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/86686.html