设计模式
命令模式
将一个请求封装为一个对象,从而允许你使用不同的请求、队列或者日志将客户端参数化,同时支持请求操作的撤销与恢复。
命令就是面向对象的回调。
对应代码c++:
// 基类
class Command
{
public:
virtual ~Command(){}
virtual void execute(GameActor& actor)=0;
};
class JumpCommand: public Command
{
public:
virtual void execute(GameActor& actor){
actor.jump();
}
}
class FireCommand :public Command
{
virtual void execute(GameActor& actor){
actor.fireGun();
}
}
// ...
// 空值命令对象模式
class NullCommand: public Command
{
virtual void execute(GameActor& actor){}
}
// 输入处理
class InputHandler
{
public:
Command* handleInput();
// 绑定其他命令
private:
Command* buttonX_;
Command* buttonY_;
Command* buttonA_;
Command* buttonB_;
}
void InputHnadler: handleInput()
{
if (isPressed(BUTTON_X)) return buttonX_;
else if (isPressed(BUTTON_Y)) return buttonY_;
else if (isPressed(BUTTON_A)) return buttonA_;
else if (isPressed(BUTTON_B)) return buttonB_;
}
// 角色执行命令
Command* command = inputHandler.handleInput();
if(command)
{
command->exectue(actor);
}
在命令与角色之间加入间接层,可以使得玩家以及AI操控任意的角色。如果我们把命令序列化,便可以通过网络发送数据流,这个是多人游戏中主要的一部分。
撤销与重做
c++:
// 基类
class Command
{
public:
virtual ~Command(){}
virtual void execute(GameActor& actor)=0;
virtual void undo()=0;
};
class MoveUnitCommand: public Command
{
public:
MoveUnitCommand(Unit* unit,int x,int y): unit_(unit),x_(x),y_(y)
{}
virtual void execute()
{
unit_->moveTo(x_,y_);
}
private:
Unit* unit_;
int x_;
int y_;
}
与备忘录相比,节约内存。
重做在游戏中不常见,但回放很常见。
JS实现(使用闭包)
function makeMoveUnitCommand(unit,x,y){
var xBefore,yBefore;
return function(){
execute:function(){
xBefore = unit.x();
yBefore = unit.y();
unit.moveTo(x,y);
},
undo: function(){
unit.moveTo(xBefore,yBefore)
}
}
}
- 如果有很多不同的命令类,可以定义一个具体的基类来实现更高层次的方法。命令主要的execute()变成了子类沙盒。
- 如果在对象分层等那些不明确执行命令的角色情况下,可以将命令下放給其从属对象。可以参考责任链。
- 同样的命令实例重复,可以使用享元模式。
享元模式
class TreeModel
{
private:
Mesh mesh_;
Texture bark_;
Texture leaves_;
}
class Tree
{
private:
TreeModel* model_;
Vector position_;
double height_;
double thicknes_;
Color barkTint_;
Color leafTint_;
}
与类型对象模式对比:
类型对象通过把“类型”对象化,可以尽可能较少定义新类型的数量,而享元模式却更加注重效率。
应用:
TiledMap
* 一般来说,享元对象总是不可变的。
- 按需创建享元。在接口体上封装,来隐藏构造函数。可以参考工厂方法模式。
- 查找实例化的享元,对象池模式。
- 状态模式下,使用享元。以及上一章节提到的命令模式。