b2Body *birdBody = world->CreateBody(&bodyDef);
// 隐形形状
b2PolygonShape birdShape;
// 设置为盒子,参数为内容的半宽半高
birdShape.SetAsBox(size.width/2/RATIO, size.height/2/RATIO);
// 材料属性
b2FixtureDef birdFixtureDef;
// 形状
birdFixtureDef.shape = &birdShape;
// 添加地表物体
birdBody->CreateFixture(&birdFixtureDef);
// 设置度量比例
bird->setPTMRatio(RATIO);
// 设置小鸟刚体
bird->setB2Body(birdBody);
// 添加小鸟到层中
addChild(bird);
}
4. 如何添加地板
地板跟小鸟也是类似的,只是设置地板刚体的类型为静态的,因为它相对静止的也不受重力影响。
/**
*/
{
// 地板精灵
B2Sprite *ground = B2Sprite::create(“ground.png”);
// 得到地板内容的大小
CCSize size = ground->getContentSize();
// 用于初始化刚体在物理世界的一些属性,比如位置,类型
b2BodyDef bDef;
// 静态的刚体
bDef.type = b2_staticBody;
// 设置位置
bDef.position = b2Vec2(size.width/2/RATIO, size.height/2/RATIO);
// 创建刚体
b2Body *groundBody = world->CreateBody(&bDef);
// 形状
b2PolygonShape groundShape;
// 设置为矩形
groundShape.SetAsBox(size.width/2/RATIO, size.height/2/RATIO);
// 材料定制器
b2FixtureDef groundFixtureDef;
// 设置形状
groundFixtureDef.shape = &groundShape;
// 创建定制器
groundBody->CreateFixture(&groundFixtureDef);
// 为精灵设置刚体
ground->setB2Body(groundBody);
// 设置度量比例
ground->setPTMRatio(RATIO);
// 添加地板到层当中
addChild(ground);
}
5. 添加水管
我们玩FlappyBird的时候,会知道水管高低不同,然后是从右往左运动的,这就需要我们随机设置上下两根水管的位置了,并且不停的添加水管。
/ 添加运动的水管
void HelloWorld::addBar(float dt) {
// 随机生成偏移量
float offset = -rand() %5;
// 创建向下水管的精灵
B2Sprite *down_bar = B2Sprite::create(“down_bar.png”);
// 得到水管的大小
CCSize down_bar_size = down_bar->getContentSize();
// 下水管
b2BodyDef down_bar_body_def;
// 运动学物体,但不受重力影响
down_bar_body_def.type = b2_kinematicBody;
// 设置下水管的位置
down_bar_body_def.position = b2Vec2(screenSize.width/RATIO + 2, down_bar_size.height/RATIO/2 +offset);
// 线性速度,从右往左移动
down_bar_body_def.linearVelocity = b2Vec2(-5,0);
// 创建刚体
b2Body *down_bar_body = world->CreateBody(&down_bar_body_def);
// 隐形形状
b2PolygonShape down_bar_shape;
// 设置为盒子,参数为内容的半宽半高
down_bar_shape.SetAsBox(down_bar_size.width/2/RATIO, down_bar_size.height/2/RATIO);
// 声明定制器
b2FixtureDef down_bar_fixture_def;
// 定制器形状
down_bar_fixture_def.shape = &down_bar_shape;
// 为刚体创建定制器
down_bar_body->CreateFixture(&down_bar_fixture_def);
// 设置精灵刚体
down_bar->setB2Body(down_bar_body);
// 设置度量
down_bar->setPTMRatio(RATIO);
// 上水管
B2Sprite *up_bar = B2Sprite::create(“up_bar.png”);
// 获得内容大小
CCSize up_bar_size = up_bar->getContentSize();
b2BodyDef up_bar_body_def;
// 运动学物体,但不受重力影响
up_bar_body_def.type = b2_kinematicBody;
// 设置水管位置
up_bar_body_def.position = b2Vec2(screenSize.width/RATIO+2, down_bar_size.height/RATIO+offset+2+up_bar_size.height/2/RATIO);
up_bar_body_def.linearVelocity = b2Vec2(-5, 0);
b2Body *up_bar_body = world->CreateBody(&up_bar_body_def);
// 隐形形状
b2PolygonShape up_bar_shape;
// 设置为盒子形状,参数为半宽半高
up_bar_shape.SetAsBox(up_bar_size.width/2/RATIO, up_bar_size.height/2/RATIO);
b2FixtureDef up_bar_fixture_def;
up_bar_fixture_def.shape = &up_bar_shape;
up_bar_body->CreateFixture(&up_bar_fixture_def);
up_bar->setB2Body(up_bar_body);
up_bar->setPTMRatio(RATIO);
barContainer->addChild(down_bar);
barContainer->addChild(up_bar);
}
6. 添加碰撞检测
这里需要说一下如何让小鸟运动,我们需要设置屏幕的监听事件,并且让小鸟有一个向上的线性速度,点击屏幕的时候小鸟向上运动,松开则下坠。
我们需要重写方法:
virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
实现:
// 触摸事件开始
void HelloWorld::ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent) {
bird->getB2Body()->SetLinearVelocity(b2Vec2(0, 5));
}
接下来讲碰撞检测,这个我们同样也要设置监听器,我们设置的是物理世界的事件监听。
// 添加碰撞监听
world->SetContactListener(this);
然后重写以下方法:
virtual void BeginContact(b2Contact* contact);
void HelloWorld::BeginContact(b2Contact *contact) {
// 发生碰撞,则弹出对话框
if (contact->GetFixtureA()->GetBody()->GetUserData() == bird ||
contact->GetFixtureB()->GetBody()->GetUserData() == bird) {
stopGame();
CCMessageBox(“游戏失败”,“游戏失败”);
}
}
7. 本文总结
以上内容就是开发一款FlappyBird的简单Demo,读者可以在这个的基础上实现更丰富的功能,笔者觉得不想一步到位把所有东西介绍完毕。最重要的还是思路,把基本的东西掌握了,然后就可以按照这样的思路去做一个这样的东西。
可以到以下地址下载源码:https://github.com/devilWwj/eoeFlappyBird
下面是完整实现:
HelloWorldScene.h
#ifndef HELLOWORLD_SCENE_H
#define HELLOWORLD_SCENE_H
#include “cocos2d.h”
#include “Box2D\Box2D.h”// 引入Box2D物理引擎
#include “B2Sprite.h”
// 定义物理世界的比例
#define RATIO 48.0f
class HelloWorld : public cocos2d::CCLayer,public b2ContactListener
{
public:
// Here’s a difference. Method ‘init’ in cocos2d-x returns bool, instead of returning ‘id’ in cocos2d-iphone
virtual bool init();
// there’s no ‘id’ in cpp, so we recommend returning the class instance pointer
static cocos2d::CCScene* scene();
// a selector callback
void menuCloseCallback(CCObject* pSender);
// implement the “static node()” method manually
CREATE_FUNC(HelloWorld);
virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
virtual void BeginContact(b2Contact* contact);
// 重写update方法
virtual void update(float dt);
// 声明物理世界引用
b2World *world;
B2Sprite *bird;
CCSize screenSize;
CCSprite *barContainer;
private:
// 添加小鸟
void addBird();
// 初始化物理世界
void initWorld();
// 添加地板
void addGround();
// 添加水管
void addBar(float dt);
// 添加一个容器
void addBarContainer();
// 开始游戏
void startGame(float dt);
// 结束游戏
void stopGame();
};
#endif // HELLOWORLD_SCENE_H
HelloWorldScene.cpp
#include “HelloWorldScene.h”
USING_NS_CC;
CCScene* HelloWorld::scene()
{
// ‘scene’ is an autorelease object
CCScene *scene = CCScene::create();
// ‘layer’ is an autorelease object
HelloWorld *layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on “init” you need to initialize your instance
bool HelloWorld::init()
{
//
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
// 获取屏幕大小
screenSize = CCDirector::sharedDirector()->getVisibleSize();
initWorld();
addBird();
addBarContainer();
addGround();
// 设置可点击
setTouchEnabled(true);
//scheduleUpdate();
//schedule(schedule_selector(HelloWorld::addBar), 1);
// 3秒之后执行
scheduleOnce(schedule_selector(HelloWorld::startGame),3);
return true;
}
/**
添加小鸟
*/
void HelloWorld::addBird()
{
// 创建小鸟
bird = B2Sprite::create(“bird.png”);
// 获取内容大小
CCSize size = bird->getContentSize();
// 刚体属性
b2BodyDef bodyDef;
// 动态刚体
bodyDef.type = b2_dynamicBody;
// 设置初始位置
bodyDef.position = b2Vec2(screenSize.width/2/RATIO, screenSize.height/2/RATIO);
// 创建一个小鸟刚体
b2Body *birdBody = world->CreateBody(&bodyDef);
// 隐形形状
b2PolygonShape birdShape;
// 设置为盒子,参数为内容的半宽半高
birdShape.SetAsBox(size.width/2/RATIO, size.height/2/RATIO);
// 材料属性
b2FixtureDef birdFixtureDef;
// 形状
birdFixtureDef.shape = &birdShape;
// 添加地表物体
birdBody->CreateFixture(&birdFixtureDef);
// 设置度量比例
bird->setPTMRatio(RATIO);
// 设置小鸟刚体
bird->setB2Body(birdBody);
// 添加小鸟到层中
addChild(bird);
}
// 初始化物理世界
void HelloWorld::initWorld()
{
// -10表示重力加速度方向为向下
world = new b2World(b2Vec2(0, -10));
// 添加碰撞监听
world->SetContactListener(this);
}
// 更新
void HelloWorld::update(float dt)
{
// 模拟物理世界
// Box2D建议的迭代次数是速度阶段8次,位置阶段3次
world->Step(dt, 8, 3);
CCSprite *s;
// 遍历销毁
for (b2Body *b = world->GetBodyList(); b!= NULL; b=b->GetNext()) {
if (b->GetPosition().x<-3) {
s = (CCSprite*)b->GetUserData();
);
// 材料属性
b2FixtureDef birdFixtureDef;
// 形状
birdFixtureDef.shape = &birdShape;
// 添加地表物体
birdBody->CreateFixture(&birdFixtureDef);
// 设置度量比例
bird->setPTMRatio(RATIO);
// 设置小鸟刚体
bird->setB2Body(birdBody);
// 添加小鸟到层中
addChild(bird);
}
// 初始化物理世界
void HelloWorld::initWorld()
{
// -10表示重力加速度方向为向下
world = new b2World(b2Vec2(0, -10));
// 添加碰撞监听
world->SetContactListener(this);
}
// 更新
void HelloWorld::update(float dt)
{
// 模拟物理世界
// Box2D建议的迭代次数是速度阶段8次,位置阶段3次
world->Step(dt, 8, 3);
CCSprite *s;
// 遍历销毁
for (b2Body *b = world->GetBodyList(); b!= NULL; b=b->GetNext()) {
if (b->GetPosition().x<-3) {
s = (CCSprite*)b->GetUserData();