网上找了好多教程写2048,不过都没有实现卡片的移动动画,自己写了一个不太完美的带动画版。
开发步骤:
1,设计一个CardSprite类。
2,设计主游戏场景GameScene,实现游戏逻辑,添加动画逻辑。
3,添加游戏胜利或者游戏失败的层,添加历史分数存储。
4,添加声音等其他元素,专门弄了一个声音预加载的场景,主场景添加声音切换变量存储。
贴上主场景关键代码:
GameScene.h
[cpp] view plain copy #pragmaonce #include"cocos2d.h" #include"cardSprite.h" #include"Menulayer.h" classGameScene:publiccocos2d::Layer { public: staticcocos2d::Scene*createScene(); virtualboolinit(); CREATE_FUNC(GameScene); //触摸监听 boolontouchBegan(cocos2d::touch*touch,cocos2d::Event*event);//注意这里要加命名空间作用域cocos2d virtualvoIDontouchended(cocos2d::touch*touch,cocos2d::Event*event); //上下左右滑动动作 boolmoveleft(); boolmoveRight(); boolmoveUp(); boolmoveDown(); //创建4*4卡片矩阵 voIDcreateCardArr(Sizesize); voIDrandomCreateCard(); //判断游戏赢输 voIDcheckGameWin(); voIDcheckGameOver(); voIDrestart(Ref*sender);//重新开始游戏菜单项 private: intscore;//当前分数 intbestscore;//最好分数 boolsound;//声音变量 cocos2d::LabelTTF*scoreLabel; LabelTTF*restartBtn;//重新开始的按钮 LabelTTF*isSoundBtn;//声音切换按钮 CardSprite*cardArr[4][4];//数字卡片矩阵 CardSprite*cardArrAction[4][4];//用于动画的临时数字卡片矩阵 PointstartPt;//触摸开始点 intoffsetX,offsetY;//触摸水平和竖直方向偏移量 Menulayer*menulayer;//菜单层 timevaltv;//当前时间 };
copy /* *game:2048 *author:tashaxing *time:2014/10/12 */ #include"GameScene.h" #include"SimpleAudioEngine.h" usingnamespacecocos2d; namespaceCocosDenshion; Scene*GameScene::createScene() { autoscene=Scene::create(); autolayer=GameScene::create(); scene->addChild(layer); returnscene; } boolGameScene::init() if(!Layer::init()) returnfalse; //获得屏幕尺寸和原点 SizevisibleSize=Director::getInstance()->getVisibleSize(); Vec2origin=Director::getInstance()->getVisibleOrigin(); //添加背景 autogameBkGround=LayerColor::create(Color4B(180,170,255)); this->addChild(gameBkGround); //添加标题 autotitle=LabelTTF::create("My2048","Arial",60); title->setColor(Color3B(255,153)); title->setPosition(Point(visibleSize.width/2,visibleSize.height-50)); this->addChild(title); //加入restart按钮 restartBtn=LabelTTF::create("Restart",40); restartBtn->setColor(Color3B(204,253)); restartBtn->setPosition(Point(visibleSize.width/2,visibleSize.height-110)); this->addChild(restartBtn); //添加声音切换按钮 //初始化获取最好分数和声音变量,第一次启动应用的话xml里没有任何值,所以下面的会返回0和false sound=UserDefault::getInstance()->getBoolForKey("SOUND"); if(sound) isSoundBtn=LabelTTF::create("SoundOn",153); font-weight:bold; background-color:inherit">else isSoundBtn=LabelTTF::create("SoundOff",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> isSoundBtn->setColor(Color3B(204,248)"> isSoundBtn->setPosition(Point(visibleSize.width/2,50)); this->addChild(isSoundBtn); //加入游戏分数 autoslabel=LabelTTF::create("Score",30); slabel->setPosition(Point(visibleSize.width/5,visibleSize.height-150)); this->addChild(slabel); score=0; scoreLabel=LabelTTF::create("0",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> scoreLabel->setColor(Color3B(0,37)); scoreLabel->setPosition(Point(visibleSize.width/2+30,visibleSize.height-150)); this->addChild(scoreLabel); bestScore=UserDefault::getInstance()->getIntegerForKey("BEST"); //初始化卡片 createCardArr(visibleSize); randomCreateCard(); randomCreateCard(); //添加触摸监听 autolistener=EventListenerTouchOneByOne::create(); listener->onTouchBegan=CC_CALLBACK_2(GameScene::onTouchBegan,this); listener->onTouchEnded=CC_CALLBACK_2(GameScene::onTouchEnded,153); font-weight:bold; background-color:inherit">this); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener,153); font-weight:bold; background-color:inherit">true; voidGameScene::restart(Ref*sender) //转场,重新开始游戏 Director::getInstance()->replaceScene(TransitionFade::create(0.7f,GameScene::createScene())); boolGameScene::onTouchBegan(Touch*touch,Event*event) gettimeofday(&tv,NULL);//记录当前时间 startPt=touch->getLocation();//保存开始触摸点 //判断如果触摸点在restart按钮区域内则重新开始 if(restartBtn->getBoundingBox().containsPoint(restartBtn->convertToNodeSpace(touch->getLocation()))) Director::getInstance()->replaceScene(TransitionFade::create(0.7f,GameScene::createScene())); //声音开关 if(isSoundBtn->getBoundingBox().containsPoint(isSoundBtn->convertToNodeSpace(touch->getLocation()))) sound=!sound; UserDefault::getInstance()->setBoolForKey("SOUND",sound); if(sound) isSoundBtn->setString("SoundOn"); else isSoundBtn->setString("SoundOff"); voidGameScene::onTouchEnded(Touch*touch,248)"> timevaltv_end; gettimeofday(&tv_end,NULL); if(tv_end.tv_sec-tv.tv_sec>3) //开个后门,用来测试游戏赢了 cardArr[0][3]->setNumber(2048); checkGameWin(); } autoendPt=touch->getLocation();//获得触摸结束点 offsetX=endPt.x-startPt.x;//计算偏移 offsetY=endPt.y-startPt.y; boolisTouch=false; if(abs(offsetX)>abs(offsetY))//判断为方向 if(offsetX<-5) isTouch=moveLeft(); elseif(offsetX>5) isTouch=moveRight(); if(offsetY>5)//注意这里的纵向坐标别弄反 isTouch=moveDown(); if(offsetY<-5) isTouch=moveUp(); if(isTouch)//如果滑动成功则判断 scoreLabel->setString(String::createWithFormat("%d",score)->getCString()); //这三个的顺序不能乱 checkGameWin(); checkGameOver(); voidGameScene::createCardArr(Sizesize) intspace=5;//卡片间的间隔 intcardSize=(size.width-4*space)/4; //创建卡片矩阵 for(inti=0;i<4;i++) intj=0;j<4;j++) //最左边留白12,最下面留白size.height/6 //坐标从左下角算起,右为正,上为正 CardSprite*card=CardSprite::createCard(0,cardSize,cardSize*i+12,cardSize*j+12+size.height/6); this->addChild(card);//一定要把card添加到子节点才能渲染出来 cardArr[i][j]=card;//存到卡片矩阵 //创建临时卡片矩阵,用于动画,每个动画卡片对应一个实际卡片的动画,这是个技巧,并且动画层在卡片层之上,所以后加入,也可以设置addchild层次 inti=0;i<4;i++) intj=0;j<4;j++) //最左边留白12,最下面留白size.height/6 this->addChild(card); cardArrAction[i][j]=card; //一开始把这层全部因此 autohide=Hide::create(); cardArrAction[i][j]->getCardLayer()->runAction(hide); voidGameScene::randomCreateCard() //在随机位置生成卡片 introw=CCRANDOM_0_1()*4; intcol=CCRANDOM_0_1()*4; if(cardArr[row][col]->getNumber()>0)//如果有数字,则递归调用 cardArr[row][col]->setNumber(CCRANDOM_0_1()*10<1?4:2);//有10%的几率生成4 //用动画效果生成 autoaction=Sequence::createWithTwoActions(ScaleTo::create(0,0),ScaleTo::create(0.3f,1));//在0.3秒内从小缩放到大 cardArr[row][col]->getCardLayer()->runAction(action);//用卡片的层而不是卡片精灵本身做动作是为了使用局部坐标缩放 //向左滑动游戏逻辑,其他方向类似 boolGameScene::moveLeft() //是否有移动的逻辑变量,如果没有任何移动,则不需要随机生成卡片,也不检验赢输,这一点很关键,否则很容易出bug boolmoved=//计算移动的步进间距 autocardSize=(Director::getInstance()->getVisibleSize().width-5*4)/4; //y表示行标号,x表示列标号 inty=0;y<4;y++)//最外层的行遍历可以先不管 intx=0;x<4;x++)//内部的N^2复杂度的类似冒泡排序 intx1=x+1;x1<4;x1++) if(cardArr[x1][y]->getNumber()>0)//x右边的卡片有数字才动作 if(cardArr[x][y]->getNumber()==0) //专门弄一个动画层卡片实现定位、显现、移动、隐藏系列动画 autoplace=Place::create(Point(cardSize*x1+12,cardSize*y+12+Director::getInstance()->getVisibleSize().height/6)); cardArrAction[x1][y]->setNumber(cardArr[x1][y]->getNumber());//每次都重新把动画卡片重新定位到实际对应的卡片位置,并设置相同的数字 autoshow=Show::create(); automove=MoveBy::create(0.1f,Point(-cardSize*(x1-x),0));//注意移动的距离 autohide=Hide::create(); cardArrAction[x1][y]->getCardLayer()->runAction(Sequence::create(place,show,move,hide,NULL)); //如果x位置是空卡片,就把x1卡片移到x处,x1处变成空卡片 cardArr[x][y]->setNumber(cardArr[x1][y]->getNumber()); cardArr[x1][y]->setNumber(0); x--;//再扫描一遍,确保所有结果正确 moved=true; if(cardArr[x][y]->getNumber()==cardArr[x1][y]->getNumber()) cardArrAction[x1][y]->setNumber(cardArr[x1][y]->getNumber()); //如果x位置非空,且与x1处数字相同,则乘2 cardArr[x][y]->setNumber(cardArr[x][y]->getNumber()*2); cardArr[x1][y]->setNumber(0); //数字合并动画 automerge=Sequence::create(ScaleTo::create(0.1f,1.2f),ScaleTo::create(0.1f,1.0f),NULL); cardArr[x][y]->getCardLayer()->runAction(merge); score+=cardArr[x][y]->getNumber(); //播放得分声音 SimpleAudioEngine::getInstance()->playEffect("get.mp3"); moved=break;//此处break防止出现连续乘2的bug returnmoved; boolGameScene::moveRight() intx=3;x>=0;x--)intx1=x-1;x1>=0;x1--) //x左边的卡片有数字才动作 autoplace=Place::create(Point(cardSize*x1+12,cardSize*y+12+Director::getInstance()->getVisibleSize().height/6)); cardArrAction[x1][y]->setNumber(cardArr[x1][y]->getNumber()); autoshow=Show::create(); automove=MoveBy::create(0.1f,0); background-color:inherit">//注意移动的距离 cardArrAction[x1][y]->getCardLayer()->runAction(Sequence::create(place,NULL)); x++; //注意移动的距离,此处算出来为正 automerge=Sequence::create(ScaleTo::create(0.1f,248)"> cardArr[x][y]->getCardLayer()->runAction(merge); score+=cardArr[x][y]->getNumber(); SimpleAudioEngine::getInstance()->playEffect("get.mp3"); boolGameScene::moveUp()//这里的“上”是逻辑上往坐标值小的方向,在屏幕上实际是往下动 //最外层的列遍历可以先不管 inty1=y+1;y1<4;y1++) if(cardArr[x][y1]->getNumber()>0)//x下边的卡片有数字才动作 autoplace=Place::create(Point(cardSize*x+12,cardSize*y1+12+Director::getInstance()->getVisibleSize().height/6)); cardArrAction[x][y1]->setNumber(cardArr[x][y1]->getNumber()); cardArrAction[x][y1]->getCardLayer()->runAction(Sequence::create(place,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> cardArr[x][y]->setNumber(cardArr[x][y1]->getNumber()); cardArr[x][y1]->setNumber(0); y--; if(cardArr[x][y]->getNumber()==cardArr[x][y1]->getNumber()) autoplace=Place::create(Point(cardSize*x+12,cardSize*y1+12+Director::getInstance()->getVisibleSize().height/6)); cardArrAction[x][y1]->setNumber(cardArr[x][y1]->getNumber()); cardArrAction[x][y1]->getCardLayer()->runAction(Sequence::create(place,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> cardArr[x][y1]->setNumber(0); boolGameScene::moveDown()//这里的“下”是逻辑上往坐标值小的方向,在屏幕上实际是往上动 inty=3;y>=0;y--)inty1=y-1;y1>=0;y1--) //x上边的卡片有数字才动作 y++; voidGameScene::checkGameWin() boolisWin=if(2048==cardArr[i][j]->getNumber()) isWin=if(isWin) //播放音效 SimpleAudioEngine::getInstance()->playEffect("gamewin.mp3"); //有一个2048游戏就是赢了 /*初始化菜单层*/ menuLayer=MenuLayer::create(Color4B(0,100)); this->addChild(menuLayer); automenuSize=menuLayer->getContentSize(); automenuTitle=LabelTTF::create("YOUWIN",30); menuTitle->setPosition(menuSize.width/2,menuSize.height/2+50); menuLayer->addChild(menuTitle); //添加当前分数 automenuscoreLabel=LabelTTF::create(String::createWithFormat("current:%d",score)->getCString(),20); menuscoreLabel->setPosition(menuSize.width/2,menuSize.height/2); menuLayer->addChild(menuscoreLabel); //添加最好分数 bestScore=UserDefault::getInstance()->getIntegerForKey("BEST"); if(score>bestScore) bestScore=score; UserDefault::getInstance()->setIntegerForKey("BEST",bestScore); automenuBestscoreLabel=LabelTTF::create(String::createWithFormat("best:%d",bestScore)->getCString(),248)"> menuBestscoreLabel->setPosition(menuSize.width/2,menuSize.height/2-30); menuLayer->addChild(menuBestscoreLabel); MenuItemFont::setFontName("Arial"); MenuItemFont::setFontSize(25); automenuItemRestart=MenuItemFont::create("Restart",this,menu_selector(GameScene::restart)); menuItemRestart->setColor(Color3B(255,0)); automenu=Menu::create(menuItemRestart,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> menuLayer->addChild(menu); menu->setPosition(Point(menuSize.width/2,menuSize.height/2-80)); voidGameScene::checkGameOver() boolisGameOver=//以下情况则游戏继续 if((cardArr[i][j]->getNumber()==0)|| (i>0&&cardArr[i][j]->getNumber()==cardArr[i-1][j]->getNumber())|| (i<3&&cardArr[i][j]->getNumber()==cardArr[i+1][j]->getNumber())|| (j>0&&cardArr[i][j]->getNumber()==cardArr[i][j-1]->getNumber())|| (j<3&&cardArr[i][j]->getNumber()==cardArr[i][j+1]->getNumber())) isGameOver=//否则游戏结束 if(isGameOver) SimpleAudioEngine::getInstance()->playEffect("gameover.mp3"); /*初始化菜单层*/ menuLayer=MenuLayer::create(Color4B(0,100)); this->addChild(menuLayer); automenuSize=menuLayer->getContentSize(); //添加标题 automenuTitle=LabelTTF::create("GAMEOVER",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> menuTitle->setPosition(menuSize.width/2,menuSize.height/2+50); menuLayer->addChild(menuTitle); //添加当前分数 automenuscoreLabel=LabelTTF::create(String::createWithFormat("current:%d",20); menuscoreLabel->setPosition(menuSize.width/2,menuSize.height/2); menuLayer->addChild(menuscoreLabel); //添加最好分数 if(score>bestScore) bestScore=score; UserDefault::getInstance()->setIntegerForKey("BEST",bestScore); automenuBestscoreLabel=LabelTTF::create(String::createWithFormat("best:%d",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> menuBestscoreLabel->setPosition(menuSize.width/2,menuSize.height/2-30); menuLayer->addChild(menuBestscoreLabel); MenuItemFont::setFontName("Arial"); MenuItemFont::setFontSize(25); automenuItemRestart=MenuItemFont::create("Restart",menu_selector(GameScene::restart)); menuItemRestart->setColor(Color3B(255,0)); automenu=Menu::create(menuItemRestart,248)"> menuLayer->addChild(menu); menu->setPosition(Point(menuSize.width/2,menuSize.height/2-80)); }
关键点:
1,专门弄了一个4*4的卡片临时矩阵做为动画层,也就是有16个卡片专门负责对应卡片的动画(卡片生成的动画是实际卡片,这个是例外)。
2,关于游戏逻辑中每次移动后面都有一个break
如果不加这个break就会出现:
本来是 2 2 4 2
向左滑动应该是 4 4 2 0
结果是: 8 2 0 0 加上break就正常了。
游戏截图:
源代码
csdn下载:2048源码
github下载:2048源码
FROM:http://blog.csdn.net/u012234115/article/details/40061303?utm_source=tuicool&utm_medium=referral
总结以上是内存溢出为你收集整理的cocos2dx实例开发之2048(添加动画版)全部内容,希望文章能够帮你解决cocos2dx实例开发之2048(添加动画版)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)