Box2D is a simple open source 2D physics engine. It was written as part of a GDC talk in 2006, and a lot of famous iPhone games like Angry Birds use it. You can find more information about it over at its website. I also found a great set of tutorials here, which introduce a lot of the concepts behind Box2D. These tutorials are in C++, but you can get the general idea and apply it to Moai/Lua. Moai has built in support for Box2D, so you can use it without referencing any additional files.
I’m not going to go too deep about how to use Box2D, but I’ll provide some general guidance to get you started. In this post I’ll show you enough to demonstrate a simple ball bouncing on a floor.
We’ll start with some basic setup code to create a window, viewport and layer:
local screenWidth = 640 local screenHeight = 480 MOAISim.openWindow("Window",screenWidth,screenHeight) local viewport = MOAIViewport.new() viewport:setSize(screenWidth,screenHeight) viewport:setScale(screenWidth,screenHeight) local layer = MOAILayer2D.new() layer:setViewport(viewport)
The next thing you need to create is a Box2D world (MOAIBox2DWorld). This class manages the general state of your physics simulation. You can set stuff like your world’s gravity and a scale from your screen coordinates to actual meters. This is important because Box2D works best for a certain range of sizes, so if you find that your physics are not working out, you may need to scale everything differently. Calling start() will start up the simulation. Finally you can set your world to a layer to draw some debug information. This works better if you have a separate layer just for this purpose, and is set above your regular layer so you can see the information on top of your sprites. For this example we won’t be drawing anything else, so we set it to our only layer.
local box2dworld = MOAIBox2DWorld.new() box2dworld:setGravity(0, -9.8) box2dworld:setUnitsToMeters(1/30) box2dworld:start() layer:setBox2DWorld(box2dworld)
Once you have this setup, you can try adding some objects to it. Objects in Box2D are represented by “bodies” and “fixtures”. Bodies hold information that isn’t related to the shape of the object like position, velocity and mass. Fixtures are attached to bodies, and define the actual shape of the object. A single body can have a bunch of fixtures attached to it – for example, a rectangle fixture and a circle fixture might both be attached to a single body to represent a person’s head and torso. Here some code to create a floor:
local floorBody = box2dworld:addBody( MOAIBox2DBody.STATIC ) floorBody:setTransform( 0, -200 ) local floorFixture = floorBody:addRect(-200, 5, 200, -5)
You can create bodies by calling addBody() on the world class. Pass in the type of body you want, which can be “static” for things that never move, “dynamic” for things that are affected by the physics simulation, and “kinematic” for things that can move, but are not affected by the physics (think moving platforms). Next we call setTransform() to set its location. Finally we call addRect() on the body to create a fixture that is 400 units wide and 10 tall. This is attached around the origin of the body, so you are really creating a rectangle that goes from (-200, -195) to (200, -205).
Next we create a ball:
local ballBody = box2dworld:addBody( MOAIBox2DBody.DYNAMIC ) ballBody:setTransform( 0, 100 ) local circleFixture = ballBody:addCircle( 0, 0, 20 ) circleFixture:setDensity(1) circleFixture:setRestitution(0.9) ballBody:resetMassData()
This makes a new dynamic body at 100 units up. We attach a circular fixture to it with a radius of 20 units. We also set the density to 1, and restitution (bounciness) to 0.9 (on a scale of 0 to 1, so this is pretty bouncy). Since the density of the fixtures affects the body’s mass, we have to tell the body to recalculate its mass after adding this fixture.
If you’ve followed these steps, you should see a bouncing ball appear! Of course there is much more to Box2D than what I have described. You can use forces and impulses to control your objects, use joints to tie multiple bodies together, and use sensors to detect when areas overlap.
Here are some other random things I’ve noticed about the Moai Box2D setup that I’ve found interesting:
- Call destroy() on a body to clean it up along with all of its fixtures. This means you need to keep track of all the bodies you create (you cannot query the world for a list of bodies – see the item below).
- Normally you have to be careful when you call destroy() – if you do it while the simulation is running, you get an error because you can’t take something out of the simulation while it is halfway through its calculations. Normally you should add it to a list, and then clean everything up once the simulation loop is done. However, I’m not sure Moai lets you control when the simulation starts and ends. The good news is I found some documentation that says Moai now automatically queues up destroy() calls, so you can call it whenever you want and it will not get really called until it is safe.
- Call stop() to end your physics simulation.
- Even though the Box2D documents a lot of functions in C++, not all of them are actually exposed through Moai. You can look at the Moai class list here to see which classes and functions are actually exposed. I’ve found things like “getBodyList()” on the world and various ray casting / collision functions aren’t there. If you do want to expose some additional functionality, here is an example of how someone did it.
- Even though a lot of the collision related functions aren’t exposed in Moai, there is still a way to handle it, through a fixture’s setCollisionHandler() function. I’ll show an example in the next post.