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(物理引擎,委托)

时间: 2024-07-30 08:06:54

Flappy Bird的相关文章

Flappy bird用户手册

   flappy bird基本玩法: 小鸟会在屏幕中自动往前飞,点击屏幕小鸟就会弹一下,不点击屏幕就会直接往下掉.小鸟通过一根水管就能得一分,直到小鸟撞上水管游戏结束,然后结算分数. 在此过程中: (1)界面刚开始时,按空格键,然后按左键,进入运行界面,按空格键让鸟向上移动.  (2)通过吃苹果,可以无障碍的通过一个柱子.  (3)通过吃橙子,可以隐身5秒 flappy bird下载: flappy bird我们已经放在云平台共享上,链接网址为:http://pan.baidu.com/s/1

下坠的小鸟(flappy bird)速算版

下坠的小鸟速算版是根据著名的像素鸟(flappy bird)改编而成的一款运行在pc web上的游戏,它跟传统的玩法稍有不同,你必须时刻计算当前数字的倍数,以便为通过下一个数字缺口做准备,而不仅仅只是通过当前缺口.这不仅考验着您的速算功力,还对您的兼顾能力发起了挑战.ready? Go! http://sentsin.com/hello/flappy/ 下坠的小鸟(flappy bird)速算版,布布扣,bubuko.com

NOIP2014 飞扬的小鸟(Flappy Bird)

题目描述 Flappy Bird 是一款风靡一时的休闲手机游戏.玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙.如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败. 为了简化问题,我们对游戏规则进行了简化和改编: 游戏界面是一个长为n ,高为 m 的二维平面,其中有k 个管道(忽略管道的宽度). 小鸟始终在游戏界面内移动.小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成. 小鸟每个单位时间沿横坐标方向右移的距离为1 ,竖直移动

【转】通过制作Flappy Bird了解Native 2D中的Sprite,Animation

作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 引子 上一次我们讲了MonoBehaviour的前世今生,了解了游戏中的每一个GameObjec都是由脚本控制的,这一次我们开始将Unity中Native 2D中的Sprite,并且使用Animation来让Sprite动起来. 在接下来的几篇博客里,我会通过做一个Flappy Bird来讲解Unity中各个组件的使用,项目的源代码在这里:U

【转】通过制作Flappy Bird了解Native 2D中的RigidBody2D和Collider

作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 引子 在第一篇文章[Unity3D基础教程]给初学者看的Unity教程(一):GameObject,Compoent,Time,Input,Physics我已经讲过了一些关于刚体和碰撞的关系,这次我们就通过Flappy Bird这个事例来讲解一下刚体和碰撞体在游戏中的具体应用.相关代码可以参考Flappy Bird的源码. 认识RigidBo

【教程】HTML5+JavaScript编写flappy bird

     作者: 风小锐      新浪微博ID:永远de风小锐      QQ:547953539      转载请注明出处 PS:新修复了两个bug,已下载代码的同学请查看一下 大学立即要毕业了.未来的公司为我们制定了在校学习计划.希望我们能在毕业之前掌握一些技术,当中有一项就是使用HTML5+JavaScript编写flappy bird这个小游戏. 相信大家和我一样,对这个小游戏还是非常熟悉的,控制小鸟跳过高矮不一的水管,并记录下每局得到的分数,对于亲手编写这个小游戏非常感兴趣,立即開始

Unity3D基础教程】(四):通过制作Flappy Bird了解Native 2D...

[狗刨学习网] 引子 在第一篇文章[Unity3D基础教程]给初学者看的Unity教程(一):GameObject,Compoent,Time,Input,Physics我已经讲过了一些关于刚体和碰撞的关系,这次我们就通过Flappy Bird这个事例来讲解一下刚体和碰撞体在游戏中的具体应用.相关代码可以参考Flappy Bird的源码. 认识RigidBody 当RigidBody2D的质量属性被设置为0时,刚体的质量变为无限大,此时刚体相当于静态刚体,永远一动不动.但是在Unity中你是无法

【Unity3D基础教程】(三):通过制作Flappy Bird了解Native 2D

[狗刨学习网] 引子 上一次我们讲了MonoBehaviour的前世今生,了解了游戏中的每一个GameObjec都是由脚本控制的,这一次我们开始将Unity中Native 2D中的Sprite,并且使用Animation来让Sprite动起来. 在接下来的几篇博客里,我会通过做一个Flappy Bird来讲解Unity中各个组件的使用,项目的源代码在这里:Unity Flappy Bird.欢迎各位前去Fork和Star. 如何创建Sprite 创建一个Sprite可以遵循如下步骤 将一张图片拖

SDL版Flappy bird代码分享

用SDL编写的Flappy bird电脑版. 请大家尊重原创,转载或者用到其中的函数请注明出处,以及作者(五十风) main.cpp /*************************************************************** * 版权所有 (C)2014, 五十风 * * 文件名称:main.cpp * 内容摘要:FlappyBird主函数文件 * 其它说明:无 * 当前版本:v1.2 * 作 者:五十风 * 完成日期:2014.11.4 * 代替版本:v1

用Tensorflow基于Deep Q Learning DQN 玩Flappy Bird

前言 2013年DeepMind 在NIPS上发表Playing Atari with Deep Reinforcement Learning 一文,提出了DQN(Deep Q Network)算法,实现端到端学习玩Atari游戏,即只有像素输入,看着屏幕玩游戏.Deep Mind就凭借这个应用以6亿美元被Google收购.由于DQN的开源,在github上涌现了大量各种版本的DQN程序.但大多是复现Atari的游戏,代码量很大,也不好理解. Flappy Bird是个极其简单又困难的游戏,风靡