Cocos2d-x 2.3.3版本 FlappyBird
本篇博客基于Cocos2d-x 2.3.3, 介绍怎样开发一款之前非常火的一款游戏FlappyBird。本篇博客内容大纲例如以下:
1. 怎样创建Cocos2d-x 2.3.3 项目
2. 初始化Box2d物理世界,并模拟物理世界
3. 怎样加入小鸟到物理世界
4. 怎样加入地板
5. 加入水管
6. 碰撞检測
7. 本文总结
效果图:
1. 怎样创建Cocos2d-x 2.3.3
本篇博客是基于Cocos2d-x 2.3.3,刚開始学习的人能够选择这个版本号学习,也能够从3.x版本号学习。但版本号差异较大。
用命令行进入文件夹D:cocos2d-x-2.2.3 oolsproject-creator,敲入下面命令创建项目:
python create_project.py -project FlappyBirdCpp -package com.wwj.flappybird -language cpp
创建了名为FlappyBirdCpp的Cocos2d-x项目。包名为com.wwj.flappybird,开发语言为c++
2. 初始化Box2d物理世界,并模拟物理世界
Cocos2d-x使用了Box2d物理引擎来模拟物理世界,它还支持Chipmunk物理引擎。这里我们使用Box2d来为我们创建一个看似比較真实的世界。
// -10表示重力加速度方向为向下 world = new b2World(b2Vec2(0, -10));
// 模拟物理世界 // Box2D建议的迭代次数是速度阶段8次,位置阶段3次 world->Step(dt, 8, 3);
3. 怎样加入小鸟到物理世界
小鸟在Cocos2d-x就是一个Sprite(精灵),我们知道精灵是须要加入到层其中的。我们还要设置我们小鸟在物理世界的刚体。
关于Box2d相关的概念。笔者在这里不具体说,读者能够自己找百度老师。学习很多其它关于Box2d的知识。
我们定义下面方法:
/** * 加入小鸟 * */ 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); }
4. 怎样加入地板
地板跟小鸟也是相似的。仅仅是设置地板刚体的类型为静态的。由于它相对精巧的也不受重力影响。
/** * 加入地板 */ void HelloWorld::addGround() { // 地板精灵 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 "Box2DBox2D.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(); if (s != NULL) { s->removeFromParent(); CCLog("Remove"); } world->DestroyBody(b); } } } /** * 加入地板 */ void HelloWorld::addGround() { // 地板精灵 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); } // 触摸事件開始 void HelloWorld::ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent) { bird->getB2Body()->SetLinearVelocity(b2Vec2(0, 5)); } // 加入运动的水管 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); } // 运动条的容器 void HelloWorld::addBarContainer() { barContainer = CCSprite::create(); addChild(barContainer); } // 開始游戏 void HelloWorld::startGame(float dt) { scheduleUpdate(); schedule(schedule_selector(HelloWorld::addBar),1); } // 结束游戏 void HelloWorld::stopGame() { unscheduleUpdate(); unschedule(schedule_selector(HelloWorld::addBar)); } void HelloWorld::BeginContact(b2Contact *contact) { // 发生碰撞,则弹出对话框 if (contact->GetFixtureA()->GetBody()->GetUserData() == bird || contact->GetFixtureB()->GetBody()->GetUserData() == bird) { stopGame(); CCMessageBox("游戏失败","游戏失败"); } } void HelloWorld::menuCloseCallback(CCObject* pSender) { #if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert"); #else CCDirector::sharedDirector()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif #endif }
版权声明:本文博客原创文章,博客,未经同意,不得转载。