標籤:des style blog http color 使用
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\tools\project-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 "Box2D\Box2D.h"// 引入Box2D物理引擎#include "B2Sprite.h"// 定義物理世界的比例#define RATIO 48.0fclass 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 objectCCScene *scene = CCScene::create();// ‘layer‘ is an autorelease objectHelloWorld *layer = HelloWorld::create();// add layer as a child to scenescene->addChild(layer);// return the scenereturn scene;}// on "init" you need to initialize your instancebool HelloWorld::init(){//////////////////////////////// 1. super init firstif ( !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");#elseCCDirector::sharedDirector()->end();#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)exit(0);#endif#endif}