zoukankan      html  css  js  c++  java
  • 让子弹飞Demo版

    让子弹飞是我非常喜欢的一款游戏。今天的目标就是利用cocos2dx 3.0 和box2d 打造一款这样的类型游戏的Demo版。本来cocos2dx 3.0 已经封装了physicals模块,可是我在使用的过程中遇到了一些问题,比方子弹速度过快时候会出屏等,所以就认为还是直接封装box2d API来完毕这款Demo。


    我主要封装了两个类,一个叫Box2dHandler, 这个类继承自Node和b2ContactListener, 是用来和box2d打交道的。能够用来创建方形,圆形的静态或者动态刚体。创建物理世界,以及处理碰撞的检測重任都交给这个类了。另外一个类叫B2Sprite, 也继承自Node,本来是想继承自Sprite的,可是在实现过程中发现有问题。就改成继承自Node。它的功能是用来粘合cocos2dx和Box2D, 起到一个中间的桥梁作用。这里要感谢一下<<cocos2d-x高级开发教程>>一书,这两个类基本从它移植而来,可是原书使用的是2.0版本号。所以还是要做一些改动。


    好了,闲话到此为止。上代码。

    首先是Box2dHandler。

    #ifndef __BOX2DHANDLER_H__
    #define __BOX2DHANDLER_H__
    
    #include "cocos2d.h"
    #include "Box2D.h"
    #include "Box2dHandlerDelegate.h"
    #include "B2Sprite.h"
    
    USING_NS_CC;
    
    enum shape
    {
    	box=1,
    	circle=2,
    };
    
    class Box2dHandler : public cocos2d::Node, public b2ContactListener
    {
    
    private:
    	b2World *m_world;
    	typedef std::pair<b2Fixture*, b2Fixture*> MyContact;
    	std::set<MyContact> m_contacts;
    public:
    	bool init();
    	bool initBox2D();
    	void addBodyForSprite(B2Sprite* sprite, double density = 1.0, double friction = 0.9, double restituion = 0.1, shape type=box);
    	void addFixtureForSprite(B2Sprite* sprite, double density = 1.0, double friction = 0.9, double restituion = 0.1, shape type=box);
    	void addStaticBodyForSprite(B2Sprite* sprite, double density = 0.0);
    	void dealCollision();
    
    public:
    	virtual void BeginContact(b2Contact * contact);
    	virtual void EndContact(b2Contact * contact);
    
    	static Box2dHandler * handler();
    	//void draw();
    	void update(float dt);
    	CC_SYNTHESIZE(Box2dHandlerDelegate*, m_delegate,  Delegate);
    	
    };
    
    #endif // __BOX2DHANDLER_H__
    

    熟悉box2d的话,非常easy看清楚这个类就是封装了主要的Box2D操作。包含创建刚体,以及监听碰撞。


    接下来是实现代码。

    #include "Box2dHandler.h"
    #include "HelloWorldScene.h"
    #define PTM_RATIO 32
    
    Box2dHandler * Box2dHandler::handler() 
    {
    	static Box2dHandler * handler = NULL;
    	if(handler == NULL)
    	{
    	   handler = new Box2dHandler();
    	   handler->init();
    	   return handler;
    	}
    	else
    	{
    	   return handler;
    	}
    }
    
    bool  Box2dHandler::init()
    {
    	this->initBox2D();
    	this->scheduleUpdate();
    	return true;
    }
    
    bool Box2dHandler::initBox2D()
    {
    	Size s = Director::getInstance()->getWinSize();
    	b2Vec2 gravity;
    	gravity.Set(0.0f, -10.0f);
    
    	m_world = new b2World(gravity);
    	m_world->SetAllowSleeping(true);
    	m_world->SetContinuousPhysics(true);
    	m_world->SetContactListener(this);
    
    	b2BodyDef groundBodyDef;
    	groundBodyDef.position.Set(0, 0);
    
    	b2Body* groundBody = m_world->CreateBody(&groundBodyDef);
    
    	b2EdgeShape groundBox;
    
    	//Bottom
    	//groundBox.Set(b2Vec2(0, 0), b2Vec2(s.width/PTM_RATIO, 0));
    	//groundBody->CreateFixture(&groundBox, 0);
    
    	//Top
    	groundBox.Set(b2Vec2(0, s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO, s.height/PTM_RATIO));
    	groundBody->CreateFixture(&groundBox, 0);
    
    	//Left
    	groundBox.Set(b2Vec2(0, s.height/PTM_RATIO), b2Vec2(0,0));
    	groundBody->CreateFixture(&groundBox, 0);
    
    	//Right
    	groundBox.Set(b2Vec2(s.width/PTM_RATIO, s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO, 0));
    	groundBody->CreateFixture(&groundBox, 0);
    
    	return true;
    }
     
    void Box2dHandler::addFixtureForSprite(B2Sprite* sprite, double density, double friction, double restitution, shape type)
    {
    	b2PolygonShape spriteShape;
    	Size size = sprite->getB2Sprite()->getContentSize() * sprite->getScale();
    	spriteShape.SetAsBox(size.width / PTM_RATIO / 2, size.height / PTM_RATIO / 2);
    
    	b2CircleShape circle;
    	circle.m_radius = (sprite->getB2Sprite()->getContentSize().width * sprite->getScale())/2/PTM_RATIO;
    
    
    	b2FixtureDef spriteShapeDef;
    	if(type == box)
    	    spriteShapeDef.shape = &spriteShape;
    	else
    		spriteShapeDef.shape = &circle;
    	spriteShapeDef.density = density;
    	spriteShapeDef.restitution = restitution;
    	spriteShapeDef.friction = friction;
    
    	b2Body * spriteBody = sprite->getB2Body();
    	spriteBody->CreateFixture(&spriteShapeDef);
    }
    
    void Box2dHandler::addBodyForSprite(B2Sprite* sprite, double density, double friction, double restitution, shape type)
    {
    	b2BodyDef spriteBodyDef;
    	spriteBodyDef.type = b2_dynamicBody;
    	spriteBodyDef.position.Set(sprite->getPosition().x / PTM_RATIO, sprite->getPosition().y / PTM_RATIO);
    	spriteBodyDef.userData = sprite;
    
    	b2Body* spriteBody = m_world->CreateBody(&spriteBodyDef);
    	sprite->setB2Body(spriteBody);
    	this->addFixtureForSprite(sprite, density, friction, restitution, type);
    }
    
    void Box2dHandler::addStaticBodyForSprite(B2Sprite* sprite, double density)
    {
    	b2BodyDef spriteBodyDef;
    	spriteBodyDef.type = b2_staticBody;
    	spriteBodyDef.position.Set(sprite->getPosition().x / PTM_RATIO, sprite->getPosition().y / PTM_RATIO);
    	//spriteBodyDef.userData = sprite;
    
    	b2Body* spriteBody = m_world->CreateBody(&spriteBodyDef);
    	sprite->setB2Body(spriteBody);
    	this->addFixtureForSprite(sprite, density);
    }
    
    void Box2dHandler::update(float dt)
    {
    	m_world->Step(dt, 8, 8);
    	std::vector<b2Body*> toDestory;
        for(b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
    	{
    		if(b->GetUserData() != NULL)
    		{
    			B2Sprite* sprite = static_cast<B2Sprite*>(b->GetUserData());
    			b2Vec2 pos = b->GetPosition();
    			float rotation = b->GetAngle() / 0.01745329252;
    			sprite->setPosition(pos.x * PTM_RATIO, pos.y * PTM_RATIO);
    			sprite->setRotation(rotation);
    			if(b->GetPosition().y*PTM_RATIO<= -25)
    			{
    				toDestory.push_back(b);
    			}
    		}
    	}
    
    	if(toDestory.size()>0)
    	{
    		for(int i = 0; i < toDestory.size(); i++)
    		{
    			B2Sprite* sp=static_cast<B2Sprite*>( toDestory.at(i)->GetUserData());
    			if(sp != NULL)
    				sp->removeFromParentAndCleanup(true);
    			m_world->DestroyBody(toDestory.at(i));
    		}
    		toDestory.clear();
    	}
    
         this->dealCollision();
    }
    
    void Box2dHandler::BeginContact(b2Contact * contact)
    {
    	CCLog("start");
    	B2Sprite* spa = static_cast<B2Sprite*>(contact->GetFixtureA()->GetBody()->GetUserData());
    	B2Sprite* spb = static_cast<B2Sprite*>(contact->GetFixtureB()->GetBody()->GetUserData());
    
    	if(spa != NULL && spb != NULL)
    	{
    	   MyContact myContact(contact->GetFixtureA(), contact->GetFixtureB());
    	   m_contacts.insert(myContact);
    	}
    }
    
    void Box2dHandler::EndContact(b2Contact* contact)
    {
    	CCLog("end");
    	MyContact myContact(contact->GetFixtureA(), contact->GetFixtureB());
    	m_contacts.erase(myContact);
    }
    
    void Box2dHandler::dealCollision()
    {
    	if(m_delegate != NULL && m_contacts.size()>0)
    	{
    		std::set<MyContact>::iterator it;
    		for(it = m_contacts.begin(); it != m_contacts.end(); ++it)
    		{
    			B2Sprite* bullet = static_cast<B2Sprite*>(it->first->GetBody()->GetUserData());
    			B2Sprite* actor = static_cast<B2Sprite*>(it->second->GetBody()->GetUserData());
    			if(bullet->getTag() == kTagBulletBase && (actor->getTag() == kTagRedEnemy  || actor->getTag() == kTagBlueEnemy || actor->getTag() == kTagYellowEnemy))
    			    m_delegate->CollisionEvent(bullet, actor);
    			else if((bullet->getTag() == kTagRedEnemy  || bullet->getTag() == kTagBlueEnemy || bullet->getTag() == kTagYellowEnemy) && actor->getTag() == kTagBulletBase )
    				m_delegate->CollisionEvent(actor, bullet);
    		}
    	}
    	m_contacts.clear();
    }

    简单解释下:

    Box2dHandler * Box2dHandler::handler() 
    构造函数,内存管理交给cocos2dx

    bool Box2dHandler::initBox2D()
    设置好重力场,创建物理世界m_world, 以及定义好屏幕边界为可碰撞的静态刚体。

    void Box2dHandler::addBodyForSprite(B2Sprite* sprite, double density, double friction, double restitution, shape type)

    创建动态刚体,有方形和圆形两种选择。

    void Box2dHandler::addFixtureForSprite(B2Sprite* sprite, double density, double friction, double restitution, shape type)

    为刚体创建外观并设置刚体属性。

    void Box2dHandler::update(float dt)
    更新物理世界,并同步刚体位置到cocos2dx中精灵,当中的桥梁就是B2Sprite。

    void Box2dHandler::BeginContact(b2Contact * contact)
    void Box2dHandler::EndContact(b2Contact* contact)
    物理碰撞检測的回调方法。

    void Box2dHandler::dealCollision()
    自己定义的碰撞处理方法。


    就这么多。非常easy明了。接下来看看B2Sprite的代码

    #include "cocos2d.h"
    #include "Box2D.h"
    USING_NS_CC;
    
    enum Enemy_Color
    {
    	k_red = 0,
    	k_blue = 1,
    	k_yellow = 2,
    };
    
    class B2Sprite : public cocos2d::Node
    {
    public:
    
    	static B2Sprite* create(CCTexture2D * texture);
    	static B2Sprite* create(const char* pngFile);
    	bool init(const char* pngFile);
    	bool init(CCTexture2D* texture);
    	CCActionInterval* createAnimation(const char* plist,int frames);
    	CCActionInterval* createZombileAnimation(const char* plist, int frames, Enemy_Color color);
    
    	static B2Sprite* create(const char* plist, int frames, Enemy_Color color);
    	bool init(const char* plist,  int frames, Enemy_Color color);
    
    	CC_SYNTHESIZE_READONLY(Sprite*, m_sprite, B2Sprite);
    	CC_SYNTHESIZE(b2Body*, m_b2Body, B2Body); // 物理实际的“物体”
    	CC_SYNTHESIZE(bool, m_isDead, IsDead);
    	CC_SYNTHESIZE(bool, m_isAlive, IsAlive);
    };
    
    #endif

    主要内容在这里

    CC_SYNTHESIZE_READONLY(Sprite*, m_sprite, B2Sprite);
    CC_SYNTHESIZE(b2Body*, m_b2Body, B2Body); // 物理实际的“物体”
    我们能够看到B2Sprite一方面挂接了一个Sprite用于显示刚体,另外一方面有挂接了一个刚体对象m_b2Body, 所以它就起着一个桥梁的作用。


    实现代码就不给出了。由于都是些细节性的方法,是用来写这个Demo的。大家全然能够依据自己须要来封装自己的B2Sprite版本号。


    好了完毕了这两个类的封装。接下来就是完毕我们的Demo了。


    我建立一个叫HelloWorldScene的Layer来承载这个游戏Demo。

    #ifndef __HELLOWORLD_SCENE_H__
    #define __HELLOWORLD_SCENE_H__
    
    #include "cocos2d.h"
    #include "Box2D.h"
    #include "VisibleRect.h"
    #include "cocos-ext.h"
    #include "Box2dHandler.h"
    using namespace cocos2d;
    
    enum {
        kTagParentNode = 1,
    	kTagBulletParentNode=2,
    	kTagHandler = 3,
    	kTagFloor = 100,
    	kTagFloor2 = 101,
    	kTagRedEnemy = 102,
    	kTagBlueEnemy = 103,
    	kTagYellowEnemy = 104,
    	kTagBulletBase = 500,
    };
    
    
    class HelloWorld : public cocos2d::Layer, public Box2dHandlerDelegate
    {
    public:
        // there's no 'id' in cpp, so we recommend returning the class instance pointer
           static cocos2d::Scene* createScene();
            HelloWorld();
    	~HelloWorld();
    
    	// Touch process
    	 bool onTouchBegan(Touch* touch, Event* pEvent);
    	 void onTouchEnded(Touch* touch, Event* pEvent);
    
    	 virtual void CollisionEvent(B2Sprite*, B2Sprite*);
    	 void HelloWorld::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity);
    
    };
    
    #endif // __HELLOWORLD_SCENE_H__

    我将创建刚体的任务都放在构造函数中,CollisionEvent是处理碰撞的回调方法。LauchBomb是发射子弹的方法。

    接下来看实现。

    #include "HelloWorldScene.h"
    
    #define PTM_RATIO 32
    USING_NS_CC;
    
    
    Scene* HelloWorld::createScene()
    {
        auto scene = Scene::create();
        auto layer =  new HelloWorld();
        scene->addChild(layer);
    	layer->release();
        return scene;
    }
    
    HelloWorld::HelloWorld() 
    {
    	auto dispatcher = Director::getInstance()->getEventDispatcher();
    	auto touchListener = EventListenerTouchOneByOne::create();
    	touchListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
    	//touchListener->onTouchMoved = CC_CALLBACK_2(MapLayer::onTouchMoved, this);
    	touchListener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
    	dispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
    
    	Sprite* bg = Sprite::create("bg3.png");
    	addChild(bg,  -1);
    	bg->setPosition(ccp( VisibleRect::center().x, VisibleRect::center().y));
    
    	Box2dHandler * handler = Box2dHandler::handler();
    	handler->setDelegate(this);
    	handler->setTag(kTagHandler);
    	this->addChild(handler);
    	
    	B2Sprite * floor = B2Sprite::create("floor.png");
    	floor->setTag(kTagFloor);
    	floor->setPosition(300, 350);
    	handler->addStaticBodyForSprite(floor);
    	addChild(floor);
    
    
    	B2Sprite * floor2 = B2Sprite::create("floor2.png");
    	floor2->setTag(kTagFloor2);
    	floor2->setPosition(450, 250);
    	handler->addStaticBodyForSprite(floor2);
    	addChild(floor2);
    
    	B2Sprite * redEnemy = B2Sprite::create("Zombie", 16, k_red);
    	redEnemy->setTag(kTagRedEnemy);
    	redEnemy->setPosition(442,500);
    	handler->addBodyForSprite(redEnemy);
    	addChild(redEnemy);
    
    	B2Sprite * blueEnemy = B2Sprite::create("Zombie", 16, k_blue);
    	blueEnemy->setTag(kTagBlueEnemy);
    	blueEnemy->setPosition(310,500);
    	handler->addBodyForSprite(blueEnemy);
    	addChild(blueEnemy);
    
    	B2Sprite * yellowEnemy = B2Sprite::create("Zombie", 16, k_yellow);
    	yellowEnemy->setTag(kTagYellowEnemy);
    	yellowEnemy->setPosition(330,500);
    	handler->addBodyForSprite(yellowEnemy);
    	addChild(yellowEnemy);
    }
          
    
    HelloWorld::~HelloWorld()
    {
    }
    
    void HelloWorld::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity)
    {
    	B2Sprite * sprite = B2Sprite::create("bullet.png");
    	sprite->setScale(0.3);
    	sprite->setPosition(ccp(position.x, position.y));
    	Box2dHandler * handler = (Box2dHandler*)(this->getChildByTag(kTagHandler));
    	handler->addBodyForSprite(sprite, 1.0, 0.3, 0.8, circle);
    	sprite->getB2Body()->SetLinearVelocity(velocity);
    	sprite->getB2Body()->SetBullet(true);
    	sprite->setTag(kTagBulletBase);
    	addChild(sprite);
    }
    
    bool HelloWorld::onTouchBegan(Touch* touch, Event* pEvent)
     {
    	  return true;
     }
    
     void HelloWorld::onTouchEnded(Touch* touch, Event* pEvent)
     { 
    	 Point p = touch->getLocation();	
    	 b2Vec2 target(p.normalize().x * 100, p.normalize().y*100);
    	 b2Vec2 v = target;
    	 b2Vec2 source(0, 0);
         LaunchBomb(source, v);
     }
    
     // a is bullet
      void HelloWorld::CollisionEvent(B2Sprite*a, B2Sprite*b)
      {
    	  if(a->getPositionX() < b->getPositionX())
    		  b->getB2Body()->ApplyLinearImpulse(b2Vec2(100,0), b->getB2Body()->GetPosition(), true);
    	  else if(a->getPositionX() > b->getPositionX())
    		  b->getB2Body()->ApplyLinearImpulse(b2Vec2(-100,0),  b->getB2Body()->GetPosition(), true);
    
      }


    HelloWorld::HelloWorld() 
    中我放置了两个镜头刚体作为floor, 然后在上面放了几个敌人。当然,假设要做一个正式的游戏,关卡数据要在外面编辑好,然后读取进来。这里只写个Demo,就没有编辑关卡了。

    void HelloWorld::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity)
    创建了一个子弹刚体。这种方法在每次触摸屏幕都会触发。

      void HelloWorld::CollisionEvent(B2Sprite*a, B2Sprite*b)
    碰撞检測回调方法,这里做的处理是: 假设子弹和敌人产生碰撞,假设敌人在子弹坐标,就给它一个水平向左的冲量,反之给它一个向右的冲量,让它掉下平台。


    能够看到,真正的Demo代码是很少的。


    接下来上图。



    设计一颗子弹






    好了,这就是本章的所有内容。源代码已经上传到群 216208142 空间,有须要的读者能够加群来获取。



  • 相关阅读:
    乔治·奥威尔的六条有效写作的规则
    读书:《个人形成论》 Carl R. Rogers
    想想体制性的生存法则
    每一个山峰都建立在同一座山上
    读书笔记:这些道理没有人告诉过你(二)
    举国的不仅仅是运动员
    参加了一个社会化营销策划比赛整理一下参考资料
    读书:《个人形成论》2 Carl R. Rogers
    前端避坑指南丨辛辛苦苦开发的 APP 竟然被判定为简单网页打包?
    Entify Framework 4.1[问题集] 一个实体的双向依赖引起的错误
  • 原文地址:https://www.cnblogs.com/blfshiye/p/3766384.html
Copyright © 2011-2022 走看看