懒骨头(http://blog.csdn.net/iamlazybone QQ:124774397 青岛)
笔记分好几次完成,可能有的地方转载的不全,请 传送到这里
《Flappy Bird》
关于这个游戏骨头不多说了
直接开始学习吧(山寨不好听)
正好前段时间看了几个DEMO拿这个游戏练练手
开搞!
报环境:
vs2013+cocos2dx3.0beta2
首先下载apk,找到资源文件,裁剪几个图片:
用脚本新建一个空的Cocos2dx项目
新建一个Scene类
#include "cocos2d.h" #include "Obstacle.h" class FlyBirdGame :public cocos2d::Layer { public: static cocos2d::Scene* createScene(); virtual bool init(); CREATE_FUNC(FlyBirdGame); void initUI(); void gameStart(Object* pSender); void update(float time); Obstacle* obstacle; };
#include "cocos2d.h" #include "FlyBirdGame.h" #include "resource.h"; USING_NS_CC; Scene* FlyBirdGame::createScene() { auto scene = Scene::create(); auto layer = FlyBirdGame::create(); scene->addChild(layer); return scene; } bool FlyBirdGame::init() { if (!Layer::init()) { return false; } initUI(); return true; }
// win size auto winSize = Director::getInstance()->getVisibleSize(); // game bg auto bg = Sprite::create(bird_bg); bg->setPosition(winSize.width / 2, winSize.height / 2); bg->setScale(winSize.width / bg->getContentSize().width, winSize.height / bg->getContentSize().height); this->addChild(bg); // start btn auto startBtn = MenuItemImage::create(bird_start_btn, bird_start_btn_pressed, CC_CALLBACK_1(FlyBirdGame::gameStart, this)); auto menu = Menu::create(startBtn, NULL); menu->setTag(100); this->addChild(menu); // hero auto hero = Sprite::create(bird_hero); hero->setPosition(winSize.width / 5, winSize.height*0.8); hero->setVisible(false); hero->setTag(200); this->addChild(hero);
开始游戏按钮绑定的gameStart方法:
void FlyBirdGame::gameStart(Object* pSender) { auto btn = this->getChildByTag(100); btn->setVisible(false); auto hero = this->getChildByTag(200); Size win = Director::getInstance()->getWinSize(); obstacle->gameStart = true; }
还有更新方法:
scheduleUpdate(); void FlyBirdGame::update(float time) { obstacle->update(); }
水管类:Obstacle.cpp
update方法里判断游戏是游戏是否开始
void Obstacle::update() { if (gameStart == false) return; addCount++; if (addCount == 60) { addOne(0); addCount = 0; } for (int i = obstacleList->count() - 1; i >= 0; i--) { auto s = (Sprite*)obstacleList->getObjectAtIndex(i); s->setPositionX(s->getPositionX() - 3); if (s->getPositionX() < -s->getContentSize().width / 2) { obstacleList->removeObjectAtIndex(i); this->removeChild(s); } } }水管类的更新方法里,每60帧(1秒)添加一对水管
并且遍历水管列表
出边界的化销毁
接下来是addOne方法:添加水管方法:
void Obstacle::addOne(int offsetX) { Size size = Director::getInstance()->getWinSize(); auto sprite = Sprite::create(bird_obstacle_up); Size spriteSize = sprite->getContentSize(); obstacleList->addObject(sprite); this->addChild(sprite); auto sprite2 = Sprite::create(bird_obstacle_down); Size spriteSize2 = sprite->getContentSize(); obstacleList->addObject(sprite2); this->addChild(sprite2); // set positon int maxUpY = size.height + spriteSize.height / 4; int minUpY = size.height - spriteSize.height / 4; int y1 = CCRANDOM_0_1()*(maxUpY - minUpY) + minUpY; int maxDownY = spriteSize.height / 4; int minDownY = -spriteSize.height / 4; int y2 = CCRANDOM_0_1()*(maxDownY - minDownY) + minDownY; if (y1 - y2 - spriteSize.height < 160) { y2 = y1 - spriteSize.height - 160; } sprite->setPosition(ccp(size.width + spriteSize.width / 2 + offsetX, y1)); sprite2->setPosition(ccp(size.width + spriteSize2.width / 2 + offsetX, y2)); }这段代码比较凌乱,就是找到水管上下位置的范围
然后随机一下,并且保证上下连个水管有个最小的距离
效果如下:
此时的游戏还没触摸和碰撞逻辑
马上添加:(刚才抽空玩了把魔方:五阶的我只能搞定一个面,虽然有官方规律但是那样好像比的是记忆力)
听说cocos2dx3.0的事件监听方式改变了
先在FlyBirdGame.h里声明俩方法:
void onTouchesEnded(const vector<Touch*>& touches, Event* event); void onTouchesBegan(const vector<Touch*>& touches, Event* event);
// touch auto dispatcher = Director::getInstance()->getEventDispatcher(); auto listener = EventListenerTouchAllAtOnce::create(); listener->onTouchesEnded = CC_CALLBACK_2(FlyBirdGame::onTouchesEnded, this); listener->onTouchesBegan = CC_CALLBACK_2(FlyBirdGame::onTouchesBegan, this); dispatcher->addEventListenerWithSceneGraphPriority(listener, this);
(哲哲喊我休息了,先到这吧,待续。。。)
=============================
碰撞检测:
记得cocos2dx3.0以前都是自己写一个根据CCSprite获取CCRect的方法
现在直接用 Sprite的getBoundingBox()方法
不过如果有特殊需求还得自己写,比如想扩大或缩小碰撞区域
在FlyBirdGame.cpp文件里
update算主逻辑方法:
void FlyBirdGame::update(float time)
{
auto winSize = Director::getInstance()->getVisibleSize();
auto hero = this->getChildByTag(TAG_HERO);
Rect rHero = ((Sprite*)hero)->getBoundingBox();
switch (GAME_STATUS)
{
case GAME_STATUS_PLAYING:
obstacle->update();
// update bird positionY
if (isFlying&&hero->getPositionY() < winSize.height)
{
hero->setPositionY(hero->getPositionY() + v);
}
else if (hero->getPositionY()>0)
{
hero->setPositionY(hero->getPositionY() - v);
}
//check collision
for (int i = 0; i < obstacle->obstacleList->count(); i++)
{
Sprite* obstacleRect = (Sprite*)obstacle->obstacleList->getObjectAtIndex(i);
bool pia = rHero.intersectsRect(obstacleRect->getBoundingBox());
if (pia == true)
{
GAME_STATUS = GAME_STATUS_GAME_OVER;
break;
}
}
break;
case GAME_STATUS_GAME_OVER:
CCLog("over");
this->getChildByTag(TAG_OVER)->setVisible(true);
break;
case GAME_STATUS_RESTART:
//reset game
obstacle->removeAllChildren();
obstacle->obstacleList->removeAllObjects();
// reset hero
hero->setPosition(winSize.width / 5, winSize.height*0.8);
// show btn
auto btn = this->getChildByTag(TAG_START_BTN);
btn->setVisible(true);
// show logo
auto logo = this->getChildByTag(TAG_LOGO);
logo->setVisible(true);
break;
}
}
碰撞检测方法:intersectsRect
bool pia = rHero.intersectsRect(obstacleRect->getBoundingBox());
if (pia == true)
{
GAME_STATUS = GAME_STATUS_GAME_OVER;
break;
}
游戏状态定义在 resource.h 文件里
static const int GAME_STATUS_START = 10; static const int GAME_STATUS_PLAYING = 20; static const int GAME_STATUS_GAME_OVER = 30; static const int GAME_STATUS_RESTART = 40;
接下来可以优化很多很多东西
===================================================
真搞不懂为什么和弦要那么别扭的按法
相较之下
还是代码简单
继续优化
添加小鸟动画:
添加到resource.h 文件中等待使用
也可以直接在cpp文件中用不过骨头习惯了资源统一管理
Animation* an = Animation::create(); an->addSpriteFrameWithFileName(bird_hero); an->addSpriteFrameWithFileName(bird_hero2); an->addSpriteFrameWithFileName(bird_hero3); an->setDelayPerUnit(0.5f / 3.0f); an->setLoops(-1); Animate* anim = Animate::create(an); hero->runAction(anim);
F5运行看效果,确实生动一点了
=========================================================
分辨率问题:
在AppDelegate.cpp入口方法中加入:
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(320, 480, kResolutionExactFit);
其中320,480是游戏設計時的分辨率。
后面参数介绍如下“:
//kResolutionExactFit x,y都拉伸,使铺满屏幕 //kResolutionNoBorder 一个方向铺满屏幕,另外一个方向超出屏幕 //kResolutionShowAll 854 * 480 的设计区域全部可见,但是可能留有黑边(如在960 * 640的屏幕上)=============================================================
分数的显示:
之前的几个demo都是使用本办法显示的特殊数字:一排图片然后根据每位上的数字决定显示哪个图片
本来打算自己封装个方法,再一看,有更简单的:
从自带的demo里找点合适的字体资源,拷贝到fonts文件夹下
记住把fnt和对应的png都拷贝过来
然后
auto label1 = LabelBMFont::create("123456", "fonts/futura-48.fnt"); addChild(label1); label1->setPosition(Point(win.width / 2, win.height / 2));
================================================
分数显示ok了,下面看下分数逻辑
当小鸟的坐标等于柱子的坐标时,score+1(因为是上下俩柱子对象,所以除以2显示)
别忘记判断时要让小鸟飞过柱子,而不是跟柱子纵轴对齐
相关代码在检测碰撞时添加:
//check collision for (int i = 0; i < obstacle->obstacleList->count(); i++) { Sprite* obstacleSprite = (Sprite*)obstacle->obstacleList->getObjectAtIndex(i); bool pia = rHero.intersectsRect(obstacleSprite->getBoundingBox()); if (pia == true) { GAME_STATUS = GAME_STATUS_GAME_OVER; break; } int oPosX = obstacleSprite->getPositionX() + obstacleSprite->getContentSize().width / 2; int heroX = hero->getPositionX() - hero->getContentSize().width; if (oPosX == heroX) { score++; auto scoreSprite = (LabelBMFont*)this->getChildByTag(TAG_SCORE); String* s = String::createWithFormat("%d", score / 2); scoreSprite->setString(s->getCString()); } }
======================================================
增加下游戏难度
之所以死不了是因为小鸟的速度改变的很均匀
因为触摸事件只改变速度 不改变加速度
// update bird positionY if (hero->getPositionY() > 0 && hero->getPositionY() < winSize.height) { velocity -= gravity; hero->setPositionY(hero->getPositionY() + velocity); } //if (isFlying&&hero->getPositionY() < winSize.height) //{ // hero->setPositionY(hero->getPositionY() + velocity); //} //else if (hero->getPositionY()>0) //{ // hero->setPositionY(hero->getPositionY() - velocity); //} //check collision
将之前的速度改变方式修改一下,每次点击给一个向上的速度,然后重力向下,这样就类似原游戏的跳跃感了
至于数值可以继续微调来加大游戏难度
=====================================
原作还有好多细节等愿意动弹了慢慢添加
源码下载
http://download.csdn.net/detail/iamlazybone/6914353