cocos2dx-v3.4 2048(四):单元格的设计与实现
前言单元格即显示2、4、8等数字的不同颜色的方格,如下图。本项目中Grid类实现单元格的相关内容,包括数字、背景更新,移动、新增、消除特效 设计#pragma once #include "cocos2d.h" USING_NS_CC; class Grid:public cocos2d::Layer { public: static char* G2U(const char* gb2312); static void changeType(int type); static int getType(); CREATE_FUNC(Grid); virtual bool init(); CC_SYNTHESIZE(int,_value,ScoreValue); //void updateValue(); void initValue(int value); void initValue(int value,int row,int column); bool compareTo(Grid* grid); void setLocalPosition(int row,int column); void moveAndClear(int targetRow,int targetColumn); void moveAndUpdate(); private: void loadMap(); void updateBg(); Label* _label; LayerColor* _bg; }; 首先我们看一下头文件中Grid类包含的函数,并对此进行说明介绍:
1. 基本功能设计 单元格的基本类容就是显示数字和背景色,也就是绘制一个LayerColor和一个Label的问题,在init函数中初始化实现;初始化完毕后就是设置显示值以及坐标的问题,由initValue实现 initValue函数,用于初始化单元格的显示值、背景色、和显示位置,因此其结构如下: void Grid::initValue(int value,int column) { setLocalPosition(row,column); initValue(value); } // init the first value of the grid,and the value should be 0 or 1 void Grid::initValue(int value) { _value = value; updateBg(); } void Grid::setLocalPosition(int row,int column) { this->setPosition(column*73 + 8,row*73 + 8); } void Grid::updateBg() { ... } 其中,由于显示值改变必然涉及到背景色的更新,因此设计的时候将initValue(int value)时,直接内调用了updateBg(且该函数设置为私有),更新背景色代码为: void Grid::updateBg() { _label->setString(map[_value]); Color3B color,fcolor = Color3B(255,255,255); switch(_value) { case 0: color = Color3B(247,213,97); fcolor = Color3B(120,120,120); break; case 1: color = Color3B(166,232,103); fcolor = Color3B(90,90,90); break; case 2: color = Color3B(87,212,154); break; case 3: color = Color3B(19,181,177); break; case 4: color = Color3B(68,138,202); break; case 5: color = Color3B(200,97,234); break; case 6: color = Color3B(225,115,181); break; case 7: color = Color3B(238,100,141); break; case 8: color = Color3B(243,157,79); break; case 9: color = Color3B(245,124,78); break; case 10: color = Color3B(246,76,20); break; case 11: color = Color3B(210,210,10); //Color3B(105,84,187); break; case 12: color = Color3B(190,194,22); // Color3B(50,86,204); break; case 13: color = Color3B(160,160,10); break; case 14: color = Color3B(50,204);// Color3B(24,66,149); break; default: color = Color3B(24,149); break; } _label->setColor(fcolor); _bg->setColor(color); if(type != 1) return; if(_value > 15) _label->setSystemFontSize(20); else if(_value > 12) _label->setSystemFontSize(24); else if(_value > 8) _label->setSystemFontSize(26); } 2. 显示内容设计 由于本项目有三个模式,不同模式单元格的显示内容不同,如下图:(从左到右,分别是经典模式,小兵传奇,纯色模式) 三种不同的模式中,显示内容如: std::string map[16] = {"0"}; std::string guan[] = {"小兵","下士","中士","上士","少尉","中尉","上尉","少校","中校","上校","大校","准将","少将","中将","上将","元帅"}; std::string num[] = {"2","4","8","16","32","64","128","256","512","1024","2048","4096","8192","16384","32768","65536","131072","262144","524288"}; 然后需要根据当前的模式来选择显示的内容集: void Grid::loadMap() { if(map[0].compare("0") == 0) //first load,then get the type from save file { type = UserDefault::getInstance()->getIntegerForKey("type",0); } for(int i = 0; i<16; i++) { if(type == 0) map[i] = Grid::G2U(guan[i].c_str()); // soldier mode else if(type == 1) map[i] = num[i]; // classic mode else map[i] = ""; // color mode } } 在该函数中,type作为全局变量使用,对于第二个for循环,type直接与0,1其他进行比较是个隐患,建议用enum进行修改(当时顺手直接用了常量)为了阅读简洁改成如下代码: enum StateType{ SOLDIER,// 0 CLASSIC,// 1 COLOR // 2 }; void Grid::loadMap() { if(map[0].compare("0") == 0) //first load,0); } for(int i = 0; i<16; i++) { if(type == StateType::SOLDIER) map[i] = Grid::G2U(guan[i].c_str()); else if(type == StateType::CLASSIC) map[i] = num[i]; else if(type == StateType::COLOR) map[i] = ""; } } 从上面的代码中可以看出,直接影响显示内容的是变量type,其相关的函数为: static int type = 0; void Grid::changeType(int _type) { type = _type; GameTool::getInstance()->loadScore(type); GameLayer::getInstance()->loadGrids(type); } int Grid::getType() { return type; } 当调用changeType时,需要重现加载分数和最高分,以及更新游戏模式后的游戏布局(从save file中获取状态并恢复) 3. 移动特效 单元格的移动特效有一下几种,刚出现时的淡入效果FadeIn,单纯的移动到目的地移动效果MoveTo,移动并消除特效,移动合并出一个新的单元格特效 3.1 新生成单元格特效 写到这才发现在代码中进入特效实在GameLayer中随机生成一个单元格时,顺手写进去的,因此没有加入Grid函数中,显然是不符合本文的设计要求的,因此将相关代码搬了个家,首先在 Grid.h中添加 initAction(),并在Grid.cpp中实现: void Grid::initAction() { auto a1 = ScaleTo::create(0.3f,1); auto a2 = FadeIn::create(1); auto a3 = Spawn::create(a1,a2,nullptr); setScale(0); runAction(a3); }
3.2 单纯移动特效 单纯的移动单元格,可以直接调用MoveTo来实现,同样的该处代码直接顺手写在了GameLayer的moveOnly函数中,同样的迁移过来,新的代码为: void Grid::moveOnly(int targetRow,int targetColumn) { runAction(MoveTo::create(0.2f,Vec2(73 * targetColumn + 8,73 * targetRow + 8))); }(插一句,设计很重要,顺手写会对后面调试以及审阅代码增加难度) 3.3 移动并消除特效 当合并两个单元格时,至少需要消除其中的一个单元格,这里有两种设计思路:
无论选择哪一种方式,都至少有一个单元格是要调用移动并消除特效的,当然本项目选择了第二种方式,至于为什么,好吧当时写的时候这样更加简单,(当然推荐用第一种,节省了node创建回收开销) 移动并消除主要包括移动、淡出、自销毁三个特效,具体代码如下: void Grid::moveAndClear(int targetRow,int targetColumn) { auto a1 = MoveTo::create(0.2f,73 * targetRow + 8)); auto a2 = FadeOut::create(0.2f); auto a3 = Spawn::create(a1,nullptr); auto a4 = CallFunc::create([&]{ //log("remove child"); this->removeFromParent(); }); this->runAction(Sequence::create(a3,a4,nullptr)); } CallFunc是一个很有意思的类,表示调用函数的特效,上面代码的意思是当单元格移动到目的地时,将该单元格从父节点中移除(引用计数-1, 为0时会自动被回收) 3.4 移动合并特效 由于我们选择第二种方式,因此这个特效就是简单的淡入、放大缩小的特效,如下: void Grid::moveAndUpdate() { this->setVisible(false); auto action01 = ScaleTo::create(0.1f,1.1f); auto action02 = ScaleTo::create(0.1f,1); auto action03 = FadeIn::create(0.2f); auto action04 = Sequence::create(action03,CallFunc::create([&]{ //log("add child"); this->setVisible(true); }),action01,action02,nullptr); this->runAction(action04); } 那么选择第一种方式呢,该如何编写相关代码?其实使用第一种也很简单,其分解过程为: 移动到目的地 ---》 更新value ---》 放大缩小特效 void Grid::moveAndUpdate(int targetRow,73 * targetRow + 8)); auto a2 = CallFunc::create([&]{ //log("remove child"); this->updateValue(); // value++ and update bg }); auto a3 = ScaleTo::create(0.1f,1.1f); auto a4 = ScaleTo::create(0.1f,1); auto a5 = Sequence::create(a1,nullptr); this->runAction(a5); } 4. 判断单元格是否相等 判断单元格是否相等是合并单元格的前提,当两个单元格的value相等,即表示可合并;否则不能合并,代码如下: bool Grid::compareTo(Grid* grid) { if(grid == nullptr) return false; return _value == grid->getScoreValue(); } 看到上面的代码发现可以直接简化为一句: bool Grid::compareTo(Grid* grid) { return grid!=nullptr && _value == grid->getScoreValue(); } (说一句,在判断指针参数的时候,需要首先确定指针非空,否则可能出现大家都知道的bug) 实现、小结上面贴出了部分代码,并给出了说明, 这里不贴代码了, 源码请参考: https://github.com/liuyueyi/2048 小结一下:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |