How to use physics with cocos2d-x

How to use physics with cocos2d-x

In this tutorial you are going to learn:

  • How physics enabled games work in cocos2d-x
  • How to create physics collision shapes for cocos2d-x
  • How to load the shapes in your game
  • How to set up a simple scene with dropping objects
game scene fs8

What you are going to learn from this tutorial

In this tutorial you are going to learn:

  • How physics enabled games work in cocos2d-x

  • How to create physics collision shapes for cocos2d-x

  • How to load the shapes in your game

  • How to set up a simple scene with dropping objects

I assume that you already have basic knowledge about cocos2d-x. If not please check out our tutorial Using sprite sheet animations in cocos2d-x — it explains the basics setting up a simple game scene.

The complete source code for this tutorial is available on github.

This screenshot shows you what the demo project from the tutorial looks like:

There's a bunch of different objects that you can drop on the scene by tapping / clicking in the game scene.

The objects fall down and nicely collide with other objects.

How do I enable the physics in cococs2d-x?

The nice thing about cocos2d-x is that it already comes with everything you need to create cool games. This includes a physics engine called Chipmunk.

So everything you need to get the engine running is to initialize it during scene creation:

auto scene = Scene::createWithPhysics();

The next step is to set the gravity vector (if you want to enable gravity):

scene->getPhysicsWorld()->setGravity(Vec2(0, -900));

You can also enable debug drawing during — which is quite helpful to see if your collision shapes are created as you expect them to be or if you expect some strange behaviour:

// optional: enable debug draw
scene->getPhysicsWorld()->setDebugDrawMask(0xffff);

With this setup your, are ready to add sprites to your scene:

auto sprite = Sprite::create("banana.png");
sprite->setPhysicsBody(body);
sprite->setPosition(pos);
addChild(sprite);

The important line of code is the sprite->setPhysicsBody(body) — it converts your normal sprite into a shape controlled by the simulation.

But where to get the PhysicsBody body from?

cocos2d-x contains several constructors for PhysicsBody objects:

  • PhysicsBody::createCircle — which creates a circle shape with a given radius

  • PhysicsBody::createBox — which creates a rectangle shape with a given size

  • PhysicsBody::createPolygon — which creates collision polygon

Creating a circle or box is simple — but creating the collision shapes for a complex object is not. Especially since the polygon has to meet 2 conditions:

  • The vertices of the polygon have to be in clockwise winding.

  • The polygon must be convex.

This means that every convex polygon is ok. But concave polygons have to be split into multiple convex polygons:

Convex decomposition of a concave polygon

Yes — you are right — this is a tedious job. I personally got fed up with it directly after creating my first object...

How to create collision shapes — the easy way

We've developed an application for you to simplify the whole work: It's called PhysicsEditor. The editor looks like this:

Creating shapes with PhysicsEditor

And you can download it from here — It's available for Windows, MacOS and Linux:

PhysicsEditor costs some bucks but believe me: You'll love it. Instead of creating the shapes manually it'll really help you speed up your development. It comes with a 7 days trial — long enough to get through this tutorial.

After installing PhysicsEditor you'll first have to choose the framework you want to develop with — which is of course cocos2d-x . Choose it from the Exporter settings in the top right panel.

Now drag & drop your sprites on the left panel.

Creating shapes manually

Let's create some shapes manually — before doing it all automatically. E.g. let's start with the crate.png from the demo project.

Click on the Polygon shape in the toolbar to create a triangle.

Editing collision polygons for cocos2d-x

Basic editing:

  • You can drag the whole shape or just the vertices with the left mouse button
  • Double-click near a line to add a vertex
  • Double-click a vertex to remove it
  • CMD / CTRL click near a line to add a vertex
  • CMD / CTRL click a vertex to remove it
  • Use ALT + Mousewheel to zoom in / out.
  • Hold Space and drag with the mouse to pan the view while zoomed in

You can also add multiple fixtures to a single body to build more complex objects. Use the Circle tool to add circles.

The small blue circle with the cross is the pivot / anchor point of your sprite. It's applied to the sprite and can be used for aligning and positioning the sprite in your game scene.

This is already a big improvement over creating the shapes by looking up coordinates in a graphics tool. But it gets even better....

Creating shapes automatically

The Shape tracer reduces your work even more:

Automatically generating physics shapes

The most important setting here is the Tolerance . It allows you to control the shape creation.

It's now an act of balance: Find the best tolerance value that gives you a good shape coverage while keeping the vertex count low.

bad not enough fs8

Not enough vertices, bad shape coverage.

bad too many fs8

Good shape coverage, but way too many vertices.

good fs8

Good shape coverage, good amount of vertices.

Setting physics parameters

There are 2 levels of physics parameters: Body and fixture level. The body level parameters effect the whole body and all fixtures inside. The fixture level parameters only effect the fixture itself.

Body parameters

  • Dynamic body
    This flag is required if you want the shape to move. If this is ticked off the shape participates in collision detection but never moves. You can use this for e.g. floor, walls, obstacles.
  • Affected by gravity
    The body is accelerated by the gravity in the game scene.
  • Allows rotation
    The body can rotate. You usually want this enabled to get a realistic behaviour of objects. You might not want to enable it on your game character to keep his head up all the time.
  • Linear damping
    Reduce the speed of a shape over time.
  • Velocity limit
    Use this to set a maximum speed for the object.
  • Angular damping
    Reduce rotation of an object over time.
  • Angular velocity limit
    Sets the maximum rotation speed of a body

Fixture parameters

These parameters belong to only 1 part of the body.

  • Density
    Controls how heavy an object is: The higher the density value and the bigger the object the heavier it is.
  • Restitution
    This is the bounciness of the fixture. Higher values let objects bounce off from others.
  • Friction
    Reduce the sprite's speed if it glides over other objects.
  • Tag
    The tag can be used to identify a specific fixture — e.g. a character's head or feet.
  • Collision group
    Only bodies in the same group can collide with each other. You can use this to layer your game scene.
  • The bit masks
    Each fixture has 3 bit masks which help filter what happens in case of collisions:
    • Category - defines what this fixture is
    • Collision - defines what this object collides with — and creates a physical reaction
    • Contact - defines which collisions report contact

This allows you to create fixtures for different purposes:

  • Object that collide with others — e.g. the fruit shapes from the demo project.

  • Object that collide and report collisions — e.g. a bullet hitting an enemy.

  • Sensors that don't collide but report contact with a certain type of other object. You can use this to trigger events if the hero passes a certain spot in a game scene. Or you can make the hero look up if something is dropping going to drop onto his head.

These parameters are all stored as part of the data file PhysicsEditor creates for you. The parameters are automatically applied as soon as you create an object.

Exporting and importing the physics data

If you finished editing the sprites press the Publish button in the top toolbar. This exports an xml file that you can load in your game. Make sure that the data file is written to your Resources folder and that you include it in your project.

Adding the loader

PhysicsEditor requires a loader to read the shapes at runtime. The loader code is open source and available from our loader repository. The example project already contains the loader for cocos2d-x.

The loader class is called PhysicsShapeCache and is implemented as a singleton to make it easy to access the data from everywhere. You can access the loader instance using PhysicsShapeCache::getInstance() .

Use addShapesWithFile("filename") to load a shapes definition file. You can also load multiple files at once. Just make sure that the sprites have different names.

To unload the shapes use removeShapesWithFile("filename") .

To get the PhysicsBody structure of a shape use createBodyWithName("name of the sprite") .

The convenience method setBodyOnSprite("name of the sprite", sprite) directly sets the body on the given sprite.

A quick walk through the example project

Creating the scene, physics setup

The createScene method creates the physics world scene, and the gravity vector.

Scene* HelloWorld::createScene()
{
    // create the scene with physics enabled
    auto scene = Scene::createWithPhysics();

    // set gravity
    scene->getPhysicsWorld()->setGravity(Vec2(0, -900));

    // optional: set debug draw
    // scene->getPhysicsWorld()->setDebugDrawMask(0xffff);

    auto layer = HelloWorld::create();
    scene->addChild(layer);

    return scene;
}

Initialising the game scene

This code block loads the physics shapes from the Shapes.plist created with PhysicsEditor.

It also adds a background image which has no physics properties, the ground sprite (static physics object) and drops a banana. The spawnSprite("name") loads a sprite and sets the physics shape.

Finally there's a touch listener setup to add new shapes when the screen is touched or the mouse is clicked in the scene.

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }

    auto pos = Vec2(Director::getInstance()->getVisibleSize()) / 2 +
    Director::getInstance()->getVisibleOrigin();

    // Load shapes
    shapeCache = PhysicsShapeCache::getInstance();
    shapeCache->addShapesWithFile("Shapes.plist");

    // Load background image
    Sprite *background = Sprite::create("background.png");
    background->setPosition(pos);
    addChild(background);

    // Add ground sprite and drop a banana
    spawnSprite("ground.png", pos);
    spawnSprite("banana.png", pos);

    // Add touch listener
    auto listener = EventListenerTouchOneByOne::create();
    listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchesBegan, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

    return true;
}

Creating sprites with physics enabled

This code block creates a sprite and sets its physics body. It also adds the sprite to the scene.

void HelloWorld::spawnSprite(const std::string &name, Vec2 pos)
{
    // create a sprite with the given image name
    auto sprite = Sprite::create(name);

    // attach physics body
    shapeCache->setBodyOnSprite(name, sprite);

    // set position and add it to the scene
    sprite->setPosition(pos);
    addChild(sprite);
}

Adding random sprites

The onTouchesBegan() method spawns random objects at the position where the touch was detected.

bool HelloWorld::onTouchesBegan(Touch *touch, Event *event)
{
    auto touchLoc = touch->getLocation();

    static int i = 0;
    static std::string sprites[] = { "banana.png", "cherries.png", "crate.png", "orange.png" };

    spawnSprite(sprites[i], touchLoc);
    i = (i + 1) % (sizeof(sprites)/sizeof(sprites[0]));

    return false;
}

Conclusion

Well — that's it for the start. You've now learned how easy it is to add physics shapes to your game.

game scene fs8