box2d - s76/libgdx GitHub Wiki
Box2D is a 2D physics library. It is one of the most popular physics libraries for 2D games and has been ported to many languages and many different engines, including libgdx.
The Box2D implementation in libgdx is a thin Java wrapper about the C++ engine. Therefore, the excellent official Box2D manual (PDF) may also come in handy.
For more documentation related to Box2D, please visit box2d.org. Any Box2D questions not related to libgdx specifically can best be asked on their forum.
Since libgdx 1.0 release Box2D is an extension and no more included in libgdx by default. Thus a manual installation is required.
- Initialization
- Creating a World
- Debug Renderer
- Stepping the simulation
- Rendering
- Objects/Bodies
- Impulses/Forces
- Joints and Gears
- Fixture Shapes
- Sprites and Bodies
- Sensors
- Contact Listeners
- Code Snippets
- Resources
- Tools
To initialize Box2D it is necessary to call Box2D.init()
. For backwards compatibility, creating a World
for the first time will have the same effect, but using the Box2D
class should be preferred.
When setting up Box2D the first thing we need is a world. The world object is basically what holds all your physics objects/bodies and simulates the reactions between them. It does not however render the objects for you; for that you will use libgdx graphics functions. That said, libgdx does come with a Box2D debug renderer which is extremely handy for debugging your physics simulations, or even for testing your game-play before writing any rendering code.
To create the world we use the following code:
World world = new World(new Vector2(0, -10), true);
The first argument we supply is a 2D vector containing the gravity: 0
to indicate no gravity in the horizontal direction, and -10 is a downwards force like in real life (assuming your y axis points upwards). These values can be anything you like, but remember to stick to a constant scale. In Box2D 1 unit = 1 metre.
The second value in the world creation is a boolean value which tells the world if we want objects to sleep or not. Generally we want objects to sleep as this conserves CPU usage, but there are situations where you might not want your objects to sleep.
It is adviced to use the same scale you use for Box2D to draw graphics. This means drawing a Sprite with a width/height in meters. To scale up the graphics to make them visible, you should use a camera with a viewportWidth / viewportHeight also in meters. E.g: drawing a Sprite with a width of 2.0f (2 meters) and using a camera viewportWidth of 20.0f, the Sprite will fill 1/10th of the width on the window.
The next thing we are going to do is setup our debug renderer. You generally will not use this in a released version of your game, but for testing purposes we will set it up now like so:
Box2DDebugRenderer debugRenderer = new Box2DDebugRenderer();
To update our simulation we need to tell our world to step. Stepping basically updates the world objects through time. The best place to call our step function is at the end of our render()
loop. In a perfect world everyone's frame rate is the same
world.step(1/60f, 6, 2);
The first argument is the time-step, or the amount of time you want your world to simulate. In most cases you want this to be a fixed time step. libgdx recommends using either 1/45f
(which is 1/45th of a second) or 1/300f
(1/300th of a second).
The other two arguments are velocityIterations
and positionIterations
. For now we will leave these at 6
and 2
, but you can read more about them in the Box2D documentation.
Stepping your simulation is a topic unto itself. See this article for an excellent discission on the use of variable time steps.
The result might look similar to this:
private float accumulator = 0;
private void doPhysicsStep(float deltaTime) {
// fixed time step
// max frame time to avoid spiral of death (on slow devices)
float frameTime = Math.min(deltaTime, 0.25f);
accumulator += frameTime;
while (accumulator >= Constants.TIME_STEP) {
WorldManager.world.step(Constants.TIME_STEP, Constants.VELOCITY_ITERATIONS, Constants.POSITION_ITERATIONS);
accumulator -= Constants.TIME_STEP;
}
}
It is recommended that you render all your graphics before you do your physics step, otherwise it will be out of sync. To do this with our debug renderer we do the following:
debugRenderer.render(world, camera.combined);
The first argument is our Box2D world and the second argument is our libgdx camera.
Now if you run your game it will be pretty boring as nothing happens. The world steps but we don’t see anything as we don’t have anything to interact with it. So now we’re going to add some objects.
In Box2D our objects are called bodies, and each body is made up of one or more fixtures, which have a fixed position and orientation within the body. Our fixtures can be any shape you can imagine or you can combine a variety of different shaped fixtures to make the shape you want.
A fixture has a shape, density, friction and restitution attached to it. Shape is obvious. Density is the mass per square metre: a bowling ball is very dense, yet a balloon isn’t very dense at all as it is mainly filled with air. Friction is the amount of opposing force when the object rubs/slides along something: a block of ice would have a very low friction but a rubber ball would have a high friction. Restitution is how bouncy something is: a rock would have a very low restitution but a basketball would have a fairly high restitution. A body with a restitution of 0 will come to a halt as soon as it hits the ground, whereas a body with a restitution of 1 would bounce to the same height forever.
Bodies come in three different types: dynamic, kinematic and static. Each type is described below.
Dynamic bodies are objects which move around and are affected by forces and other dynamic, kinematic and static objects. Dynamic bodies are suitable for any object which needs to move and be affected by forces.
We have now learned about fixtures which make up our bodies, so let's get dirty and start to create some bodies and add fixtures to them!
// First we create a body definition
BodyDef bodyDef = new BodyDef();
// We set our body to dynamic, for something like ground which doesn't move we would set it to StaticBody
bodyDef.type = BodyType.DynamicBody;
// Set our body's starting position in the world
bodyDef.position.set(100, 300);
// Create our body in the world using our body definition
Body body = world.createBody(bodyDef);
// Create a circle shape and set its radius to 6
CircleShape circle = new CircleShape();
circle.setRadius(6f);
// Create a fixture definition to apply our shape to
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.density = 0.5f;
fixtureDef.friction = 0.4f;
fixtureDef.restitution = 0.6f; // Make it bounce a little bit
// Create our fixture and attach it to the body
Fixture fixture = body.createFixture(fixtureDef);
// Remember to dispose of any shapes after you're done with them!
// BodyDef and FixtureDef don't need disposing, but shapes do.
circle.dispose();
Now we have create a ball like object and added it to our world. If you run the game now you should see a ball fall down the screen. This is still fairly boring though, as it has nothing to interact with. So let's create a floor for our ball to bounce on.
Static bodies are objects which do not move and are not affected by forces. Dynamic bodies are affected by static bodies. Static bodies are perfect for ground, walls, and any object which does not need to move. Static bodies require less computing power.
Let's go ahead and create our floor as a static body. This is much like creating our dynamic body earlier.
// Create our body definition
BodyDef groundBodyDef =new BodyDef();
// Set its world position
groundBodyDef.position.set(new Vector2(0, 10));
// Create a body from the defintion and add it to the world
Body groundBody = world.createBody(groundBodyDef);
// Create a polygon shape
PolygonShape groundBox = new PolygonShape();
// Set the polygon shape as a box which is twice the size of our view port and 20 high
// (setAsBox takes half-width and half-height as arguments)
groundBox.setAsBox(camera.viewportWidth, 10.0f);
// Create a fixture from our polygon shape and add it to our ground body
groundBody.createFixture(groundBox, 0.0f);
// Clean up after ourselves
groundBox.dispose();
See how we created a fixture without the need to define a FixtureDef
? If all you need to specify is a shape and a density, the createFixture
method has a useful overload for that.
Now if you run the game you should see a ball fall and then bounce on our newly created ground. Play around with some of the different values for the ball like density and restitution and see what happens.
Kinematic bodies are somewhat in between static and dynamic bodies. Like static bodies, they do not react to forces, but like dynamic bodies, they do have the ability to move. Kinematic bodies are great for things where you, the programmer, want to be in full control of a body's motion, such as a moving platform in a platform game.
It is possible to set the position on a kinematic body directly, but it's usually better to set a velocity instead, and letting Box2D take care of position updates.
You can create a kinematic body in much the same way as the dynamic and static bodies above. Once created, you can control the velocity like this:
// Move upwards at a rate of 1 meter per second
kinematicBody.setLinearVelocity(0.0f, 1.0f);
Impulses and Forces are used to move a body in addition to gravity and collision.
Forces occur gradually over time to change the velocity of a body. For example, a rocket lifting off would slowly have forces applied as the rocket slowly begins to accelerate.
Impulses on the other hand make immediate changes to the body's velocity. For example, playing Pac-Man the character always moved at a constant speed and achieved instant velocity upon being moved.
First you will need a Dynamic Body to apply forces/impulses to, see the Dynamic Bodies section above.
Applying Force
Forces are applied in Newtons at a World Point. If the force is not applied to the center of mass, it will generate torque and affect the angular velocity.
// Apply a force of 1 meter per second on the X-axis at pos.x/pos.y of the body slowly moving it right
dynamicBody.applyForce(1.0f, 0.0f, pos.x, pos.y, true);
// If we always want to apply force at the center of the body, use the following
dynamicBody.applyForceToCenter(1.0f, 0.0f, true);
Applying Impulse
Impulses are just like Forces with the exception that they immediately modify the velocity of a body. As with forces, if the impulse is not applied at the center of a body, it will create torque which modifies angular velocity. Impulses are applied in Newton-seconds or kg-m/s.
// Immediately set the X-velocity to 1 meter per second causing the body to move right quickly
dynamicBody.applyLinearImpulse(1.0f, 0, pos.x, pos.y, true);
Keep in mind applying forces or impulses will wake the body. Sometimes this behavior is undesired. For example, you may be applying a steady force and want to allow the body to sleep to improve performance. In this case you can set the wake boolean value to false.
// Apply impulse but don't wake the body
dynamicBody.applyLinearImpulse(0.8f, 0, pos.x, pos.y, false);
Player Movement Example
In this example, we will make a player run left or right and accelerate to a maximum velocity, just like Sonic the Hedgehog. For this example we have already created a Dynamic Body named 'player'. In addition we have defined a MAX_VELOCITY variable so our player won't accelerate beyond this value. Now it's just a matter of applying a linear impulse when a key is pressed.
Vector2 vel = this.player.body.getLinearVelocity();
Vector2 pos = this.player.body.getPosition();
// apply left impulse, but only if max velocity is not reached yet
if (Gdx.input.isKeyPressed(Keys.A) && vel.x > -MAX_VELOCITY) {
this.player.body.applyLinearImpulse(-0.80f, 0, pos.x, pos.y, true);
}
// apply right impulse, but only if max velocity is not reached yet
if (Gdx.input.isKeyPressed(Keys.D) && vel.x < MAX_VELOCITY) {
this.player.body.applyLinearImpulse(0.80f, 0, pos.x, pos.y, true);
}
Coming soon
Coming soon
The easiest way to manage a link between your sprites or game objects and Box2D is with Box2D’s User Data. You can set the user data to your game object and then update the object's position based on the Box2D body.
Setting a body's user data is easy
body.setUserData(Object);
This can be set to any Java object. It is also good to create your own game actor/object class which allows you to set a reference to its physics body.
Fixtures can also have user data set to them in the same way.
fixture.setUserData(Object);
To update all your actors/sprites you can loop through all the world's bodies easily in your game/render loop.
// Create an array to be filled with the bodies
// (better don't create a new one every time though)
Array<Body> bodies = new Array<Body>();
// Now fill the array with all bodies
world.getBodies(bodies);
for (Body b : bodies) {
// Get the body's user data - in this example, our user
// data is an instance of the Entity class
Entity e = (Entity) b.getUserData();
if (e != null) {
// Update the entities/sprites position and angle
e.setPosition(b.getPosition().x, b.getPosition().y);
// We need to convert our angle from radians to degrees
e.setRotation(MathUtils.radiansToDegrees * b.getAngle());
}
}
Then render your sprites using a libgdx SpriteBatch
as usual.
Sensors are Bodies that do not produce automatic responses during a collision (such as applying force). This is useful when one needs to be in complete control of what happens when two shapes collide. For example, think of a drone that has some kind of circular distance of sight. This body should follow the drone but shouldn't have a physical reaction to it, or any other bodies. It should detect when some target is inside it's shape.
To configure a body to be a sensor, set the 'isSensor' flag to true. An example would be:
//At the definition of the Fixture
fixtureDef.isSensor = true;
In order to listen to this sensor contact, we need to implement the ContactListener interface methods.
The Contact Listeners listen for collisions events on a specific fixture. The methods are passed a Contact object, which contain information about the two bodies involved. The beginContact method is called when the object overlap another. When the objects are no longer colliding, the endContact method is called.
public class ListenerClass implements ContactListener {
@Override
public void endContact(Contact contact) {
}
@Override
public void beginContact(Contact contact) {
}
};
This class needs to be set as the world's contact listener in the screen's show() or init() method.
world.setContactListener(ListenerClass);
We might get information about the bodies from the contact fixtures. Depending on the application design, the Entity class should be referenced in the Body or Fixture user data, so we can use it from the Contact and make some changes (e.g. change the player health).
Coming soon
There are a lot of really good Box2D resources out there and most of the code can be easily converted to libgdx.
- http://box2d.org Documentation and forums are a great place to find help.
- http://www.iforce2d.net/b2dtut/ A really good tutorial series on Box2D. Covers a lot of different problems which you will more than likely run across in your game development
The following is a list of tools for use with box2d and libgdx:
- RUBE editor for creating box2d worlds. Use RubeLoader for loading RUBE data into libgdx.
- Obli's Physics Body Editor