游戏中,玩家的输入方式有很多种,例如键盘、鼠标等。玩家输入操作,游戏需要调用相应的逻辑,最简单的处理方式是:
if (Input.GetKeyDown("Q"))
Jump();
else if (Input.GetKeyDown("T"))
Idle();
else if (Input.GetKeyDown("A"))
Attack();
else if (Input.GetKeyDown("Z"))
Skill();
对应每一个按钮的输入,直接调用相应的操作函数。
但这样的处理有一个问题:不支持玩家改键,假如玩家想要用按键Q来调用Skill,而不是Jump,那么上面的代码就有些麻烦。
命令模式可以解决这个问题。
命令模式的定义:
将一个请求封装成一个对象,从而允许你使用不同的请求、队列或日志将客户端参数化,同时支持请求操作的撤销与恢复。
将操作函数进行封装,我们可以改造上面的代码:
定义命令基类
class Command
{
public:
virtual void Run() = 0;
}
定义攻击命令
class AttackCommand:public Command
{
public:
virtual void Run(){Attack();}
}
定义跳跃命令
class JumpCommand:public Command
{
public:
virtual void Run(){Jump();}
}
……(依次定义剩余的命令)
这样,我们使用对象,将直接的行为函数调用,封装在其内部。然后我们定义一个管理类,来管理整个游戏中的所有按键
定义操作管理类
class InputHandler
{
public:
//默认按键
void Init()
{
input_map["Q"] = new JumpCommand();
input_map["T"] = new IdleCommand();
input_map["A"] = new AttackCommand();
input_map["Z"] = new SkillCommand();
}
//用户自定义按键
void ChangeButton(std::string button_name,int type)
{
//根据type,将按键映射到对应的命令
//注意内存释放
}
//处理玩家输入
void HandleInput(std::string input)
{
input_map[input]->Run();
}
private:
std::unorder_map<std::string,Command*> input_map;
}
命令模式,不单只是解决了按键的映射问题,还有别的好处:
可以将行为的对象解耦,调用对应的函数,这样,同样一份攻击代码,既可以用于玩家角色,也可以用于NPC,敌人等。
player->attack();
enemy->attack();
npc->attack();
实例是可以存储的,因此,我们可以按照时间戳的方式,将命令依次存放起来,用于撤销和回放。(只需要记录命令的参数,实现对应的run函数和cancelrun函数)
定义命令基类
class Command
{
public:
virtual void Run(Parameter && p) = 0;
virtual void CancelRun() = 0;
private:
Parameter parameter;
CancelParameter parameter;
}