Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)

来源:互联网 发布:c语言视频下载 编辑:程序博客网 时间:2024/05/14 10:35
cocos2dx3.0vs2013游戏开发引擎开源项目

懒骨头(http://blog.csdn.net/iamlazybone QQ:124774397 青岛)


笔记分好几次完成,可能有的地方转载的不全,请 传送到这里

《Flappy Bird》


关于这个游戏骨头不多说了

直接开始学习吧(山寨不好听)

正好前段时间看了几个DEMO拿这个游戏练练手

开搞!


报环境:

vs2013+cocos2dx3.0beta2

首先下载apk,找到资源文件,裁剪几个图片:


用脚本新建一个空的Cocos2dx项目

新建一个Scene类

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include "cocos2d.h"  
  2. #include "Obstacle.h"  
  3.   
  4. class FlyBirdGame :public cocos2d::Layer  
  5. {  
  6. public:  
  7.     static cocos2d::Scene* createScene();  
  8.     virtual bool init();  
  9.     CREATE_FUNC(FlyBirdGame);  
  10.     void initUI();  
  11.     void gameStart(Object* pSender);   
  12.     void update(float time);  
  13.     Obstacle* obstacle;  
  14. };  

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include "cocos2d.h"  
  2. #include "FlyBirdGame.h"  
  3. #include "resource.h";   
  4.   
  5. USING_NS_CC;  
  6.   
  7. Scene* FlyBirdGame::createScene()  
  8. {  
  9.     auto scene = Scene::create();  
  10.     auto layer = FlyBirdGame::create();  
  11.     scene->addChild(layer);  
  12.     return scene;  
  13. }  
  14.   
  15. bool FlyBirdGame::init()  
  16. {  
  17.     if (!Layer::init())  
  18.     {  
  19.         return false;  
  20.     }  
  21.     initUI();  
  22.     return true;  
  23. }  

initUI里是一些UI初始化方法:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1.        // win size  
  2. auto winSize = Director::getInstance()->getVisibleSize();  
  3.   
  4. // game bg  
  5. auto bg = Sprite::create(bird_bg);  
  6. bg->setPosition(winSize.width / 2, winSize.height / 2);  
  7. bg->setScale(winSize.width / bg->getContentSize().width, winSize.height / bg->getContentSize().height);  
  8. this->addChild(bg);  
  9.   
  10. // start btn  
  11. auto startBtn = MenuItemImage::create(bird_start_btn, bird_start_btn_pressed, CC_CALLBACK_1(FlyBirdGame::gameStart, this));  
  12. auto menu = Menu::create(startBtn, NULL);  
  13. menu->setTag(100);  
  14. this->addChild(menu);  
  15.   
  16. // hero  
  17. auto hero = Sprite::create(bird_hero);  
  18. hero->setPosition(winSize.width / 5, winSize.height*0.8);  
  19. hero->setVisible(false);  
  20. hero->setTag(200);  
  21. this->addChild(hero);  

开始游戏按钮绑定的gameStart方法:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void FlyBirdGame::gameStart(Object* pSender)  
  2. {  
  3.     auto btn = this->getChildByTag(100);  
  4.     btn->setVisible(false);  
  5.     auto hero = this->getChildByTag(200);   
  6.     Size win = Director::getInstance()->getWinSize();  
  7.     obstacle->gameStart = true;  
  8. }  

隐藏开始按钮,显示小鸟,水管开始移动

还有更新方法:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. scheduleUpdate();  
  2. void FlyBirdGame::update(float time)  
  3. {  
  4.     obstacle->update();  
  5. }  

=======================================

水管类:Obstacle.cpp

update方法里判断游戏是游戏是否开始


[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void Obstacle::update()  
  2. {  
  3.     if (gameStart == false)  
  4.         return;  
  5.     addCount++;  
  6.     if (addCount == 60)  
  7.     {  
  8.         addOne(0);  
  9.         addCount = 0;  
  10.     }  
  11.     for (int i = obstacleList->count() - 1; i >= 0; i--)  
  12.     {  
  13.         auto s = (Sprite*)obstacleList->getObjectAtIndex(i);  
  14.         s->setPositionX(s->getPositionX() - 3);  
  15.         if (s->getPositionX() < -s->getContentSize().width / 2)  
  16.         {  
  17.             obstacleList->removeObjectAtIndex(i);  
  18.             this->removeChild(s);  
  19.         }  
  20.     }  
  21. }  
水管类的更新方法里,每60帧(1秒)添加一对水管

并且遍历水管列表

出边界的化销毁

接下来是addOne方法:添加水管方法:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void Obstacle::addOne(int offsetX)  
  2. {  
  3.     Size size = Director::getInstance()->getWinSize();  
  4.     auto sprite = Sprite::create(bird_obstacle_up);  
  5.     Size spriteSize = sprite->getContentSize();  
  6.     obstacleList->addObject(sprite);  
  7.     this->addChild(sprite);  
  8.     auto sprite2 = Sprite::create(bird_obstacle_down);  
  9.     Size spriteSize2 = sprite->getContentSize();  
  10.     obstacleList->addObject(sprite2);  
  11.     this->addChild(sprite2);  
  12.     // set positon  
  13.     int maxUpY = size.height + spriteSize.height / 4;  
  14.     int minUpY = size.height - spriteSize.height / 4;  
  15.     int y1 = CCRANDOM_0_1()*(maxUpY - minUpY) + minUpY;  
  16.     int maxDownY = spriteSize.height / 4;  
  17.     int minDownY = -spriteSize.height / 4;  
  18.     int y2 = CCRANDOM_0_1()*(maxDownY - minDownY) + minDownY;  
  19.     if (y1 - y2 - spriteSize.height < 160)  
  20.     {  
  21.         y2 = y1 - spriteSize.height - 160;  
  22.     }  
  23.     sprite->setPosition(ccp(size.width + spriteSize.width / 2 + offsetX, y1));  
  24.     sprite2->setPosition(ccp(size.width + spriteSize2.width / 2 + offsetX, y2));  
  25. }  
这段代码比较凌乱,就是找到水管上下位置的范围

然后随机一下,并且保证上下连个水管有个最小的距离

效果如下:



=============================

此时的游戏还没触摸和碰撞逻辑

马上添加:(刚才抽空玩了把魔方:五阶的我只能搞定一个面,虽然有官方规律但是那样好像比的是记忆力)

听说cocos2dx3.0的事件监听方式改变了

先在FlyBirdGame.h里声明俩方法:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void onTouchesEnded(const vector<Touch*>& touches, Event* event);  
  2. void onTouchesBegan(const vector<Touch*>& touches, Event* event);  

在cpp文件的初始化里绑定事件:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // touch  
  2. auto dispatcher = Director::getInstance()->getEventDispatcher();  
  3. auto listener = EventListenerTouchAllAtOnce::create();  
  4. listener->onTouchesEnded = CC_CALLBACK_2(FlyBirdGame::onTouchesEnded, this);  
  5. listener->onTouchesBegan = CC_CALLBACK_2(FlyBirdGame::onTouchesBegan, this);  
  6. dispatcher->addEventListenerWithSceneGraphPriority(listener, this);  

在两个事件方法里改变标记位,在小鸟的update方法里根据这个标记位来改变高度

(哲哲喊我休息了,先到这吧,待续。。。)

=============================

碰撞检测:

记得cocos2dx3.0以前都是自己写一个根据CCSprite获取CCRect的方法

现在直接用 Sprite的getBoundingBox()方法

不过如果有特殊需求还得自己写,比如想扩大或缩小碰撞区域

在FlyBirdGame.cpp文件里

update算主逻辑方法:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void FlyBirdGame::update(float time)  
  2. {  
  3.     auto winSize = Director::getInstance()->getVisibleSize();  
  4.     auto hero = this->getChildByTag(TAG_HERO);  
  5.     Rect rHero = ((Sprite*)hero)->getBoundingBox();  
  6.   
  7.     switch (GAME_STATUS)  
  8.     {  
  9.     case GAME_STATUS_PLAYING:  
  10.         obstacle->update();  
  11.         // update bird positionY  
  12.         if (isFlying&&hero->getPositionY() < winSize.height)  
  13.         {  
  14.             hero->setPositionY(hero->getPositionY() + v);  
  15.         }  
  16.         else if (hero->getPositionY()>0)  
  17.         {  
  18.             hero->setPositionY(hero->getPositionY() - v);  
  19.         }  
  20.         //check collision  
  21.         for (int i = 0; i < obstacle->obstacleList->count(); i++)  
  22.         {  
  23.             Sprite* obstacleRect = (Sprite*)obstacle->obstacleList->getObjectAtIndex(i);  
  24.             bool pia = rHero.intersectsRect(obstacleRect->getBoundingBox());  
  25.             if (pia == true)  
  26.             {  
  27.                 GAME_STATUS = GAME_STATUS_GAME_OVER;  
  28.                 break;  
  29.             }  
  30.         }  
  31.         break;  
  32.     case GAME_STATUS_GAME_OVER:  
  33.         CCLog("over");  
  34.         this->getChildByTag(TAG_OVER)->setVisible(true);  
  35.         break;  
  36.     case GAME_STATUS_RESTART:  
  37.         //reset game  
  38. <span style="white-space: pre;">        </span>obstacle->removeAllChildren();  
  39.         obstacle->obstacleList->removeAllObjects();  
  40.         // reset hero  
  41.         hero->setPosition(winSize.width / 5, winSize.height*0.8);  
  42.         // show btn  
  43.         auto btn = this->getChildByTag(TAG_START_BTN);  
  44.         btn->setVisible(true);  
  45.         // show logo  
  46.         auto logo = this->getChildByTag(TAG_LOGO);  
  47.         logo->setVisible(true);  
  48.         break;  
  49.     }  
  50. }  

根据游戏状态处理。

碰撞检测方法:intersectsRect

bool pia = rHero.intersectsRect(obstacleRect->getBoundingBox());
if (pia == true)
{
GAME_STATUS = GAME_STATUS_GAME_OVER;
break;
}


游戏状态定义在 resource.h 文件里

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. static const int GAME_STATUS_START = 10;  
  2. static const int GAME_STATUS_PLAYING = 20;  
  3. static const int GAME_STATUS_GAME_OVER = 30;  
  4. static const int GAME_STATUS_RESTART = 40;  


整个游戏流程已经通了:开始,游戏,结束,重新开始

接下来可以优化很多很多东西

===================================================

真搞不懂为什么和弦要那么别扭的按法

相较之下

还是代码简单

继续优化

添加小鸟动画:


添加到resource.h 文件中等待使用

也可以直接在cpp文件中用不过骨头习惯了资源统一管理

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. Animation* an = Animation::create();  
  2. an->addSpriteFrameWithFileName(bird_hero);  
  3. an->addSpriteFrameWithFileName(bird_hero2);  
  4. an->addSpriteFrameWithFileName(bird_hero3);  
  5. an->setDelayPerUnit(0.5f / 3.0f);  
  6. an->setLoops(-1);  
  7. Animate* anim = Animate::create(an);  
  8. hero->runAction(anim);  

F5运行看效果,确实生动一点了

=========================================================

分辨率问题:

在AppDelegate.cpp入口方法中加入:

CCEGLView::sharedOpenGLView()->setDesignResolutionSize(320, 480, kResolutionExactFit);

其中320,480是游戏設計時的分辨率。

后面参数介绍如下“:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //kResolutionExactFit x,y都拉伸,使铺满屏幕  
  2. //kResolutionNoBorder 一个方向铺满屏幕,另外一个方向超出屏幕  
  3. //kResolutionShowAll  854 * 480 的设计区域全部可见,但是可能留有黑边(如在960 * 640的屏幕上)  
=============================================================

分数的显示:

之前的几个demo都是使用本办法显示的特殊数字:一排图片然后根据每位上的数字决定显示哪个图片

本来打算自己封装个方法,再一看,有更简单的:

从自带的demo里找点合适的字体资源,拷贝到fonts文件夹下

记住把fnt和对应的png都拷贝过来

然后

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. auto label1 = LabelBMFont::create("123456""fonts/futura-48.fnt");  
  2. addChild(label1);  
  3. label1->setPosition(Point(win.width / 2, win.height / 2));  


================================================

分数显示ok了,下面看下分数逻辑

当小鸟的坐标等于柱子的坐标时,score+1(因为是上下俩柱子对象,所以除以2显示)

别忘记判断时要让小鸟飞过柱子,而不是跟柱子纵轴对齐

相关代码在检测碰撞时添加:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //check collision  
  2. for (int i = 0; i < obstacle->obstacleList->count(); i++)  
  3. {  
  4.     Sprite* obstacleSprite = (Sprite*)obstacle->obstacleList->getObjectAtIndex(i);  
  5.     bool pia = rHero.intersectsRect(obstacleSprite->getBoundingBox());  
  6.     if (pia == true)  
  7.     {  
  8.         GAME_STATUS = GAME_STATUS_GAME_OVER;  
  9.         break;  
  10.     }  
  11.     int oPosX = obstacleSprite->getPositionX() + obstacleSprite->getContentSize().width / 2;  
  12.     int heroX = hero->getPositionX() - hero->getContentSize().width;  
  13.     if (oPosX == heroX)  
  14.     {  
  15.         score++;  
  16.         auto scoreSprite = (LabelBMFont*)this->getChildByTag(TAG_SCORE);  
  17.         String* s = String::createWithFormat("%d", score / 2);  
  18.         scoreSprite->setString(s->getCString());  
  19.     }  
  20. }  


======================================================

增加下游戏难度

之所以死不了是因为小鸟的速度改变的很均匀

因为触摸事件只改变速度 不改变加速度

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // update bird positionY  
  2. if (hero->getPositionY() > 0 && hero->getPositionY() < winSize.height)  
  3. {  
  4.     velocity -= gravity;  
  5.     hero->setPositionY(hero->getPositionY() + velocity);  
  6. }  
  7. //if (isFlying&&hero->getPositionY() < winSize.height)  
  8. //{  
  9. //  hero->setPositionY(hero->getPositionY() + velocity);  
  10. //}  
  11. //else if (hero->getPositionY()>0)  
  12. //{  
  13. //  hero->setPositionY(hero->getPositionY() - velocity);  
  14. //}  
  15. //check collision  

将之前的速度改变方式修改一下,每次点击给一个向上的速度,然后重力向下,这样就类似原游戏的跳跃感了

至于数值可以继续微调来加大游戏难度


=====================================

原作还有好多细节等愿意动弹了慢慢添加

源码下载

http://download.csdn.net/detail/iamlazybone/6914353

0 0
原创粉丝点击