Search code examples
cocos2d-x

Box2d body generation dynamically in cocos2dx using c++ in Xcode


I am a new developer in cocos2dx.I am developing a game in which i am using the box2d bodies that are loading through physics editor.There are more than 20 bodies using in a level of the game,that i am making separate body for separate sprite attached with them and the similar bodies are used in the other levels and there are 50 levels in the game and for each level,i have made separate class and again making the b2body loading function,all the code is working properly but I just want to make a generic function for loading the bodies in a class so that i can use the same b2body loading function in all the levels.Also i have to destroy the particular body and the sprite on touching the sprite

//Sprites:
rect_sprite1=CCSprite::create("rect1.png");
rect_sprite1->setScaleX(rX);
rect_sprite1->setScaleY(rY);
this->addChild(rect_sprite1,1);

rect_sprite2=CCSprite::create("rect2.png");
rect_sprite2->setScaleX(rX);
rect_sprite2->setScaleY(rY);
this->addChild(rect_sprite2,1);

rect_sprite3=CCSprite::create("circle.png");
rect_sprite3->setScale(rZ);
this->addChild(rect_sprite3,1);

GB2ShapeCache::sharedGB2ShapeCache()->addShapesWithFile("obs.plist");

//body loading function

void Level1::addNewSpriteWithCoords()
{
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
b2BodyDef bodyDef1;
bodyDef1.type=b2_dynamicBody;
bodyDef1.position.Set((winSize.width*0.38)/PTM_RATIO,(winSize.height*0.4) /PTM_RATIO);
bodyDef1.userData = rect_sprite1;
body1 = (MyPhysicsBody*)world->CreateBody(&bodyDef1);
body1->setTypeFlag(7);
// add the fixture definitions to the body
GB2ShapeCache *sc1 = GB2ShapeCache::sharedGB2ShapeCache();
sc1->addFixturesToBody(body1,"rect1", rect_sprite1);
rect_sprite1->setAnchorPoint(sc1->anchorPointForShape("rect1"));

b2BodyDef bodyDef2;
bodyDef2.type=b2_dynamicBody;
bodyDef2.position.Set((winSize.width*0.62)/PTM_RATIO,  
(winSize.height*0.4)/PTM_RATIO);
bodyDef2.userData = rect_sprite2;
body2 = (MyPhysicsBody*)world->CreateBody(&bodyDef2);
body2->setTypeFlag(7);
// add the fixture definitions to the body
GB2ShapeCache *sc2 = GB2ShapeCache::sharedGB2ShapeCache();
sc2->addFixturesToBody(body2,"rect2", rect_sprite2);
rect_sprite2->setAnchorPoint(sc2->anchorPointForShape("rect2"));

b2BodyDef bodyDef3;
bodyDef3.type=b2_dynamicBody;
bodyDef3.position.Set((winSize.width*0.5)/PTM_RATIO, (winSize.height*0.23)/PTM_RATIO);
bodyDef3.userData = rect_sprite3;
body3 = (MyPhysicsBody*)world->CreateBody(&bodyDef3);
body3->setTypeFlag(7);
// add the fixture definitions to the body
GB2ShapeCache *sc3 = GB2ShapeCache::sharedGB2ShapeCache();
sc3->addFixturesToBody(body3,"circle", rect_sprite3);
rect_sprite3->setAnchorPoint(sc3->anchorPointForShape("circle"));
}
void Level1::ccTouchesBegan(cocos2d::CCSet* touches, cocos2d::CCEvent* event)
{

if(box->containsPoint(touchPoint))
                {
                    this->removeChild(((CCSprite*)rect),true);
                    if(((CCSprite*)rect)==rect_sprite1)
                    {
                        rect_sprite1=NULL;
                        world->DestroyBody(body1);
                        Utils::setCount(Utils::getCount()-1);

                    }
                    if(((CCSprite*)rect)==rect_sprite2)
                    {
                        rect_sprite2=NULL;
                        world->DestroyBody(body2);
                        Utils::setCount(Utils::getCount()-1);
                    }


                    if(((CCSprite*)rect)==rect_sprite3)
                    {
                        rect_sprite3=NULL;
                        world->DestroyBody(body3);
                        Utils::setCount(Utils::getCount()-1);

                    }

} 

Similarly i am doing for other levels. If anyone know about it,please suggest.Thanks


Solution

  • This seems more like a "What design pattern should I use?" than a specific problem with loading the code.

    In general, when I want to create "Entities" that require a physical body, I use a base class that contains the Box2D body as one of its members. The base class is a container for the body (which is assigned to it) and is responsible for destroying the body (removing it from the Box2D world) when the Entity is destroyed.

    Derived classes can load the body from the Box2D shape cache. This is best shown through an example. There is a game I am working on where I have a swarm of different shaped asteroids circling a sun. Here is a screen shot:

    enter image description here

    The base class, Entity, contains the body and destroys it when the Entity is destroyed:

    class Entity : public HasFlags
    {
    public:
    
       enum
       {
          DEFAULT_ENTITY_ID = -1,
       };
    private:
       uint32 _ID;
       // Every entity has one "main" body which it
       // controls in some way.  Or not.
       b2Body* _body;
       // Every entity has a scale size from 1 to 100.
       // This maps on to the meters size of 0.1 to 10
       // in the physics engine.
       uint32 _scale;
    
    protected:
       void SetScale(uint32 value)
       {
          assert(value >= 1);
          assert(value <= 100);
          _scale = value;
       }
    
    public:
    
       void SetBody(b2Body* body)
       {
          assert(_body == NULL);
          if(_body != NULL)
          {
             CCLOG("BODY SHOULD BE NULL BEFORE ASSIGNING");
             _body->GetWorld()->DestroyBody(_body);
             _body = NULL;
          }
          _body = body;
          if(body != NULL)
          {
             _body->SetUserData(this);
             for (b2Fixture* f = _body->GetFixtureList(); f; f = f->GetNext())
             {
                f->SetUserData(this);
             }
          }
       }
    
    
    
       inline void SetID(uint32 ID)
       {
          _ID = ID;
       }
    
       inline uint32 GetID() const
       {
          return _ID;
       }
    
       virtual string ToString(bool updateDescription = false)
       {
          string descr = "ID: ";
          descr += _ID;
          descr += "Flags: ";
          if(IsFlagSet(HF_IS_GRAPH_SENSOR))
             descr += "IS_FLAG_SENSOR ";
          return descr;
       }
    
    
       Entity() :
       _ID(DEFAULT_ENTITY_ID),
       _body(NULL),
       _scale(1)
       {
       }
    
       Entity(uint32 flags, uint32 scale) :
       HasFlags(flags),
       _ID(DEFAULT_ENTITY_ID),
       _body(NULL),
       _scale(scale)
       {
    
       }
    
       virtual void Update()
       {
    
       }
    
       virtual void UpdateDisplay()
       {
    
       }
    
       virtual ~Entity()
       {
          if(_body != NULL)
          {
             _body->GetWorld()->DestroyBody(_body);
          }
       }
    
       inline static float32 ScaleToMeters(uint32 scale)
       {
          return 0.1*scale;
       }
    
       inline Body* GetBody()
       {
          return _body;
       }
    
       inline const Body* GetBody() const
       {
          return _body;
       }
    
       inline uint32 GetScale()
       {
          return _scale;
       }
    
       inline float32 GetSizeMeters()
       {
          return ScaleToMeters(_scale);
       }
    };
    

    The Asteroid class itself is responsible for loading one of several different "Asteroid" shapes from the shape cache. However, ALL the asteroids have common logic for making them move about the center of the screen. They have a rope joint attached and the Update(...) function adds some "spin" to them so they rotate around the center:

     class Asteroid : public Entity
        {
        private:
           b2Fixture* _hull;
           Vec2 _anchor;
           CCSprite* _sprite;
           float32 _targetRadius;
        public:
           // Some getters to help us out.
           b2Fixture& GetHullFixture() const { return *_hull; }
           float32 GetTargetRadius() { return _targetRadius; }
           CCSprite* GetSprite() { return _sprite; }
    
    
           void UpdateDisplay()
           {
              // Update the sprite position and orientation.
              CCPoint pixel = Viewport::Instance().Convert(GetBody()->GetPosition());
              _sprite->setPosition(pixel);
              _sprite->setRotation(-CC_RADIANS_TO_DEGREES(GetBody()->GetAngle()));
           }
    
           virtual void Update()
           {
              Body* body = GetBody();
    
              Vec2 vRadius = body->GetPosition();
              Vec2 vTangent = vRadius.Skew();
    
              vTangent.Normalize();
              vRadius.Normalize();
    
    
              // If it is not moving...give it some spin.
              if(fabs(vTangent.Dot(body->GetLinearVelocity())) < 1)
              {
                 body->SetLinearDamping(0.001);
                 body->ApplyForceToCenter(body->GetMass()*1.5*vTangent);
                 body->ApplyForce(vRadius,body->GetMass()*0.05*vRadius);
              }
              else
              {
                 body->SetLinearDamping(0.05);
              }
           }
    
           ~Asteroid()
           {
    
           }
    
           Asteroid() :
           Entity(HF_CAN_MOVE | HF_UPDATE_PRIO_5,50)
           {
    
           }
    
           bool Create(b2World& world, const string& shapeName,const Vec2& position, float32 targetRadius)
           {
              _targetRadius = targetRadius;
              _anchor = position;
    
              string str = shapeName;
              str += ".png";
              _sprite = CCSprite::createWithSpriteFrameName(str.c_str());
              _sprite->setTag((int)this);
              _sprite->setAnchorPoint(ccp(0.5,0.5));
    
              //      _sprite->setVisible(false);
    
              b2BodyDef bodyDef;
              bodyDef.position = position;
              bodyDef.type = b2_dynamicBody;
              Body* body = world.CreateBody(&bodyDef);
              assert(body != NULL);
    
              // Add the polygons to the body.
              Box2DShapeCache::instance().addFixturesToBody(body, shapeName, GetSizeMeters());
    
              SetBody(body);      
              return true;
           }
    
        };
    

    The Create(...) function for the Asteroid is called from the loading code in the scene, which could easily be a .csv file with the shape names in it. It actually reuses the names several times (there are only about 10 asteroid shapes).

    You will notice that the Asteroid also has a CCSprite associated with it. Not all Entities have a sprite, but some do. I could have created an Entity-Derived class (EntityWithSprite) for this case so that the sprite could be managed as well, but I try to avoid too many nested classes. I could have put it into the base class, and may still. Regardless, the Asteroids contain their own sprites and load them from the SpriteCache. They are updated in a different part of the code (not relevant here, but I will be happy to answer questions about it if you are curious).

    NOTE: This is part of a larger code base and there are features like zooming, cameras, graph/path finding, and lots of other goodies in the code base. Feel free to use what you find useful.

    You can find all the (iOS) code for this on github and there are videos and tutorials posted on my site here.