• Cocos2d-x 2.3.3版本 FlappyBird


    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
    }
    


    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    OSI Model Reference graphic / What do you guys think? Anything to edit/remove/add?
    Iframe Target for a Link
    Abp FullAuditedAggregateRoot
    After Effects上课视频
    Openwrt NAT ALG
    ipcalc.sh
    网络流量测试工具
    NatTypeTester
    基于DPDK开源项目
    GPS的调试与报文解析
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4728244.html
Copyright © 2020-2023  润新知