1 前言
前些天看了一个Cocos2D写的俄罗斯方块代码(下载网址为:http://www.okbase.net/file/item/27944),代码逻辑很清晰。
2 讨论
俄罗斯方块游戏逻辑很简单(其实细节也很多),主要包含五个部分:下移、左移、右移、形状变换(上移)以及消行。于是,该代码的结构为:
这么写存在什么问题?举个例子,如果此时,玩家需要新增或自定义一些方块的样式,并将其加入至程序中,如何修改?对于上面的程序,无非是打开每一个函数,添加Switch分支,并为每一个分支实现独立逻辑。
经常写代码的朋友一定知道,需求的更改最为恼人,尤其是当这些需求的更改将导致客户端代码的大量修订。解决这一问题的常用方法是将客户段逻辑与计算逻辑分离。那么对于这一游戏来说,能否采用这一设计来优化并重构现有代码?
想要完成这一重构,我们必须想清楚,哪些属于客户端逻辑,哪些属于计算逻辑。很明显,当前得分,当前方块的行列位置,都属于客户端逻辑,而方块的具体逻辑动作:上(变换)、下、左、右、消除,都属于计算逻辑,如果我们将这些计算逻辑抽象出来,就能够实现计算逻辑与客户端的低耦合。如何抽象?很简单,声明一个父类,并定义所有的计算逻辑,如下:
class Square { public: //向下移动处理 bool WhetherCanToDown(int pCurrLine,int pCurrColumn); void GoToDown(int pCurrLine,int pCurrColumn); //向左移动处理 bool WhetherCanToLeft(int pCurrLine,int pCurrColumn); void GoToLeft(int pCurrLine,int pCurrColumn); //向右移动处理 bool WhetherCanToRight(int pCurrLine,int pCurrColumn); void GoToRight(int pCurrLine,int pCurrColumn); //处理特定行 bool ClearLastLine(); void ClearLines(int lineStart,int lineEnd); protected: CCSprite*** pSquare;//窗体精灵数组 int pRow,pColumn;//固定行列 };之后,声明子类,并依此独立实现基类的方法,以下面这一方块为例:
//SquareType1成员 SquareType1::SquareType1(int pRow,int pColumn,CCSprite*** pSquare) { this->pRow=pRow; this->pColumn=pColumn; this->pSquare=pSquare; } //向下移动处理 bool SquareType1::WhetherCanToDown(int pCurrLine,int pCurrColumn) { for (int i = 0; i < 4; i++) { if (pSquare[pCurrLine][pCurrColumn + i]->getTag() == 1)//下层存在方块,禁止向下移动 { return false; } } return true; } void SquareType1::GoToDown(int pCurrLine,int pCurrColumn) { //下降一格 for (int i = 0; i < 4; i++) { //消除原色:档处于第一行时,无需消除原色 if (pCurrLine < pRow && pCurrLine > 0) { pSquare[pCurrLine - 1][pCurrColumn + i]->setColor(ccc3(255,255,255)); pSquare[pCurrLine - 1][pCurrColumn + i]->setTag(0); } //显示新色 if (pCurrLine < pRow) { pSquare[pCurrLine][pCurrColumn + i]->setColor(ccc3(52,228,249)); pSquare[pCurrLine][pCurrColumn + i]->setTag(1); } } } //向左移动处理 bool SquareType1::WhetherCanToLeft(int pCurrLine,int pCurrColumn) { if (pCurrLine < 1 || pCurrColumn <= 0 || pSquare[pCurrLine - 1][pCurrColumn - 1]->getTag() == 1) { return false; } return true; } void SquareType1::GoToLeft(int pCurrLine,int pCurrColumn) { pSquare[pCurrLine - 1][pCurrColumn - 1]->setColor(ccc3(52,249)); pSquare[pCurrLine - 1][pCurrColumn - 1]->setTag(1); pSquare[pCurrLine - 1][pCurrColumn + 3]->setColor(ccc3(255,255)); pSquare[pCurrLine - 1][pCurrColumn + 3]->setTag(0); } //向右移动处理 bool SquareType1::WhetherCanToRight(int pCurrLine,int pCurrColumn) { if (pCurrLine - 1 < 0 || pCurrColumn + 3 >= pColumn - 1 || pSquare[pCurrLine - 1][pCurrColumn + 3 + 1]->getTag() == 1) { return false; } return true; } void SquareType1::GoToRight(int pCurrLine,int pCurrColumn) { pSquare[pCurrLine - 1][pCurrColumn + 3 + 1]->setColor(ccc3(52,249)); pSquare[pCurrLine - 1][pCurrColumn + 3 + 1]->setTag(1); pSquare[pCurrLine - 1][pCurrColumn]->setColor(ccc3(255,255)); pSquare[pCurrLine - 1][pCurrColumn]->setTag(0); } //方块运行至最后一行 void SquareType1::ProcessLastLine() { ClearLines(pRow - 1,pRow - 1); } //变换当前方块的类型 bool SquareType1::WhetherCanChangeSquareType(int pCurrLine,int pCurrColumn) { if (pCurrLine + 1 > pRow - 1) { return false; } for (int i = 0; i < 4; i++) { if (i != 1 && pCurrLine - 2 + i > -1 && pSquare[pCurrLine - 2 + i][pCurrColumn + 1]->getTag() == 1) { return false; } } return true; } void SquareType1::ChangeSquareType(int pCurrLine,int pCurrColumn) { for (int i = 0; i < 4; i++) { if (i != 1 && pCurrLine - 1 > -1) { pSquare[pCurrLine - 1][pCurrColumn + i]->setColor(ccc3(255,255)); pSquare[pCurrLine - 1][pCurrColumn + i]->setTag(0); } if (i != 1 && pCurrLine - 2 + i > -1) { pSquare[pCurrLine - 2 + i][pCurrColumn + 1]->setColor(ccc3(52,249)); pSquare[pCurrLine - 2 + i][pCurrColumn + 1]->setTag(1); } } }
之后,在客户端中,利用工厂方法的方式实例化某一特定类型的方块,并将其赋值给父类:
Square* square=new SquareType1;从此,程序中所有的行为都用square进行操作。这有什么好处?1) 程序将及其简短;2) 需要添加其他类型方块?直接继承父类并独立实现其计算逻辑,客户端代码修改量为零。至此,程序的维护性与稳定性提升了一个等级。 原文链接:https://www.f2er.com/cocos2dx/340697.html