cocos2d-x 3.1 flappy bird 简单学习制作

原文地址http://www.is17.com/p181.html

学习cocos2dx3.1中,网上教程不算特别多,参照各种教程,慢慢总结编写,跟引用了一些优秀类,总算做出来了个超简单的像素鸟游戏。
现在讲解一下核心内容跟,我遇到的一些问题的处理方法。

一、新建项目:
其实网上各种各样的环境配置眼花缭乱的也不一定配置成功,我们要做的仅仅是建一个可以显示helloworld的东西。
我这里有个超简单的方法。
原材料:vs2012,cocos2dx3.1.1 没错,只需要这些。
打开某英文(中文将导致编译失败)目录下的D:\cocos2d-x-3.1.1\build\cocos2d-win32.vc2012.sln
右键解决方案,生成解决方案,等十几分钟吧大概。i3i5表示都是100%cpu,卡的一比。
完了之后,右键cpp-empty-test->设为启动项目。---->F5。不出意外的话出现了helloworld,这么简单都失败的话请百度。
我们的新项目就是改这个东西,我们仅仅是学习,现在不用看那些各种Python的东西来生成新项目====。

二、分析游戏:
其实我写的时候是俩眼一抹黑,先做核心,然后在添加各种功能,然后代码各种改,对于一个没有学过c++的人,各种问题各种出。
真正做东西我们要理清思路,设计类什么的更好一些。思路清晰方能事半功倍。
小鸟,背景,水管,地面各做一个类,我写的仓促,地面并没有写成类。
小鸟类核心代码:Player

  1. boolPlayer::init()
  2. {
  3. //当时为了显高端引用了别人的一个大图类,所有资源打包的那种,其实并不好用,而且我并不知道他的包怎么打,但是思路挺好,真正的官方用法是引用cocostudio打包
  4. playerSp=Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("bird0_0"));
  5. //注释掉这一句跟上句相同效果。
  6. //playerSp=Sprite::create("bird0_0.png");
  7. this->addChild(playerSp);
  8. //小鸟飞行动画
  9. autoanimation=Animation::create();
  10. charszName[100]={0};
  11. //将小鸟的三张图片做成动画,
  12. for(inti=0;i<3;i++)
  13. {
  14. sprintf(szName,"bird0_%d.png",i);
  15. animation->addSpriteFrameWithFile(szName);
  16. }
  17. animation->setDelayPerUnit(1.8f/14.0f);
  18. autoaction=Animate::create(animation);
  19. //小鸟循环飞行
  20. playerSp->runAction(RepeatForever::create(action));
  21. //添加刚体,这个类中小鸟自动附加到刚体上
  22. autobody=PhysicsBody::createCircle(playerSp->getContentSize().width*0.3f);
  23. body->getShape(0)->setFriction(0);
  24. body->getShape(0)->setRestitution(1.0f);
  25. //以下三行设定可以碰撞,具体参数不懂,反正这样就触发碰撞了。
  26. body->setCategoryBitmask(1);//0001
  27. body->setCollisionBitmask(1);//0001
  28. body->setContactTestBitmask(1);this->setPhysicsBody(body);
  29. returntrue;
  30. //小鸟死亡,仅作了停止播放动画
  31. voidPlayer::die(){
  32. playerSp->stopAllActions();
  33. }

背景类:bggroundlayer

copy
    SizevisibleSize=Director::getInstance()->getVisibleSize();
  1. /*生成两张图片放在0和游戏宽度位置*/
  2. m_bg1=Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("bg_day"));
  3. m_bg1->setPosition(Point(0,0));
  4. m_bg1->setAnchorPoint(Point::ZERO);
  5. //设置抗锯齿修正拼图缝隙(这个很有用不加会有一个显示bug可自己测试)
  6. m_bg1->getTexture()->setAliasTexParameters();
  7. this->addChild(m_bg1);
  8. m_bg2=Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("bg_day"));
  9. m_bg2->setPosition(Point(visibleSize.width,248)"> m_bg2->setAnchorPoint(Point::ZERO);
  10. m_bg1->getTexture()->setAliasTexParameters();
  11. this->addChild(m_bg2);
  12. //这个是根据帧频率触发的一个公共方法,来实现地图的横向滚动
  13. voidBackgroundLayer::logic(floatdt)
  14. intposX1=m_bg1->getPositionX();//背景地图1的X坐标
  15. intposX2=m_bg2->getPositionX();//背景地图2的X坐标
  16. intiSpeed=1;//地图滚动速度,其实这个可以提出来的
  17. /*两张地图向上滚动(两张地图是相邻的,所以要一起滚动,否则会出现空隙)*/
  18. posX1-=iSpeed;
  19. posX2-=iSpeed;
  20. /*屏幕宽*/
  21. intiVisibleWidth=Director::getInstance()->getVisibleSize().width;
  22. /*当第1个地图完全离开屏幕时,让第2个地图完全出现在屏幕上,同时让第1个地图紧贴在第2个地图后面*/
  23. if(posX1<-iVisibleWidth){
  24. posX2=0;
  25. posX1=iVisibleWidth;
  26. }
  27. /*同理,当第2个地图完全离开屏幕时,让第1个地图完全出现在屏幕上,同时让第2个地图紧贴在第1个地图后面*/
  28. if(posX2<-iVisibleWidth){
  29. posX1=0;
  30. posX2=iVisibleWidth;
  31. m_bg1->setPositionX(posX1);
  32. m_bg2->setPositionX(posX2);
  33. }

管道类:Conduit

写这个遇到了很大问题,我并没有接触过cocos,只是找例子来参考做,使用了跟小鸟一样的生成方法,要么只能生成一个刚体,要么生成之后变成了类似于单例的样子,无法生成多个。后来终于看到了这个方法解决了问题。

copy
    intiVisibleWidth=Director::getInstance()->getVisibleSize().width;
  1. intjianHeight=100;
  2. autopipeUpSp=Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("pipe_up"));
  3. pipeUpSp->setPosition(Point(0,0));
  4. this->addChild(pipeUpSp);
  5. autopipeDownSp=Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("pipe_down"));
  6. pipeDownSp->setPosition(Point(0,pipeUpSp->getContentSize().height+jianHeight));
  7. this->addChild(pipeDownSp);
  8. //添加刚体
  9. autobody=PhysicsBody::create();
  10. body->setDynamic(false);
  11. //我们生成了一个刚体,向这里放入两个元素。这样我们就生成了一堆可操作的管道了。
  12. body->addShape(PhysicsShapeBox::create(pipeUpSp->getContentSize(),PHYSICSSHAPE_MATERIAL_DEFAULT,Point(0,0)));
  13. body->addShape(PhysicsShapeBox::create(pipeDownSp->getContentSize(),pipeUpSp->getContentSize().height+jianHeight)));
  14. this->setPhysicsBody(body);

地面我并没有写到类里边,加载地面也并没有新技术,就是创建一个Sprite,一个Body。其移动方法如下

copy
    ground->setPositionX(ground->getPositionX()-1);
  1. if(ground->getPositionX()<ground->getContentSize().width/2-24){
  2. ground->setPositionX(ground->getContentSize().width/2-1);
  3. 然后我们将这些东西添加到场景类中,在这里之前我们要修改自带的类,使之能跳转到我们的场景类。

    我们修改AppDeleGate.cpp-->applicationDidFinishLaunching使其加载loadscene,

    copy
      director->setOpenGLView(glview);
    1. *设置Win32屏幕大小为288X512,*/
    2. glview->setFrameSize(288,512);
    3. /*简单的屏幕适配,按比例拉伸,可能有黑边*/
    4. glview->setDesignResolutionSize(288,512,ResolutionPolicy::SHOW_ALL);
    5. //turnondisplayFPS
    6. director->setDisplayStats(true);
    7. //setFPS.thedefaultvalueis1.0/60ifyoudon'tcallthis
    8. director->setAnimationInterval(1.0/60);
    9. //这里添加load类
    10. //autoscene=TollgateScene::scene();
    11. utoscene=LoadingScene::create();
    12. //run
    13. director->runWithScene(scene);

    loadscene类:loadscene中实现了一个开头切换场景动画,也可用作实现load进度条之类。

    copy
      voidLoadingScene::onEnter(){
    1. //addbackgroundtocurrentscene
    2. autofileUtils=FileUtils::getInstance();
    3. std::vector<std::string>searchPaths;
    4. searchPaths.push_back("image");
    5. fileUtils->setSearchPaths(searchPaths);
    6. Sprite*background=Sprite::create("splash.png");
    7. SizevisibleSize=Director::getInstance()->getVisibleSize();
    8. Pointorigin=Director::getInstance()->getVisibleOrigin();
    9. background->setPosition(origin.x+visibleSize.width/2,origin.y+visibleSize.height/2);
    10. this->addChild(background);
    11. //加载1024的一张大图
    12. Director::getInstance()->getTextureCache()->addImageAsync("atlas.png",CC_CALLBACK_1(LoadingScene::loadingCallBack,this));
    13. voidLoadingScene::loadingCallBack(Texture2D*texture){
    14. //配合atlastxt
    15. AtlasLoader::getInstance()->loadAtlas("atlas.txt",texture);
    16. //切换场景,TollgateScene便是我们的主要游戏场景类
    17. autoscene=TollgateScene::scene();
    18. TransitionScene*transition=TransitionFade::create(1,scene);
    19. Director::getInstance()->replaceScene(transition);
    20. 然后就是最重要的TollgateScene类了,这里我们实现了游戏逻辑。
      首先我们添加重力环境,当时我顺手把背景也写在了这里边,背景不用重复加载也就没改。

      copy
        Scene*TollgateScene::scene()
      1. autoscene=Scene::createWithPhysics();
      2. /**设置重力*/
      3. Vectgravity(0,-300.1f);
      4. scene->getPhysicsWorld()->setGravity(gravity);
      5. /*开启测试模式这将会给所有刚体加一个红框*/
      6. //scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
      7. autobackgroundLayer=BackgroundLayer::create();
      8. //addChild方法添加到场景,第二个参数是层级,越大层级越高越靠前
      9. scene->addChild(backgroundLayer,0);
      10. autolayer=TollgateScene::create();
      11. scene->addChild(layer,10);
      12. layer->m_backgroundLayer=backgroundLayer;
      13. returnscene;
      14. [/cc]
      15. 然后我们在init函数里边添加一些初始显示且不怎么变动的东西。
      16. [cclang="c++"]
      17. boolTollgateScene::init()
      18. if(!Layer::init())
      19. false;
      20. //场景尺寸
      21. visibleSize=Director::getInstance()->getVisibleSize();
      22. //初始化一个个性字体类
      23. Number::getInstance()->loadNumber(NUMBER_FONT.c_str(),"font_0%02d",48);
      24. //添加开始按钮
      25. this->initGame();
      26. //添加地面
      27. this->setGround();
      28. /**添加开始按钮按钮点击会触发游戏开始事件。*/
      29. voidTollgateScene::initGame(){
      30. autocloseItem=MenuItemImage::create("play_0.png","play_1.png",CC_CALLBACK_1(TollgateScene::startGame,248)"> closeItem->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
      31. //createmenu,it'sanautoreleaseobject
      32. menu=Menu::create(closeItem,NULL);
      33. menu->setPosition(Vec2::ZERO);
      34. this->addChild(menu,1);
      35. 最最重点的游戏开始了:(其实并不复杂,我们设定了初始等级,隐藏开始按钮,添加小鸟与管道,添加随帧事件,添加碰撞事件,逻辑很清晰)

        copy
          voidTollgateScene::startGame(Ref*sender){
        1. level=0;
        2. showLevel(level);
        3. menu->setVisible(false);
        4. this->setConduit();
        5. this->setBird();
        6. this->schedule(schedule_selector(TollgateScene::logic));
        7. //监听事件
        8. autolistener=EventListenerTouchOneByOne::create();
        9. listener->setSwallowTouches(true);
        10. listener->onTouchBegan=CC_CALLBACK_2(TollgateScene::onTouchBegan,this);
        11. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener,153); font-weight:bold; background-color:inherit">this);
        12. autocontactListener=EventListenerPhysicsContact::create();
        13. contactListener->onContactBegin=CC_CALLBACK_1(TollgateScene::onContactBegin,248)"> _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener,0); background-color:inherit">//加载小鸟
        14. voidTollgateScene::setBird(){
        15. player=Player::create();
        16. player->setPosition(Point(visibleSize.width/2,visibleSize.height/2+100));
        17. this->addChild(player,5);
        18. //加载管道,我们把管道添加到一个数组里边,以遍来播放管道动画,其实我们只用到了两组管道来重复播放。
        19. voidTollgateScene::setConduit(){
        20. inti=0;i<2;i++){
        21. conduit=Conduit::create();
        22. conduit->setPosition(Point(visibleSize.width+i*PIP_INTERVAL+PIP_WIDTH,153); font-weight:bold; background-color:inherit">this->getRandomHeight()));
        23. this->addChild(conduit,4);
        24. this->pips.push_back(conduit);
        25. //给管道一个随机高度
        26. intTollgateScene::getRandomHeight(){
        27. SizevisibleSize=Director::getInstance()->getVisibleSize();
        28. returnrand()%(int)(2*PIP_HEIGHT+PIP_DISTANCE-visibleSize.height);
        29. //点击屏幕简单的给小鸟添加一个向上的力量
        30. boolTollgateScene::onTouchBegan(Touch*touch,Event*event){
        31. player->getPhysicsBody()->setVelocity(Vect(0,180));
        32. //碰撞触发游戏结束事件,显示按钮,游戏结束,小鸟死亡。(好吧这个函数里并没写)
        33. boolTollgateScene::onContactBegin(constPhysicsContact&contact){
        34. menu->setVisible(this->gameOver();
        35. //游戏结束停止帧事件,结束侦听事件,清空数组,移除显示元素。
        36. voidTollgateScene::gameOver(){
        37. this->unschedule(schedule_selector(TollgateScene::logic));
        38. _eventDispatcher->removeAllEventListeners();
        39. for(autosinglePip:this->pips){
        40. this->removeChild(singlePip);
        41. pips.clear();
        42. player->die();
        43. this->removeChild(player);
        44. this->removeChild(menu);
        45. this->removeChild(scoreSprite);
        46. this->initGame();
        47. 还有一些动态的东西,并不多:

          copy
            [cclang="c++"]
          1. //背景移动
          2. voidTollgateScene::logic(floatdt)
          3. {//调用背景移动
          4. m_backgroundLayer->logic(dt);
          5. //这里好像介绍过了
          6. ground->setPositionX(ground->getPositionX()-1);
          7. //判断过关,当管道x<小鸟x
          8. singlePip->setPositionX(singlePip->getPositionX()-1);
          9. if(singlePip->getPositionX()==visibleSize.width/2-PIP_WIDTH){
          10. level++;
          11. //当管道从这个移出屏幕了再让他从另一边进来。
          12. if(singlePip->getPositionX()<-PIP_WIDTH){
          13. singlePip->setPositionX(visibleSize.width+PIP_WIDTH/2);
          14. singlePip->setPositionY(this->getRandomHeight());
          15. //显示关卡等级Number类会根据数字返回一个精灵,也就是图片版的数字,比较好看而已。当过关了就刷一下。
          16. voidTollgateScene::showLevel(intlev){
          17. this->removeChild(scoreSprite);
          18. scoreSprite=(Sprite*)Number::getInstance()->convert(NUMBER_FONT.c_str(),lev);
          19. scoreSprite->setPosition(Point(visibleSize.width/2-scoreSprite->getContentSize().width/2,visibleSize.height*7/8));
          20. this->addChild(scoreSprite,20);
          21. 讲解跟流程写完了,可能还遗漏了一些东西大致流程就是这样了,请忽略排版,我的编辑器是最原始的。

            最最最后,附上源码,跟素材。(好吧这才是最重要的)

            ----------------------------------------

            源码

            --------------------------------

            资源

            --------------------------------

            相关文章

                本文实践自 RayWenderlich、Ali Hafizji 的文章《...
            Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
            第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
                Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
            1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
               Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...