Flappy Bird

Flappy Bird

源码取自:https://github.com/OiteBoys/Earlybird

(一) 文件的读取

  游戏中非常重要的元素---图片,所以先从图片下手。

  由于所有的图片资源都是放在一张大图上的,切割图片保存的又不是plist文件,所以在这里模拟SpriteFrameCache写了一个AtlasLoader类,该类主要功能用于缓存图片。

AtlasLoader.h

 1 #pragma once
 2 #include "cocos2d.h"
 3 
 4 using namespace cocos2d;
 5 using namespace std;
 6 
 7 // 图片信息结构体,主要用于保存图片
 8 typedef struct _atlas{
 9     char name[255];
10     int width;
11     int height;
12     Point start;
13     Point end;
14 } Atlas;
15 
16 class AtlasLoader{
17 public:
18     // 获取单例
19     static AtlasLoader* getInstance();
20 
21     // 销毁单例
22     static void destroyInstance();
23 
24     /**
25     * 加载文件
26     * 这个函数加载图片,会延迟主线程
27     * 例: AtlasLoader::getInstance()->loadAtlas("atlas.txt");
28     */
29     void loadAtlas(string filename);
30 
31     /**
32     * 加载文件
33     * 可以在异步加载纹理的回调函数中调用该函数
34     */
35     void loadAtlas(string filename, Texture2D *texture);
36 
37     /**
38     * 通过名字获取精灵帧,用于创建精灵
39     * 提示:应该在用getTextureCache加载atlas.png完成之后调用该函数
40     * 例如. SpriteFrame *bg_day = AtlasLoader::getInstance()->getSpriteFrameByName("bg_day");
41     */
42     SpriteFrame* getSpriteFrameByName(string name);
43 
44 protected:
45     /**
46      *  默认构造函数
47      */
48     AtlasLoader();
49     
50     /**
51      *  初始化 
52      */
53     virtual bool init();
54 
55     /**
56      * 单例指针
57      */
58     static AtlasLoader* sharedAtlasLoader;
59 
60     /**
61     *  用于保存精灵帧的MAP<std::string 名字,SpriteFrame* 精灵帧指针>
62     */
63     Map<std::string, SpriteFrame*> _spriteFrames;
64 };

AtlasLoader.cpp

 1 #include "AtlasLoader.h"
 2 
 3 // 初始化单例指针(因为是静态变量,所以在这里初始化)
 4 AtlasLoader* AtlasLoader::sharedAtlasLoader = nullptr;
 5 
 6 AtlasLoader* AtlasLoader::getInstance(){
 7     if(sharedAtlasLoader == NULL) {
 8         sharedAtlasLoader = new AtlasLoader();
 9         if(!sharedAtlasLoader->init()){
10             delete sharedAtlasLoader;
11             sharedAtlasLoader = NULL;
12             CCLOG("ERROR: Could not init sharedAtlasLoader");
13         }
14     }
15     return sharedAtlasLoader;
16 }
17 
18 void AtlasLoader::destroyInstance()
19 {
20     CC_SAFE_DELETE(sharedAtlasLoader);
21 }
22 
23 AtlasLoader::AtlasLoader(){}
24 
25 
26 bool AtlasLoader::init(){
27     return true;
28 }
29 
30 void AtlasLoader::loadAtlas(string filename){
31     // 获取一张纹理
32     auto textureAtlas = Director::getInstance()->getTextureCache()->addImage("atlas.png");
33     // 加载文件
34     this->loadAtlas(filename, textureAtlas);
35 }
36 
37 void AtlasLoader::loadAtlas(string filename, Texture2D *texture) {
38     // 读取文件
39     string data = FileUtils::getInstance()->getStringFromFile(filename);
40 
41     unsigned pos;
42     Atlas atlas;
43     pos = data.find_first_of("\n");
44     string line = data.substr(0, pos);
45     data = data.substr(pos + 1);
46     while(line != ""){
47         sscanf(line.c_str(), "%s %d %d %f %f %f %f", 
48         atlas.name, &atlas.width, &atlas.height, &atlas.start.x, 
49         &atlas.start.y, &atlas.end.x, &atlas.end.y);
50         atlas.start.x = 1024*atlas.start.x;
51         atlas.start.y = 1024*atlas.start.y;
52         atlas.end.x = 1024*atlas.end.x;
53         atlas.end.y = 1024*atlas.end.y;
54 
55         pos = data.find_first_of("\n");
56         line = data.substr(0, pos);
57         data = data.substr(pos + 1);
58 
59         // use the data to create a sprite frame
60         // fix 1px edge bug
61         if(atlas.name == string("land")) {
62             atlas.start.x += 1;
63         }
64 
65         Rect rect = Rect(atlas.start.x, atlas.start.y, atlas.width, atlas.height);
66         auto frame = SpriteFrame::createWithTexture(texture, rect);        // 通过纹理创建精灵帧
67         this->_spriteFrames.insert(string(atlas.name), frame);            // 插入到MAP中
68     }
69 }
70 
71 SpriteFrame* AtlasLoader::getSpriteFrameByName(string name){
72     return this->_spriteFrames.at(name);
73 }

 

(二) LoadingScene(加载场景)

  完成图片缓存之后,我们真正的开始游戏。启动游戏的第一个场景LoadingScene。

  ---> 异步加载图片

  ---> 加载声音

  ---> 场景跳转

 LoadingScene.h

 1 #include "cocos2d.h"
 2 #include "AtlasLoader.h"
 3 #include "SimpleAudioEngine.h"
 4 #include "HelloWorldScene.h"
 5 #include "WelcomeScene.h"
 6 #include "BackgroundLayer.h"
 7 
 8 using namespace cocos2d;
 9 using namespace CocosDenshion;
10 
11 class LoadingScene : public Scene {
12 public:
13     /**
14      * 默认构造
15      */
16     LoadingScene();
17     
18     ~LoadingScene();
19     
20     /**
21      *  初始化
22      */
23     virtual bool init();
24     
25     /* 提供一个create方法,并且返回该类的指针
26     static __TYPE__* create() 27     { 28         __TYPE__ *pRet = new __TYPE__(); 29         if (pRet && pRet->init()) 30         { 31             pRet->autorelease(); 32             return pRet; 33         } 34         else 35         { 36             delete pRet; 37             pRet = NULL; 38             return NULL; 39         } 40     }
41     */
42     CREATE_FUNC(LoadingScene);
43     
44     /**
45      * 该类被载入场景的时候被调用,可能会发生多次
46      */
47     void onEnter() override;
48     
49 private:
50     /**
51      * 异步加载纹理完成之后的回调函数
52      */
53     void loadingCallBack(Texture2D *texture);
54 };

LoadingScene.cpp

 1 #include "LoadingScene.h"
 2 LoadingScene::LoadingScene(){}
 3 
 4 LoadingScene::~LoadingScene(){}
 5 
 6 bool LoadingScene::init() {
 7     if(Scene::init()){
 8         return true;
 9     } else {
10         return false;
11     }
12 }
13 
14 void LoadingScene::onEnter(){
15     // 给当前场景添加背景
16     Sprite *background = Sprite::create("splash.png");
17     Size visibleSize = Director::getInstance()->getVisibleSize();
18     Point origin = Director::getInstance()->getVisibleOrigin();
19     background->setPosition(origin.x + visibleSize.width/2, origin.y + visibleSize.height/2);
20     this->addChild(background);
21 
22     // 开始异步加载 atlas.png
23     Director::getInstance()->getTextureCache()->addImageAsync("atlas.png", CC_CALLBACK_1(LoadingScene::loadingCallBack, this));
24 }
25 
26 void LoadingScene::loadingCallBack(Texture2D *texture){
27 
28     // 加载完成之后,切割图片,保存成精灵帧
29     AtlasLoader::getInstance()->loadAtlas("atlas.txt", texture);
30 
31     // 加载声音
32     SimpleAudioEngine::getInstance()->preloadEffect("sfx_die.ogg");
33     SimpleAudioEngine::getInstance()->preloadEffect("sfx_hit.ogg");
34     SimpleAudioEngine::getInstance()->preloadEffect("sfx_point.ogg");
35     SimpleAudioEngine::getInstance()->preloadEffect("sfx_swooshing.ogg");
36     SimpleAudioEngine::getInstance()->preloadEffect("sfx_wing.ogg");
37 
38     // 加载完成之后,跳转到欢迎场景
39     auto scene = WelcomeScene::create();
40     TransitionScene *transition = TransitionFade::create(1, scene);
41     Director::getInstance()->replaceScene(transition);
42 }

 

(三) WelcomeScene(欢迎场景)

  资源加载完成之后,跳转到欢迎场景,在这里我们将看到我们的主角---小鸟。

  ---> 显示背景

  ---> 地图无限滚动

  ---> 菜单

  ---> 小鸟类

WelcomeScene.h

 1 #pragma once
 2 #include "AtlasLoader.h"
 3 #include "WelcomeLayer.h"
 4 #include "BackgroundLayer.h"
 5 #include "cocos2d.h"
 6 using namespace cocos2d;
 7 using namespace std;
 8 
 9 /*
10     该类就非常简单了,主要在这里添加了一个WelcomeLayer层。
11 */
12 class WelcomeScene : public Scene{
13 public:
14     WelcomeScene(void);
15     ~WelcomeScene(void);
16     bool virtual init();
17     CREATE_FUNC(WelcomeScene);
18 };

WelcomeScene.cpp

 1 #include "WelcomeScene.h"
 2 
 3 WelcomeScene::WelcomeScene(){};
 4 
 5 WelcomeScene::~WelcomeScene(){};
 6 
 7 bool WelcomeScene::init(){
 8     bool bRet = false;
 9     do{
10         CC_BREAK_IF(!Scene::init());
11         auto _welcomeLayer = WelcomeLayer::create();
12         CC_BREAK_IF(!_welcomeLayer);
13         this->addChild(_welcomeLayer);
14         bRet = true;
15     }while(0);
16     return bRet;
17 }

 WelcomeLayer.h

 1 #pragma once
 2 
 3 #include "AtlasLoader.h"
 4 #include "SimpleAudioEngine.h"
 5 #include "CCMenuItem.h"
 6 #include "GameScene.h"
 7 #include "time.h"
 8 #include "cocos2d.h"
 9 #include "BirdSprite.h"
10 
11 using namespace cocos2d;
12 using namespace std;
13 using namespace CocosDenshion;
14 
15 const int START_BUTTON_TAG = 100;
16 
17 class WelcomeLayer : public Layer{
18 public:
19     WelcomeLayer(void);
20     ~WelcomeLayer(void);
21     virtual bool init();
22     
23     CREATE_FUNC(WelcomeLayer);
24     
25 private:
26     /**
27      * 开始按钮回调
28      */
29     void menuStartCallback(Object *sender);
30     
31     /**
32      * 地图无限滚动回调
33      */
34     void scrollLand(float dt);
35     
36     Sprite *land1;            // 地板1
37     Sprite *land2;            // 地板2
38     BirdSprite *bird;        // 主角小鸟
39 };

WelcomeLayer.cpp

  1 #include "WelcomeLayer.h"
  2 
  3 WelcomeLayer::WelcomeLayer(){};
  4 
  5 WelcomeLayer::~WelcomeLayer(){};
  6 
  7 bool WelcomeLayer::init(){
  8     if(!Layer::init()){
  9         return false;
 10     }
 11     
 12     Size visiableSize = Director::getInstance()->getVisibleSize();
 13     Point origin = Director::getInstance()->getVisibleOrigin();
 14 
 15     // 获取当前时间
 16     time_t t = time(NULL);
 17     tm* lt = localtime(&t);
 18     int hour = lt->tm_hour;
 19     
 20     // 根据当前时间创建背景(这里就用到了我们的AtlasLoader喽)
 21     Sprite *background;
 22     if(hour >= 6 && hour <= 17){
 23         background = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("bg_day"));
 24     }else{
 25         background = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("bg_night"));
 26     }
 27     background->setAnchorPoint(Point::ZERO);
 28     background->setPosition(Point::ZERO);
 29     this->addChild(background);
 30 
 31     // 游戏标题
 32     Sprite *title  = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("title"));
 33     title->setPosition(Point(origin.x + visiableSize.width/2 , (visiableSize.height * 5) / 7));
 34     this->addChild(title);
 35 
 36     // 添加开始菜单
 37     Sprite *startButton = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("button_play"));
 38     Sprite *activeStartButton = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("button_play"));
 39     
 40     // 创建菜单项 WelcomeLayer::menuStartCallback 为该菜单项的响应函数
 41     auto menuItem  = MenuItemSprite::create(startButton,activeStartButton,NULL,CC_CALLBACK_1(WelcomeLayer::menuStartCallback, this));
 42     menuItem->setPosition(Point(origin.x + visiableSize.width/2 ,origin.y + visiableSize.height*2/5));
 43 
 44     // 将菜单项加到菜单中,已NULL结束
 45     auto menu = Menu::create(menuItem,NULL);
 46     menu->setPosition(Point(origin.x ,origin.y));
 47     this->addChild(menu,1);
 48 
 49     // 创建小鸟(下面有具体的介绍)
 50     this->bird = BirdSprite::getInstance();
 51     this->bird->createBird();
 52     this->bird->setTag(BIRD_SPRITE_TAG);
 53     this->bird->setPosition(Point(origin.x + visiableSize.width / 2,origin.y + visiableSize.height*3/5 - 10));
 54     this->bird->idle();    // 使小鸟处于准备状态。
 55     this->addChild(this->bird);
 56 
 57     // 添加地板
 58     this->land1 = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("land"));
 59     this->land1->setAnchorPoint(Point::ZERO);
 60     this->land1->setPosition(Point::ZERO);
 61     this->addChild(this->land1);
 62 
 63     this->land2 = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("land"));
 64     this->land2->setAnchorPoint(Point::ZERO);
 65     this->land2->setPosition(this->land1->getContentSize().width - 2.0f, 0);
 66     this->addChild(this->land2);
 67 
 68     // 开启一个计时器,用于地板滚动
 69     this->schedule(schedule_selector(WelcomeLayer::scrollLand), 0.01f);
 70 
 71     // 版权
 72     Sprite *copyright = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrameByName("brand_copyright"));
 73     copyright->setPosition(Point(origin.x + visiableSize.width/2, origin.y + visiableSize.height/6));
 74     this->addChild(copyright, 10);
 75 
 76     return true;
 77 }
 78 
 79 void WelcomeLayer::scrollLand(float dt){
 80     /*  地图无限滚动原理:
 81         实际上是有两块地图并排放置,每隔一段时间设置两块地图的X坐标,当第一块地图完全移出屏幕的时候,
 82         第二块地图正好完全显示在屏幕内,这时再将第一块的地图X坐标设置为0,第二块地图X坐标设置为第一块地图的后面,
 83         无限循环,就形成了地图无限滚动的视觉效果。
 84     */
 85         
 86     this->land1->setPositionX(this->land1->getPositionX() - 2.0f);
 87     this->land2->setPositionX(this->land1->getPositionX() + this->land1->getContentSize().width - 2.0f);
 88 
 89     if(this->land2->getPositionX() == 0) {
 90         this->land1->setPositionX(0);
 91     }
 92 }
 93 
 94 void WelcomeLayer::menuStartCallback(Object *sender){
 95     // 播放音效
 96     SimpleAudioEngine::getInstance()->playEffect("sfx_swooshing.ogg");
 97     // 移除小鸟
 98     this->removeChildByTag(BIRD_SPRITE_TAG);
 99     // 跳转到游戏场景
100     auto scene = GameScene::create();
101     TransitionScene *transition = TransitionFade::create(1, scene);
102     Director::getInstance()->replaceScene(transition);
103 }

 


4.GameScene(物理引擎,委托)

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。