osa1 feed

Quick Box2d tips: Terrains, jump behavior and bodies with multiple shapes

March 6, 2013 - Tagged as: box2d, en.

After wasting enough time implementing my own half-baked physics engine and never having an efficiently implemented collision resolution code, I’ve decided to go on with physics module my game library of choice provides. It’s based on Box2d and the API is mostly the same.

My first try of using it was last weekend, me and my brother did a super short(12 hours) game jam, and I used Box2d for the first time. We finished our game, but it was so buggy that I don’t even mention it to anybody. Still, I learned so much about Box2d physics.

After several days, I could finally spare some time and fixed the bugs. I also wrote a simple demo app to demonstrate my points in this post. You can see the code here. It’s a full program, just paste it somewhere and run with Löve2d(the code about chained shapes are based one someone else’s code from Löve2d forums, I don’t remember whose code is this).

Implementing terrains with Box2d

Main bug source of my game was terrain implementation. It was reading the map info from a binary file and then creating lots of RectangleShapes. The problem with this approach was that other dynamic entities in my game was getting stuck between two RectangleShapes, even though each shape was in touch with other so there was no space between them. Later I realized that this behavior is actually documented(see edge shapes section).

There are two solution mentioned in Box2d manual. Now I’m using ChainShapes in my code and it’s working as expected. In the example game I linked above, you can see that my terrain is implemented with some artificial shapes:

worldShape = love.physics.newChainShape("false",
    80, -100,
    80, 680,
    120, 680,
    ...
    620, 680,
    620, -100)

But still none of the dynamic bodies get stuck.

Bodies with multiple shapes and jump behavior

Being able to bind multiple shapes to a body is just an awesome feature. It helps having a more accurate shape of an entity, and also having multiple fixtures mean you can change properties of each fixture and get a more accurate behavior of friction or some other things.

For instance, in my game, I wanted 2 things; 1) I don’t want my main character’s left and right side to have friction, so that it should slide without getting slowed down when left/right side is collided and 2) I want to check if my character is collided in bottom side.

Having multiple fixtures helped for both cases. My character consists of 4 shapes and fixtures:

o.shapeLeft   = love.physics.newCircleShape(-20, 0, 5)
o.shapeTop    = love.physics.newCircleShape(0, -50, 5)
o.shapeRight  = love.physics.newCircleShape(20, 0, 5)
o.shapeBottom = love.physics.newCircleShape(0, 50, 5)

o.fixtureLeft   = love.physics.newFixture(o.body, o.shapeLeft, 1)
o.fixtureTop    = love.physics.newFixture(o.body, o.shapeTop, 1)
o.fixtureRight  = love.physics.newFixture(o.body, o.shapeRight, 1)
o.fixtureBottom = love.physics.newFixture(o.body, o.shapeBottom, 1)

Then I set each fixtures a different friction factor:

o.fixtureLeft:setFriction(0)
o.fixtureTop:setFriction(0)
o.fixtureRight:setFriction(0)
o.fixtureBottom:setFriction(100)

And for jump behavior, I’m just checking if fixtureBottom is collided, with the help of beginContact and endContact callbacks:

function beginContact(a, b, coll)
    if a == char.fixtureBottom or b == char.fixtureBottom then
        char.jumpEnabled = true
    end
end

function endContact(a, b, coll)
    if a == char.fixtureBottom or b == char.fixtureBottom then
        char.jumpEnabled = false
    end
end

With this features implemented, I think I have a good-enough platformer engine. Can’t wait for the next jam. Maybe we can build a game that we can actually mention to people ;-)