懒骨头(http://blog.youkuaiyun.com/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 "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;
- }
#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;
}
initUI里是一些UI初始化方法:
- // 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);
// 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;
- }
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();
- }
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);
- }
- }
- }
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));
- }
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);
void onTouchesEnded(const vector<Touch*>& touches, Event* event);
void onTouchesBegan(const vector<Touch*>& touches, Event* event);
在cpp文件的初始化里绑定事件:
- // 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);
// 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);
在两个事件方法里改变标记位,在小鸟的update方法里根据这个标记位来改变高度
(哲哲喊我休息了,先到这吧,待续。。。)
=============================
碰撞检测:
记得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
- <span style="white-space: pre;"> </span>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;
- }
- }
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;
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;
接下来可以优化很多很多东西