游戏编程模式——命令模式

命令模式

本文的参数书是《游戏编程模式》,作者:Bob Nystrom。当然学习资料是在网上下载的PDF版本,等学完了这本书,一定要买来这本书的纸质版留一个纪念。

GoF是这样定义命令模式的:
将一个请求封装为一个对象,从而使你可用不同的请求的客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

每个游戏都有一些用户输入要处理,比如键盘敲击,鼠标点击等等。例如:
在这里插入图片描述

下面是一种简单实现:

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();
}

这样的做法,通常要在游戏的每一帧循环调用函数。但是却不能支持更多的功能,比如当游戏允许玩家配置按键功能的时候就不那么实用了。(扯一句题外话,想当年老罗吹嘘锤子T1的时候,讲到有对称按键并且支持按键功能自定义,感觉好屌的样子,然后就屁颠屁颠的买了T1。现在想想这也许仅仅是软件上的一个小小的改变。)

为了实现这些功能,我们通过一个基类代表游戏触发的行为:

class Command
{
public:
	virtual ~Command(){}
	virtual void execute() = 0;
};

当你有一个接口只包含一个没有返回值的方法时,很可能你可以使用命令模式。

然后我们为不同的游戏行为定义相应的子类:

class JumpCommand : public Command
{
public:
	virtual void execute() { jump(); }
};
class FirCommand: public Command
{
public:
	virtual void execute(){ firGun(); }
};
//大概就是这样的思路了

在代码的输入处理部分,为每一个按键存储一个指向命令的指针

class InputHandler
{
public: void handleInput();
//绑定命令的方法
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();
};

以前每个函数直接调用函数,现在会有一层间接寻址:
在这里插入图片描述


重点在下面

又有一个新的需求,假设我们要控制游戏玩家角色之外的角色怎么办,这个时候就需要关联角色了:

class JumpCommand: public Command
{
public:
	virtual void execute(GameActor &actor)
	{
		actor.jump();
	}
};

现在可以让游戏中的任何角色跳来跳去了。在输入控制部分和在对象上调用命令之间,还缺少了输入的控制部分,handleInput如下:

Command* InputHandler::handleInput()
{
	if(isPressed(BUTTON_X)) return buttonX_;
	if(isPressed(BUTTON_Y)) return buttonY_;
	if(isPressed(BUTTON_A)) return buttonA_;
	if(isPressed(BUTTON_B)) return buttonB_;
	//没有按下任何按键,就什么都不做
	return NULL;
};

这里还不能立即执行,因为还不知道哪个角色会传进来。这里我们享受了命令是具体调用的好处——延迟到调用执行时再知道。
然会,需要一些接受命令的代码,作用在玩家角色身上。像这样:

Command* command = inputHandler.handleInput();
if(command)
{
	command->execute(actor);
}

将actor视为玩家角色的引用,但是这里我们也可以控制其他的任何角色,只需要向命令传入不同的角色。在实际应用中,大部分游戏角色被AI控制,这时候我们可以在AI和角色之间使用相同的命令模式,这样AI代码只需要生成Command对象。

AI命令流和玩家控制同时作用时足以是游戏充满多样化,这些在游戏展示和自动演示时会很有用。如果将指令序列化,再通过网络传输它们,我们可以接受玩家的输入,将其通过网络发送到另一台机器上,然后重现,这是网络多人游戏的基础。
贴一张图,感觉很高大上
在这里插入图片描述
另外在命令模式使用时还可以为命令提供一个撤销方法,在游戏进行中和游戏编辑过程中是很有用的,这里贴张图算了,具体实现也不难,无非就是保存历史操作记录。
在这里插入图片描述


控制其它游戏对象的游戏代码

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

这里GameActor是代表游戏世界中的角色的“游戏对象”类

class JunpCommand: public Command
{
public:
	virtual void execute(GameActor& actor)
	{
		actor.jump();
	}
};

现在可以跳来跳去了

Command* command = inputHandler.handleInput();
if(command)
{
	command->execute(actor)
}

2018年10月19日

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值