Cocos2d-x 3.0 《2048》手游开发揭秘
一、游戏介绍 2048游戏是一款风靡全球的休闲类游戏,通过控制4X4范围内的数字块的移动,相同数字的块在移动的过程中累加消除,该游戏即考虑了手机触摸屏幕的操作方式,又在游戏过程中不断产生新块以增加游戏难度和耐玩性,游戏运行效果如下图: 二、创建项目&游戏逻辑分析 2.1 本项目开发环境: Mac OS 10.9 XCode 5.0 Android NDK r9 2.2部署Cocos2d-x3.2的运行环境 第一步、到www.cocos2d-x.org下载Cocos2d-x3.2的源代码 第二步、解压缩源码到桌面/Cocos2d-x2014目录 第三步、进入终端工具,执行以下命令进入源码目录 cd ~/Desktop/Cocos2d-x2014 第四步、输入./setup.py 执行安装 (Python是Mac OS自带的,不用安装,具体步骤详见视频) 第五步、输入以下命令创建一个空项目 cocosnew Game2048 –d ~/Desktop/game –l cpp –p com.game.test 第六步、使用XCode打开 ~/Desktop/game/Game2048/proj.ios_mac/Game2048.xcodeproj 编译运行,将会出现一个HelloWorld的场景 2.3 分析《2048》的游戏逻辑 在屏幕上显示4X4的游戏区域,16个元素 在游戏开始时在随机位置产生一个元素块(数值2) 用户通过触摸屏幕可以移动该块向 上下左右移动 在屏幕上的所有块将按滑动的方向移动,直到顶点或被其他块挡住 在移动的过程中,相邻相同数字的块,相加产生一个新的块,旧块消失 如果有3块数字相同则消除最前方的2个块 如果有4块数字相同的块,则前方2个块相加,后面2个块相加 如果有4块数字两两相邻的相同,则两两相加,产生新的2个块 每移动一次,在屏幕上将会在空白区域产生一个新的块(2或4) 如果屏幕上没有了空白区域 判断4X4范围内是否有相邻的数字相同的块, 不产生新块等待用户移动 如果在4X4范围内没有相邻的数字相同的块, GAME OVER 三、游戏场景和游戏画布 3.1,首先在项目中添加SplashScene.h和SplashScene.cpp classSplash:Layer { bool init(); public: CREATE_FUNC(Splash); static Scene * createScene(); void jumpToGame(float t); }; 3.2 实现3秒钟以后自动跳转到游戏场景 bool Splash::init(){ if(!Layer::init()) { return false; } //显示游戏名称 autolabelGame=Label::createWithBMFont("futura-48.fnt","2048"); labelGame->setPosition(Point(GAME_SCREEN_WIDTH/2,GAME_SCREEN_HEIGHT/2)); this->addChild(labelGame); labelGame->setScale(1.5); //显示制作单位 autolabelGameVec=Label::createWithBMFont("futura-48.fnt","Sea2014.6.25"); labelGameVec->setPosition(Point(GAME_SCREEN_WIDTH/2,GAME_SCREEN_HEIGHT/4)); this->addChild(labelGameVec); labelGameVec->setScale(0.8); //计划任务3秒钟之后,自动跳转到游戏场景 this->scheduleOnce(schedule_selector(Splash::jumpToGame),3); return true; } (这里使用了BMFont,详见视频) 3.3 创建GameScene.h和GameScene.cpp实现游戏场景 这里先简单实现,后面再对游戏场景完善功能。 四、可以移动的块的封装 每个可以移动的块定义如下: class MovedTiled:public Node { public: int m_row; //行 int m_col; //列 int m_number;//数字 void showAt(int r,int c);//在某个位置显示这个块 void moveTo(int r,int c);//移动到r行 c列 void doubleNumber();//X2 CREATE_FUNC(MovedTiled); bool init();
}; 五、元素块的封装 在游戏场景通过一个4X4的数组来定义游戏地图,然后再通过判断数组的上下左右实现元素移动的游戏逻辑。 5.1//初始化逻辑网格和数组 //初始化网格的每一个块 for (int row=0; row<GAME_ROWS; row++) { for (int col=0;col<GAME_COLS; col++) { auto layerTiled=LayerColor::create(Color4B(70,70,255), GAME_TILED_WIDTH, GAME_TILED_HEIGHT); layerTiled->setPosition( Point(GAME_TILED_WIDTH*col+ GAME_TILED_BOARD_WIDTH*(col+1), GAME_TILED_HEIGHT*row+ GAME_TILED_BOARD_WIDTH*(row+1))); colorBack->addChild(layerTiled); } } for(int i=0;i<GAME_ROWS;i++) { for(int j=0;j<GAME_COLS;j++) { map[i][j]=0; //空白 } } 5.2 初始化数字块 void Game::newMovedTiled()//产生新块 { auto tiled=MovedTiled::create(); int freeCount=16-m_allTiled.size(); int num=rand()%freeCount; int row=0; int col=0; int count=0; bool find=false; for (; row<GAME_ROWS; row++) { for(col=0;col<GAME_COLS;col++) { if(map[row][col]==0) { count++;//记录空白的数量 if(count>=num) { find=true; break; }
} } if (find) { break; } }
colorBack->addChild(tiled); tiled->showAt(row,col); m_allTiled.pushBack(tiled); map[row][col]=m_allTiled.getIndex(tiled)+1; /// if(freeCount==1)//没有空白区域 {//判定游戏输赢 … //跳转场景 … return; } } 六、可移动块移动和消除的实现(核心) 移动的过程是对数组地图的判断,以向上移动为例,代码如下: void Game::moveUp(){ //向上移动所有的块 for (int col=0; col<GAME_COLS; col++) { for(introw=GAME_ROWS-1;row>=0;row--) { if(map[row][col]>0) { for(int row1=row;row1<GAME_ROWS-1;row1++) { if(map[row1+1][col]==0) { map[row1+1][col]=map[row1][col]; map[row1][col]=0; m_allTiled.at(map[row1+1][col]-1)->moveTo(row1+1,col); } else {//判断,是否可以消除 int numObj=m_allTiled.at(map[row1+1][col]-1)->m_number; int numNow=m_allTiled.at(map[row1][col]-1)->m_number; if(numObj==numNow) { m_sound_clear=true; m_score+=numObj*2; m_allTiled.at(map[row1+1][col]-1)->doubleNumber(); m_allTiled.at(map[row1][col]-1)->removeFromParent(); int index=map[row1][col]; m_allTiled.erase(map[row1][col]-1); for (int r=0; r<GAME_ROWS; r++) { for (int c=0; c<GAME_COLS; c++) { if(map[r][c]>index) {map[r][c]--; } } } map[row1][col]=0; } break; } } } } }
} (具体看视频吧,大脑清醒的时候看) 七、游戏中加入声音 这个很简单了,首先引入 #include "SimpleAudioEngine.h" 然后using namespace CocosDenshion; 之后就可以 SimpleAudioEngine::getInstance()->playEffect("moveClear.wav"); 八、增加块的动画和颜色 让不同数字的变化有不同的背景颜色,这个很easy,不过到PS中看像素需要点时间 void MovedTiled::doubleNumber() { this->m_number=this->m_number*2; auto bk=this->getChildByTag(110); Label * label=(Label *)bk->getChildByTag(10); label->setString(StringUtils::format("%d",m_number)); //动画 bk->runAction( Sequence::create( ScaleTo::create(0.2,0.8), ScaleTo::create(0.2,1.2), ScaleTo::create(0.2,1) ,NULL));
switch (this->m_number) { case 2: bk->setColor(Color3B(230,220,210));
case 4: bk->setColor(Color3B(230,210,190)); break; case 8: bk->setColor(Color3B(230,150,100)); label->setColor(Color3B(255,255,255)); break; case 16: bk->setColor(Color3B(230,120,80)); label->setColor(Color3B(255,255)); break; case 32: bk->setColor(Color3B(230,100,90)); label->setColor(Color3B(255,255)); break; case 64: bk->setColor(Color3B(230,60)); label->setColor(Color3B(255,255)); break; case 128: bk->setColor(Color3B(230,190,255)); break; case 256: bk->setColor(Color3B(230,255)); break; case 512: bk->setColor(Color3B(230,255)); break; case 1024: case 2048: label->setScale(0.5); bk->setColor(Color3B(210,180,30)); label->setColor(Color3B(255,255)); } } 九、死亡判定 在产生新块的方法中,判定每一个块的上下左右是否有相同数字的,如果没有则GAME OVER if(freeCount==1)//没有空白区域 {//判定游戏输赢 //上 ,下 左 右 是否还能移动 for(int r=0;r<GAME_ROWS;r++) { for(int c=0;c<GAME_COLS;c++) { //第r行 第c列的数值 int num=m_allTiled.at(map[r][c]-1)->m_number; int objNum=0; //上 if(r+1<GAME_ROWS) { objNum=m_allTiled.at(map[r+1][c]-1)->m_number; if(num==objNum) {return ;} } //下 if(r-1>=0) { objNum=m_allTiled.at(map[r-1][c]-1)->m_number; if(num==objNum) {return ;} } //左 if(c-1>=0) { objNum=m_allTiled.at(map[r][c-1]-1)->m_number; if(num==objNum) {return ;} } //右 if(c+1<GAME_COLS) { objNum=m_allTiled.at(map[r][c+1]-1)->m_number; if(num==objNum) {return ;} }
} } //跳转场景 auto scene=GameOver::createScene(); Director::getInstance()->replaceScene(TransitionFadeDown::create(0.5,scene));
return; } 十、将《2048》移植到Android平台 10.1 屏幕适配 我们开发时候是以320X480(比较过时,一般会以960X640和1024X768为基础),但是目标Android设备的分辨率各不相同,为了让屏幕显示过程中适应终端的屏幕尺寸,可以缩放场景,在AppDelegate.cpp中修改如下代码 boolAppDelegate::applicationDidFinishLaunching() { // initialize director auto director = Director::getInstance(); auto glview = director->getOpenGLView(); if(!glview) { glview =GLView::create("My Game"); director->setOpenGLView(glview); } glview->setDesignResolutionSize(320,480,ResolutionPolicy::EXACT_FIT); director->setDisplayStats(true); director->setAnimationInterval(1.0 / 60); auto scene = Splash::createScene(); director->runWithScene(scene); return true; } glview->setDesignResolutionSize的用法大家可以参考相关资料。 10.2 修改makefile文件 打开Game2048/proj.android/jni/Android.mk修改如下: LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) $(callimport-add-path,$(LOCAL_PATH)/../../cocos2d) $(callimport-add-path,$(LOCAL_PATH)/../../cocos2d/external) $(callimport-add-path,$(LOCAL_PATH)/../../cocos2d/cocos) LOCAL_MODULE := cocos2dcpp_shared LOCAL_MODULE_FILENAME := libcocos2dcpp LOCAL_SRC_FILES:= hellocpp/main.cpp ../../Classes/AppDelegate.cpp ../../Classes/SplashScene.cpp ../../Classes/GameScene.cpp ../../Classes/MovedTiled.cpp ../../Classes/GameOver.cpp LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static LOCAL_WHOLE_STATIC_LIBRARIES +=cocosdenshion_static # LOCAL_WHOLE_STATIC_LIBRARIES += box2d_static # LOCAL_WHOLE_STATIC_LIBRARIES +=cocosbuilder_static # LOCAL_WHOLE_STATIC_LIBRARIES += spine_static # LOCAL_WHOLE_STATIC_LIBRARIES +=cocostudio_static # LOCAL_WHOLE_STATIC_LIBRARIES +=cocos_network_static # LOCAL_WHOLE_STATIC_LIBRARIES +=cocos_extension_static include $(BUILD_SHARED_LIBRARY) $(call import-module,.) $(call import-module,audio/android) # $(call import-module,Box2D) # $(callimport-module,editor-support/cocosbuilder) # $(call import-module,editor-support/spine) # $(callimport-module,editor-support/cocostudio) # $(call import-module,network) # $(call import-module,extensions) (黑色加粗部分为修改内容,如果Classes下有子目录,还需要修改 LOCAL_C_INCLUDES:= …) 10.3.NDK编译源码生成.so文件 打开终端,进入Game2048项目目录,输入以下命令: cocos compile –p android (好了,开始等吧,如果一切顺利会在Game2048/libs/生成一个.so文件) 10.4.在Eclipse中刷新项目 在Eclipse中选择项目名称然后刷新,这是后就可以直接安装到Android手机了(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |