加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

cocos2dx实例开发之2048(添加动画版)

发布时间:2020-12-14 16:57:10 所属栏目:百科 来源:网络整理
导读:网上找了好多教程写2048,不过都没有实现卡片的移动动画,自己写了一个不太完美的带动画版。 开发步骤: 1,设计一个CardSprite类。 2,设计主游戏场景GameScene,实现游戏逻辑,添加动画逻辑。 3,添加游戏胜利或者游戏失败的层,添加历史分数存储。 4,添

网上找了好多教程写2048,不过都没有实现卡片的移动动画,自己写了一个不太完美的带动画版。


开发步骤:

1,设计一个CardSprite类。

2,设计主游戏场景GameScene,实现游戏逻辑,添加动画逻辑。

3,添加游戏胜利或者游戏失败的层,添加历史分数存储。

4,添加声音等其他元素,专门弄了一个声音预加载的场景,主场景添加声音切换变量存储。


贴上主场景关键代码:

GameScene.h

[cpp] view plain copy
  1. #pragmaonce
  2. #include"cocos2d.h"
  3. #include"cardSprite.h"
  4. #include"MenuLayer.h"
  5. classGameScene:publiccocos2d::Layer
  6. {
  7. public:
  8. staticcocos2d::Scene*createScene();
  9. virtualboolinit();
  10. CREATE_FUNC(GameScene);
  11. //触摸监听
  12. boolonTouchBegan(cocos2d::Touch*touch,cocos2d::Event*event);//注意这里要加命名空间作用域cocos2d
  13. virtualvoidonTouchEnded(cocos2d::Touch*touch,cocos2d::Event*event);
  14. //上下左右滑动动作
  15. boolmoveLeft();
  16. boolmoveRight();
  17. boolmoveUp();
  18. boolmoveDown();
  19. //创建4*4卡片矩阵
  20. voidcreateCardArr(Sizesize);
  21. voidrandomCreateCard();
  22. //判断游戏赢输
  23. voidcheckGameWin();
  24. voidcheckGameOver();
  25. voidrestart(Ref*sender);//重新开始游戏菜单项
  26. private:
  27. intscore;//当前分数
  28. intbestScore;//最好分数
  29. boolsound;//声音变量
  30. cocos2d::LabelTTF*scoreLabel;
  31. LabelTTF*restartBtn;//重新开始的按钮
  32. LabelTTF*isSoundBtn;//声音切换按钮
  33. CardSprite*cardArr[4][4];//数字卡片矩阵
  34. CardSprite*cardArrAction[4][4];//用于动画的临时数字卡片矩阵
  35. PointstartPt;//触摸开始点
  36. intoffsetX,offsetY;//触摸水平和竖直方向偏移量
  37. MenuLayer*menuLayer;//菜单层
  38. timevaltv;//当前时间
  39. };


GameScene.cpp

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

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

      推荐文章
        热点阅读