在游戏中,游戏人物根据玩家的输入以及人物与游戏世界交互,会有许多动作动画效果的转换。简单的比如马里奥,玩家没有输入,没有发生游戏事件(吃到蘑菇、碰到怪物)时,马里奥大叔静止不动。玩家按下移动按钮,马里奥开始走动,这时需要绘制走路的动画效果。按下跳跃键,绘制跳跃的动画效果。而按键和动作并不是一一对应的关系,比如跳跃过程中按下左右移动键,并不能绘制走路的动画效果。
这些状态之间的转换可以使用if else,switch case,语句来完成,如下:
//这里把人物写成一个Role类
class Role
{
public:
enum STATE
{
IDLE,
WALK,
JUMP,
}
void JumpInput()
{
_state = JUMP;
//..设置当前动画为跳跃的动画
}
void WalkInput()
{
if (_state == IDLE)
{
_state = WALK;
//..设置当前动画为行走的动画
}
}
// 跳跃后角色落地,由碰撞检测触发
void OnLand()
{
_state = IDLE;
}
private:
STATE _state;
}
貌似挺完美,嗯,这样完美的情况仅仅只限于状态比较少,状态转换比较简单的条件下,如果人物状态一增多,状态转换条件一变复杂,那将变成一场噩梦,代码中会产生大量的if else条件判断,比如我们再增加一个蹲下的状态,蹲下时按下跳跃键让游戏人物快速向前贴地滑动一段距离,Rockman的操作好像就是这样。再加一个约束条件,蹲下时按下左右移动键无效果。哇哦,这样再看看上面那段简单的代码要变成什么样子了。
如何以一种条理清晰,便于维护的方式来实现以上需求呢? 以上需求的核心是状态的转换,自然而然,带有“状态”这个关键字的state模式首先成了思考方向。state模式将每个状态抽象成类,状态的转换由状态自己来操作,是一种比较灵活的实现方式。基于state模式,将代码重构如下(伪代码):
// 伪代码
class IStatus
{
Role* role;
// 输入向上键
virtual void Up();
// 输入向下键
virtual void Down();
// 输入向左键
virtual void Left();
// 输入向右键
virtual void Right();
// 输入跳跃键
virtual void Jump();
// 输入攻击键
virtual void Attack();
// 动作完成后
virtula void Done();
};
class IdleState;
{
Left() {role->setState(WalkState);}
Right() {role->setState(WalkState);}
Down() {role->setState(CrouchState);}
Jump() {role->setState(JumpState);}
}
class WalkState
{
Jump() {role->setState(JumpState);}
}
class JumpState;
{
}
class CrouchState
{
Jump() {role->setState(SlipState);}
}
class SlipState
{
}
Role类:
class Role
{
IState _state;
// 输入向上键
void Up() { _state.Up();}
// 输入向下键
void Down() { _state.Down();}
// 输入向左键
void Left() { _state.Left();}
// 输入向右键
void Right() { _state.Right();}
// 输入跳跃键
void Jump() { _state.Jump();}
// 输入攻击键
void Attack() { _state.Attack();}
}
这样用state模式来写,后期维护和加入新的状态也变得十分容易,加入新的状态只需要加入一个新的类,而不需变更以有的代码。