state模式实现游戏动画系统
在游戏中,游戏人物根据玩家的输入以及人物与游戏世界交互,会有许多动作动画效果的转换。简单的比如马里奥,玩家没有输入,没有发生游戏事件(吃到蘑菇、碰到怪物)时,马里奥大叔静止不动。玩家按下移动按钮,马里奥开始走动,这时需要绘制走路的动画效果。按下跳跃键,绘制跳跃的动画效果。而按键和动作并不是一一对应的关系,比如跳跃过程中按下左右移动键,并不能绘制走路的动画效果。
这些状态之间的转换可以使用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模式来写,后期维护和加入新的状态也变得十分容易,加入新的状态只需要加入一个新的类,而不需变更以有的代码。