Developers Manual > Cocos2d-x > Modules > Physics > Box2D

Box2D

Box2D is a 2D rigid body simulation library for games. Cocos2D-x programmers can use it in their games to make objects move in believable ways and make the game world more interactive.

Box2D is written in portable C**. The following tutorial, written by Ray Wenderlich, shows how to use Box2D in your game. The original article is “How To Create A Breakout Game with Box2D and Cocos2D”:
Part 1, Part 2.
And here, we only translate Objective-C into C** in this sample code. You can download from: https://github.com/clawoo/BreakoutCocos2D-x.

The Box2D World

The first thing you need to do is to create a world object for box2d within cocos2d-x. The world object is the main object in Box2D that manages all of the physics bodies and simulations.

When we created a world object, we also need to specify an initial gravity vector.

// Define the gravity vector.
b2Vec2 gravity;
gravity.Set(0.0f, 0.0f);//No gravity

// Do we want to let bodies sleep?
bool doSleep = true;

// create a world object, which will hold and simulate the rigid bodies.
_world = new b2World(gravity, doSleep);

Once we’ve created the world object, we can then add some bodies to the world. Bodies can represent any object that moves around like ninja stars or monsters, there are also static bodies that don’t moves such as platforms or walls.

Create a body

There are only 5 steps you need to do to create a body – create a body definition, a body object, a shape, a fixture definition, and a fixture object.

You firstly create a body definition to specify initial properties of the body such as position or velocity.

b2Body *_paddleBody;

Once you set that up, you can use the world object to create a body object by specifying the body definition.

// Create paddle body
    b2BodyDef paddleBodyDef;
    paddleBodyDef.type = b2_dynamicBody;
    paddleBodyDef.position.Set(winSize.width/2/PTM_RATIO, 50/PTM_RATIO);
    paddleBodyDef.userData = paddle;
    _paddleBody = _world->CreateBody(&paddleBodyDef);

You then create a shape representing the geometry you wish to simulate.

// Create shape definition and add to body
    b2FixtureDef paddleShapeDef;
    paddleShapeDef.shape = &paddleShape;
    paddleShapeDef.density = 10.0f;
    paddleShapeDef.friction = 0.4f;
    paddleShapeDef.restitution = 0.1f;

You then create a fixture definition – you set the shape of the fixture definition to be the shape you created, and set other properties such as density or friction.

    b2Fixture *_paddleFixture;

Finally you can use the body object to create a fixture object by specifying the fixture definition.

    _paddleFixture = _paddleBody->CreateFixture(&paddleShapeDef);

Note that you can add many fixture objects to a single body object. This can comes in handy when creating complex objects.
Once you’ve added the bodies you like to your world, usually we use “Box2D can do its magic on simulation – as long as you call its “Step” function periodically so it has processing time. So let’s see the following code in “tick” method:

int velocityIterations = 8;
int positionIterations = 1;

// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
_world->Step(dt, velocityIterations, positionIterations);

    bool blockFound = false;

// Iterate over the bodies in the physics world
for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL) {
// Synchronize the AtlasSprites position and rotation with the corresponding body
CCSprite* myActor = (CCSprite*)b->GetUserData();
myActor->setPosition( CCPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO) );
myActor->setRotation( -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()) );

        if (myActor->getTag() == 1) 
        {
            static int maxSpeed = 10;

            b2Vec2 velocity = b->GetLinearVelocity();
            float32 speed = velocity.Length();

            if (speed > maxSpeed) {
                b->SetLinearDamping(0.5);
            } else if (speed < maxSpeed) {
                b->SetLinearDamping(0.0);
            }

        }

            if (myActor->getTag() == 2) {
                blockFound = true;
            }

}

We use radians in Box2D(π radians = 180 degrees, so X radians * 180/π gives the same angle in degrees),so CC_RADIANS_TO_DEGREES change radians into degrees.
We call Step function here.You can set the two parameters(velocityIterations and positionIterations) to lower or higher. The consequences of setting it lower is less fine detail in the simulation, but a possible speed increase.

Next,we iterate through all of the bodies in the world looking for those with user data set. Once we find them we update the position and angle of the sprite to match the physics simulation.

Box2D and Collisions

To find out when a fixture collides with another fixture in Box2D, we need to register a contact listener. A contact listener is a C++ object that we give Box2D, and it will call methods on that object to let us know when two objects begin to touch and stop touching. we can’t just store references to the contact points that are sent to the listener, because they are reused by Box2D. So we have to store copies of them instead.

void MyContactListener::BeginContact(b2Contact* contact) 
{
    // We need to copy out the data because the b2Contact passed in
    // is reused.
    MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() };
    _contacts.push_back(myContact);
}

The following iterates through all of the buffered contact points, and checks to see if any of them are a match between the ball and the bottom of the screen.

void MyContactListener::EndContact(b2Contact* contact) 
{
    MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() };
    std::vector::iterator pos;
    pos = std::find(_contacts.begin(), _contacts.end(), myContact);
    if (pos != _contacts.end()) 
    {
        _contacts.erase(pos);
    }
}

In the Helloworld.cpp,“tick” method wil use _contactListener to go through the contact points of bodies that are colliding. If a sprite is intersecting with a block, we add the block to a list of objects to destroy.

b2Body *bodyA = contact.fixtureA->GetBody();
b2Body *bodyB = contact.fixtureB->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();

// Sprite A = ball, Sprite B = Block
if (spriteA->getTag() == 1 && spriteB->getTag() == 2) {
    if (std::find(toDestroy.begin(), toDestroy.end(), bodyB) 
             == toDestroy.end()) {
              toDestroy.push_back(bodyB);
      }
}
// Sprite B = block, Sprite A = ball
else if (spriteA->getTag() == 2 && spriteB->getTag() == 1) {
            if (std::find(toDestroy.begin(), toDestroy.end(), bodyA) 
            == toDestroy.end()) {
            toDestroy.push_back(bodyA);
       }
}        

Now you have even more techniques to add to Box2D in your game. I look forward to seeing some cool physics games from you guys!

External Blogs

Cocos2d-x Box2D iOS/Android Hybrid Tutorial by Alex.

Copyright © 2010 - 2014 Cocos2d-x.orgClustrmaps Support Email: support@cocos.org