比如有这么一个"记忆"类的比赛游戏。你和电脑对战,轮到谁的回合,谁翻两张牌,如果两张牌一样,就消掉这两张牌,得2分,可以继续翻牌,如果两张牌不一样,就换一个人。直到最后,看谁的得分高。
先把图画出来会清晰些:
2.先看下不好的设计方式
我们来设计游戏大致架构,用一个圈表示一个状态。
- typedef enum{
- WaitingPlayer,
- CheckPlayer,
- AIThink,
- AIFirstCard,
- AISecondCard,
- CheckAI
- }MatchGameState;
准备一个_state的变量来记录当前的状态,然后放到update函数里,执行下面的伪代码。
- void MatchLayer::update(float dt){
- if(allCards.size() == 0){
- _state = GameOver;
- }
- switch(_state){
- case WaitingPlayer:
- if(cardCount == 2){
- _state = CheckPlayer;
- cardCount = 0;
- }
- break;
- case CheckPlayer:
- if(playerCard1 == playerCard2){
- 玩家得分
- _state = WaitingPlayer;
- }else{
- _state = AIThink;
- 把玩家点开的卡加入到记忆数组中
- }
- break;
- case AIThink:
- 从记忆的数组中找两张相同的,找不到就随机准备两种卡
- _state = AIFirstCard;
- break;
- case AIFirstCard:
- 点开第一张卡
- 如果之前没找到相同卡,把这卡加入到记忆数组
- _state = AISecondCard;
- break;
- case AISecondCard:
- 从记忆的数组中找两张相同的:
- 如果找到跟第一张一样,就点开它,找不到就点刚开始的随机第2张,并且把第2张加入到记忆数组中。
- _state = CheckAI;
- break;
- case CheckAI:
- if(AICard1 == AICard2){
- _state = AIThink;
- 电脑得分
- }else{
- _state = WaitingPlayer;
- }
- }
- }
这样写是可以,但是随着代码行数增加和业务逻辑变得复杂,后续会比较难维护。
3.使用"设计模式"来重构
我们来看下如何重构,使用"设计模式"来重构它。我盗了一张图来说明。
不知道这方法是设计模式中的哪种。
我们打算把所有的状态都用一个类来实现,它们都继承一个基类叫MatchState,它非常简单。有一个类来管理所有的状态。 MatchState如下:
- #ifndef _MATCHSTATE_
- #define _MATCHSTATE_
- class MatchState{
- public:
- virtual void Update() = 0;
- };
- #endif
我这里就没加OnEnter和OnExit了。简单起见。
为了简单些,就把Layer作为状态管理类,在Layer中增加一个属性,来表示当前状态:
- MatchState* currentState;
在主要的Layer中增加一个方法来切换当前状态:
- void changeState(MatchState* state){
- delete currentState;
- currentState = state;
- }
在update中就简单了,一直执行当前状态的Update方法:
- void MatchLayer::update(float dt){
- if(allCards.size() == 0){
- _state = GameOver;
- }
- currentState->Update();
- }
每个状态的具体业务逻辑都写在自己的类中。比如WaitingPlayerState类:
- #ifndef _WAITINGPLAYERSTATE_H
- #define _WAITINGPLAYERSTATE_H
- #include "MatchState.h"
- class WaitingPlayerState : public MatchState{
- public:
- WaitingPlayerState(){
- }
- void Update(){
- if(sGlobal->cardCount == 2){
- sGlobal->matchLayer->changeState(new CheckPlayerCardsState());
- }
- }
- };
- #endif
这里sGlobal是一个单例。
再比如CheckPlayerCardsState:
- #ifndef _CHECKPLAYERCARDSSTATE_H
- #define _CHECKPLAYERCARDSSTATE_H
- #include "MatchState.h"
- class CheckPlayerCardsState : public MatchState
- {
- public:
- void Update(){
- //非常复杂的具体业务逻辑写在这里
- if(playerCard1 == playerCard2){
- 玩家得分
- sGlobal->matchLayer->changeState(new WaitingPlayerState ());
- }else{
- sGlobal->matchLayer->changeState(new AIThinkState ());
- 把玩家点开的卡加入到记忆数组中
- }
- }
- };
- #endif
其他状态类就不写出来了,总之通过这样把一个状态用一个类来表示,大大的使代码简洁些,扩展性强些。