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