Cocos2d-x 3.x版2048游戏开发
Cocos2d-x 3.x版2048游戏开发
本篇博客给大家介绍如何快速开发2048这样一款休闲游戏,理解整个2048游戏的开发流程,从本篇博客你将可以学习到以下内容:
这里注明一下,本教程来自极客学院,小巫对其中代码进行了解释。
- 2048游戏的逻辑
- Cocos2d-x中上下左右手势的识别
- 游戏中卡片类的创建
- 添加卡片到游戏中
- 游戏中的逻辑实现
- 游戏中随机卡片的生成
- 游戏结束判断
- 游戏分数的添加
- 游戏美化
笔者的开发环境:
Cocos2d-x 3.1.1(开发引擎)
Visual Studio 2012(Win32)
Xcode 5.1(Mac系统下)
理解2048游戏逻辑
2048游戏逻辑并不复杂,4*4的卡片布局,玩家通过手势上下左右滑动来累加卡片数值,直到累加到2048。笔者用一张图说明:
这是一张游戏中的图,在图中同一方向并且数值相同的卡片可以进行叠加,比如128和128在同一行,玩家可以通过向左或向右的手势,对其进行叠加。笔者向右滑动手势,则会变成以下效果:
Cocos2d-x中上下左右手势的识别
玩家在玩2048游戏时,手势是最频繁的操作,所以我们需要对手势所产生的事件进行监听。
在HelloWorldScene.h头文件中声明两个需要实现的监听事件:
-
- virtualboolonTouchBegan(cocos2d::Touch*touch,cocos2d::Event*unused_event);
- voidonTouchEnded(cocos2d::Event*unused_event);
声明点击的位置属性
//点击的元素位置
- intfirstX,firstY,endX,endY;
一个是触摸开始的事件,一个是触摸结束的事件。
然后再HelloWorldScene.cpp文件中实现这两个方法:
copy
boolHelloWorld::onTouchBegan(cocos2d::Event*unused_event){
//触摸点
PointtouchP0=touch->getLocation();
firstX=touchP0.x;
firstY=touchP0.y;
returntrue;
}
//触摸结束触发
voidHelloWorld::onTouchEnded(cocos2d:://获取触摸点位置
//获取X轴和Y轴的移动距离
endX=firstX-touchP0.x;
endY=firstY-touchP0.y;
//判断X轴和Y轴的移动距离,如果X轴的绝对值大于Y轴的绝对值就是左右否则是上下
if(abs(endX)>abs(endY)){
//左右
if(endX+5>0){
//左边
if(doLeft()){
autoCreateCardNumber();
doCheckGameOver();
}else{
if(doRight()){
autoCreateCardNumber();
doCheckGameOver();
}
//上下
if(endY+5>0){
//下边
if(doDown()){
//上边
if(doUp()){
};
}
在HelloWorld的init方法中设置监听器的这两个方法的监听回调:
copy
//创建手势识别的事件监听器
autotouchListener=EventListenerTouchOneByOne::create();
touchListener->onTouchBegan=CC_CALLBACK_2(HelloWorld::onTouchBegan,this);
touchListener->onTouchEnded=CC_CALLBACK_2(HelloWorld::onTouchEnded,this);
//添加事件监听
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener,this);
上面中根据计算开始位置到结束位置X轴和Y轴的移动距离来判断是向左、向右、向上还是向下的方向:
在头文件中声明四个方向的方法:
copy
//上下左右的方法
booldoLeft();
booldoRight();
booldoUp();
booldoDown();
然后对其进行实现。这四个方法的逻辑实现放到后面进行讲解。
游戏中卡片类的创建
卡片类是组成2048游戏的基础,4*4的方格的16个位置放置不同的卡片,每个位置为独立的一张卡片。
创建CardSprite.h的头文件:
copy
//
//CardSprite.h
//2048Game
//
//Createdbymacon14-7-16.
#ifndef___048Game__CardSprite__
#define___048Game__CardSprite__
#include"cocos2d.h"
classCardSprite:publiccocos2d::Sprite{
public:
//初始化游戏卡片的方法
staticCardSprite*createCardSprite(intnumbers,intwidth,153); font-weight:bold; background-color:inherit">intheight,153); font-weight:bold; background-color:inherit">floatCardSpriteX,153); font-weight:bold; background-color:inherit">floatCardSpriteY);
boolinit();
CREATE_FUNC(CardSprite);
//设置数字
voidsetNumber(intnum);
//获取数字
intgetNumber();
private:
//显示在界面的数字
intnumber;
voidenemyInit(//定义显示数字的控件
LabelTTF*labTTFCardNumber;
//显示的背景
LayerColor*layerColorBG;
};
#endif/*defined(___048Game__CardSprite__)*/
我们可以从游戏中看到,每张卡片有背景颜色和一个数字,所以我们在头文件需要声明它这两个属性。
CardSprite.cpp中对头文件方法的实现:
copy
//CardSprite.cpp
//Createdbywwjon14-7-16.
#include"CardSprite.h"
USING_NS_CC;
CardSprite*CardSprite::createCardSprite(intnumbers,intwidth,87); font-weight:bold; background-color:inherit">intheight,87); font-weight:bold; background-color:inherit">floatCardSpriteX,87); font-weight:bold; background-color:inherit">floatCardSpriteY){
//new一个卡片精灵
CardSprite*enemy=newCardSprite();
if(enemy&&enemy->init()){
enemy->autorelease();
enemy->enemyInit(numbers,width,height,CardSpriteX,CardSpriteY);
returnenemy;
CC_SAFE_DELETE(enemy);
returnNULL;
//卡片初始化方法
boolCardSprite::init(){
if(!Sprite::init()){
false;
true;
//设置数字
voidCardSprite::setNumber(intnum){
number=num;
//判断数字的大小来调整字体的大小
if(number>=0){
labTTFCardNumber->setFontSize(80);
if(number>=16){
labTTFCardNumber->setFontSize(60);
if(number>=128){
labTTFCardNumber->setFontSize(40);
if(number>=1024){
labTTFCardNumber->setFontSize(20);
//判断数组的大小调整颜色
if(number==0){
layerColorBG->setColor(cocos2d::Color3B(200,190,180));
if(number==2){
layerColorBG->setColor(cocos2d::Color3B(240,230,220));
if(number==4){
layerColorBG->setColor(cocos2d::Color3B(240,220,200));
if(number==8){
if(number==16){
if(number==32){
if(number==64){
if(number==128){
if(number==256){
if(number==512){
if(number==1024){
layerColorBG->setColor(cocos2d::Color3B(0,0));
if(number==2048){
layerColorBG->setColor(cocos2d::Color3B(0,0));
//更新显示的数字
if(number>0){
labTTFCardNumber->setString(__String::createWithFormat("%i",num)->getCString());
labTTFCardNumber->setString("");
intCardSprite::getNumber(){
returnnumber;
//第1个参数为数字,第2、3个参数为卡片的宽高,第4、5个参数为卡片的位置
voidCardSprite::enemyInit(//初始化数字
number=numbers;
//加入游戏卡片的背景颜色
layerColorBG=cocos2d::LayerColor::create(cocos2d::Color4B(200,255),width-15,height-15);
layerColorBG->setPosition(Point(CardSpriteX,CardSpriteY));
//判断如果不等于0就显示,否则为空
//加入中间字体
labTTFCardNumber=cocos2d::LabelTTF::create(__String::createWithFormat("%i",number)->getCString(),"HirakakuProN-W6",100);
//显示卡片数字的位置,这里显示在背景的中间
labTTFCardNumber->setPosition(Point(layerColorBG->getContentSize().width/2,layerColorBG->getContentSize().height/2));
//添加卡片数字到背景中
layerColorBG->addChild(labTTFCardNumber);
}else{
//加入中间字体
labTTFCardNumber=cocos2d::LabelTTF::create("","HirakakuProN-w6",80);
layerColorBG->addChild(labTTFCardNumber);
//将卡片添加到层
this->addChild(layerColorBG);
}
每张卡片都有相同的宽和高,但数字和位置不同,数字是显示在背景中间的,所以我们需要5个参数来初始化卡片的位置。
添加卡片到游戏中
卡片放置在4*4方框中的不同位置,首先我们需要计算出每张卡片的宽高,然后计算每张卡片所在的位置,最后进行显示。
在HelloWorldScene.h头文件中声明创建卡片的方法:
copy
//创建卡片
voidcreateCardSprite(cocos2d::Sizesize);
在HelloWorldScene.cpp中实现该方法:
copy
//创建卡片,size为屏幕大小
voidHelloWorld::createCardSprite(cocos2d::Sizesize){
//求出单元格的宽度和高度,28为左右距离
intlon=(size.width-28)/4;
//4*4的单元格
for(intj=0;j<4;j++){
inti=0;i<4;i++){
//数字0,宽高相同为lon,lon+j+20为卡片X轴位置,如lon+0+20为第一个卡片的位置,20是每张卡片的间隙,lon+i+20+size.height/6代表的意思是屏幕大小竖方向分了六份,我们这里只放4个位置
CardSprite*card=CardSprite::createCardSprite(0,lon,lon*j+10,lon*i+10+size.height/6);
addChild(card);
//添加卡片到二维数组中
cardArr[j][i]=card;
}
笔者对每张卡片显示的位置在代码中进行了说明,读者也可以思考一下为什么要这么做。
游戏中的逻辑实现
2048游戏最重要的部分就是四个方向的逻辑实现,也是开发这个游戏的难点所在。
copy
//向左
boolHelloWorld::doLeft(){
log("doLeft");
boolisdo=//最外层循环为4*4迭代
inty=0;y<4;y++){
intx=0;x<4;x++){
//这一层循环为判断卡片是合并还是清空
intx1=x+1;x1<4;x1++){
if(cardArr[x1][y]->getNumber()>0){
if(cardArr[x][y]->getNumber()<=0){
//设置为右边卡片的数值
cardArr[x][y]->setNumber(cardArr[x1][y]->getNumber());
cardArr[x1][y]->setNumber(0);
x--;
isdo=elseif(cardArr[x][y]->getNumber()==cardArr[x1][y]->getNumber()){
//当前卡片的值与其比较卡片的值相等,设置为其的2倍
cardArr[x][y]->setNumber(cardArr[x][y]->getNumber()*2);
//设置分数
score+=cardArr[x][y]->getNumber();
labelTTFCardNumber->setString(__String::createWithFormat("%i",score)->getCString());
isdo=break;
returnisdo;
//向右
boolHelloWorld::doRight(){
log("doRight");
intx=3;x>=0;x--){
//循环判断左边卡片往右是合并还是清空
intx1=x-1;x1>=0;x1--){
if(cardArr[x1][y]->getNumber()>0){
if(cardArr[x][y]->getNumber()<=0){
cardArr[x][y]->setNumber(cardArr[x1][y]->getNumber());
x++;
cardArr[x][y]->setNumber(cardArr[x][y]->getNumber()*2);
cardArr[x1][y]->setNumber(0);
break;
returnisdo;
//向上
boolHelloWorld::doUp(){
log("doUp");
false;
//最外层循环为4*4迭代
inty=3;y>=0;y--){
//这一层循环为判断卡片是合并还是清空
inty1=y-1;y1>=0;y1--){
if(cardArr[x][y1]->getNumber()>0){
//为空
//设置为右边卡片的数值
cardArr[x][y]->setNumber(cardArr[x][y1]->getNumber());
cardArr[x][y1]->setNumber(0);
y++;
if(cardArr[x][y]->getNumber()==cardArr[x][y1]->getNumber()){
//当前卡片的值与其比较卡片的值相等,设置为其的2倍
//跳出
//向下
boolHelloWorld::doDown(){
log("doDown");
inty1=y+1;y1<4;y1++){
y--;
//设置分数
score+=cardArr[x][y]->getNumber();
labelTTFCardNumber->setString(__String::createWithFormat("%i",score)->getCString());
}
游戏中随机卡片的生成
在HelloWorldScene.h头文件中声明随机生成卡片的方法:
copy
//自动卡片生成
voidautoCreateCardNumber();
在HelloWorldScene.cpp实现该方法:
copy
//自动生成卡片
voidHelloWorld::autoCreateCardNumber(){
inti=CCRANDOM_0_1()*4;
intj=CCRANDOM_0_1()*4;
//判断是否已经存在的位置
if(cardArr[i][j]->getNumber()>0){
//已存在,递归创建
//生成2和4的比例是1:9的概率
cardArr[i][j]->setNumber(CCRANDOM_0_1()*10<1?4:2);
}
游戏结束判断
在HelloWorldScene.h中声明判断游戏结束的方法:
在HelloWorldScene.cpp中实现该方法:
copy
//游戏是否还能继续运行下去
voidHelloWorld::doCheckGameOver(){
boolisGameOver=if(cardArr[x][y]->getNumber()==0
||(x>0&&(cardArr[x][y]->getNumber()==cardArr[x-1][y]->getNumber()))
||(x<3&&(cardArr[x][y]->getNumber()==cardArr[x+1][y]->getNumber()))
||(y<0&&(cardArr[x][y]->getNumber()==cardArr[x][y-1]->getNumber()))
||(x<3&&(cardArr[x][y]->getNumber()==cardArr[x][y+1]->getNumber()))){
isGameOver=if(isGameOver){
//结束游戏
Director::getInstance()->replaceScene(TransitionFade::create(1,HelloWorld::createScene()));
}
游戏分数的添加
2048中需要对分数进行统计并显示给玩家,我们在HelloWolrdScene.h文件中声明分数和显示分数的控件:
copy
//整体游戏的分数
intscore;
//定义显示数据的控件
cocos2d::LabelTTF*labelTTFCardNumber;
在HelloWorldScene.cpp中对分数初始化为0,并显示“分数”的文本:
copy
score=0;
//获得屏幕可视大小
SizevisibleSize=Director::getInstance()->getVisibleSize();
//加入游戏的背景
autolayerColorBG=cocos2d::LayerColor::create(cocos2d::Color4B(180,170,255));
this->addChild(layerColorBG);
//在上方加入游戏的分数
autolabelTTFCardNumberName=LabelTTF::create("分数:",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> labelTTFCardNumberName->setPosition(Point(visibleSize.width/5,visibleSize.height-100));
addChild(labelTTFCardNumberName);
labelTTFCardNumber=LabelTTF::create("0",80);
labelTTFCardNumber->setPosition(visibleSize.width/2+100,visibleSize.height-100);
addChild(labelTTFCardNumber
);
关于分数的计算,在游戏中的逻辑已经实现,具体读者可以查看代码,后面笔者也会提供完整的代码。
游戏美化
2048中我们会发现不同的数字会有不同的背景,然后字体大小也会随数值的变化而变化,实现这个需要通过判断数值的大小来显示不同的颜色背景和设置不同字体大小。
这个功能的实现是在卡片类设置数值的时候实现的,代码逻辑如下:
最后笔者给出所有代码清单:
AppDelegate.h
AppDelegate.cpp
HelloWorldScene.h
HelloWorldScene.cpp
CardSprite.h
CardSprite.cpp
>>>AppDelegate.h
copy
#ifndef_APP_DELEGATE_H_
#define_APP_DELEGATE_H_
#include"cocos2d.h"
/**
@briefThecocos2dApplication.
ThereasonforimplementasprivateinheritanceistohidesomeinterfacecallbyDirector.
*/
classAppDelegate:privatecocos2d::Application
{
public:
AppDelegate();
virtual~AppDelegate();
/**
@briefImplementDirectorandSceneinitcodehere.
@returntrueInitializesuccess,appcontinue.
@returnfalseInitializefailed,appterminate.
*/
virtualboolapplicationDidFinishLaunching();
@briefThefunctionbecalledwhentheapplicationenterbackground
@paramthepointeroftheapplication
voidapplicationDidEnterBackground();
@briefThefunctionbecalledwhentheapplicationenterforeground
voidapplicationWillEnterForeground();
#endif//_APP_DELEGATE_H_
>>>AppDelegate.cpp
copy
#include"AppDelegate.h"
#include"HelloWorldScene.h"
USING_NS_CC;
AppDelegate::AppDelegate(){
AppDelegate::~AppDelegate()
{
boolAppDelegate::applicationDidFinishLaunching(){
//initializedirector
autodirector=Director::getInstance();
autoglview=director->getOpenGLView();
if(!glview){
glview=GLView::create("MyGame");
director->setOpenGLView(glview);
glview->setDesignResolutionSize(480,800,ResolutionPolicy::SHOW_ALL);
//turnondisplayFPS
director->setDisplayStats(false);
//setFPS.thedefaultvalueis1.0/60ifyoudon'tcallthis
director->setAnimationInterval(1.0/60);
//createascene.it'sanautoreleaseobject
autoscene=HelloWorld::createScene();
//run
director->runWithScene(scene);
//Thisfunctionwillbecalledwhentheappisinactive.Whencomesaphonecall,it'sbeinvokedtoo
voidAppDelegate::applicationDidEnterBackground(){
Director::getInstance()->stopAnimation();
//ifyouuseSimpleAudioEngine,itmustbepause
//SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
//thisfunctionwillbecalledwhentheappisactiveagain
voidAppDelegate::applicationWillEnterForeground(){
Director::getInstance()->startAnimation();
//SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
}
>>>HelloWorldScene.h
copy
#ifndef__HELLOWORLD_SCENE_H__
#define__HELLOWORLD_SCENE_H__
classHelloWorld:publiccocos2d::Layer
staticcocos2d::Scene*createScene();
boolinit();
voidmenuCloseCallback(cocos2d::Ref*pSender);
CREATE_FUNC(HelloWorld);
boolonTouchBegan(cocos2d::Touch*touch,cocos2d::Event*unused_event);
voidonTouchEnded(cocos2d::Touch*touch,cocos2d::Event*unused_event);
booldoLeft();
booldoRight();
booldoUp();
booldoDown();
//自动卡片生成
voidautoCreateCardNumber();
//判断游戏是否还能继续运行下去
voiddoCheckGameOver();
private:
//定义一个二维数组
CardSprite*cardArr[4][4];
voidcreateCardSprite(cocos2d::Sizesize);
//整体游戏的分数
intscore;
//定义显示数据的控件
cocos2d::LabelTTF*labelTTFCardNumber;
#endif//__HELLOWORLD_SCENE_H__
>>>HelloWorldScene.cpp
copy
#include"HelloWorldScene.h"
#include"CardSprite.h"
Scene*HelloWorld::createScene()
//'scene'isanautoreleaseobject
autoscene=Scene::create();
//'layer'isanautoreleaseobject
autolayer=HelloWorld::create();
//addlayerasachildtoscene
scene->addChild(layer);
//returnthescene
returnscene;
//on"init"youneedtoinitializeyourinstance
boolHelloWorld::init()
//////////////////////////////
//1.superinitfirst
if(!Layer::init())
score=0;
autotouchListener=EventListenerTouchOneByOne::create();
touchListener->onTouchBegan=CC_CALLBACK_2(HelloWorld::onTouchBegan,this);
touchListener->onTouchEnded=CC_CALLBACK_2(HelloWorld::onTouchEnded,153); font-weight:bold; background-color:inherit">this);
//调用生成卡片的方法
createCardSprite(visibleSize);
//调用生成随机数
//游戏是否还能继续运行下去
voidHelloWorld::doCheckGameOver(){
inty=0;y<4;y++){
intx=0;x<4;x++){
if(cardArr[x][y]->getNumber()==0
||(x>0&&(cardArr[x][y]->getNumber()==cardArr[x-1][y]->getNumber()))
||(x<3&&(cardArr[x][y]->getNumber()==cardArr[x+1][y]->getNumber()))
||(y<0&&(cardArr[x][y]->getNumber()==cardArr[x][y-1]->getNumber()))
||(x<3&&(cardArr[x][y]->getNumber()==cardArr[x][y+1]->getNumber()))){
isGameOver=if(isGameOver){
//结束游戏
Director::getInstance()->replaceScene(TransitionFade::create(1,HelloWorld::createScene()));
//自动生成卡片
voidHelloWorld::autoCreateCardNumber(){
inti=CCRANDOM_0_1()*4;
intj=CCRANDOM_0_1()*4;
//判断是否已经存在的位置
if(cardArr[i][j]->getNumber()>0){
//已存在,递归创建
//生成2和4的比例是1:9的概率
cardArr[i][j]->setNumber(CCRANDOM_0_1()*10<1?4:2);
boolHelloWorld::onTouchBegan(cocos2d::Touch*touch,cocos2d::Event*unused_event){
PointtouchP0=touch->getLocation();
firstX=touchP0.x;
firstY=touchP0.y;
voidHelloWorld::onTouchEnded(cocos2d::Touch*touch,248)"> endX=firstX-touchP0.x;
endY=firstY-touchP0.y;
if(endX+5>0){
if(endY+5>0){
voidHelloWorld::menuCloseCallback(Ref*pSender)
#if(CC_TARGET_PLATFORM==CC_PLATFORM_WP8)||(CC_TARGET_PLATFORM==CC_PLATFORM_WINRT)
MessageBox("Youpressedtheclosebutton.WindowsStoreAppsdonotimplementaclosebutton.","Alert");
return;
#endif
Director::getInstance()->end();
#if(CC_TARGET_PLATFORM==CC_PLATFORM_IOS)
exit(0);
}
>>>CardSprite.h
最后,骚年们就不要向我要源码了,所有的代码在这里均有实现,只要把这些代码集成到你的项目中去就可以实现跟笔者一样的效果,希望各位能好好体会2048开发的流程。
FROM:http://blog.csdn.net/wwj_748/article/details/38168649
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。