Qt + Box2D is easy
Box2D is an Open Source rigid body 2D physics engine for C++. It’s currently (2.0.1) released under the MIT license, which is quite permissive. Box2D is used by, among other things, Gluon (http://gluon.tuxfamily.org/), which is a game library from KDE in-the-making.
Integrating Box2D into your Qt application is quite easy, and this blog shows you how to get started. First of all:
* Step 1: Download Box2D from Google Code: http://code.google.com/p/box2d/
* Step 2: Build it (I had to insert a few #include <cstring> to get it to build)
* Step 3: Build and try the test bed application: Box2D/Examples/TestBed/
* Step 4: Read the manual: http://www.box2d.org/manual.html
* Step 5: Continue reading this blog to hook up the two frameworks…
The library doesn’t seem to install, so I just compiled it in-source and used it directly.
What I found during my approx 2 hour study today was that Box2D manages a world with bodies, similar to how QGraphicsScene manages items. In short, you create a world object and populate it with elements. Some bodies are static, like the ground, and some dynamic, like a bouncing ball. You can define joints, masses, friction, and other parameters, define a gravity vector, and then start simulating. Box2D doesn’t require a graphics system – any scene graph with elements that you can move and rotate should do fine. Graphics View works quite well. I’ve based this code on the provided “Hello World” example that comes with Box2D.
The world object defines the bounds of the coordinate system and the gravity vector. It feels very similar to QGraphicsScene. The bounds are, according to the docs, not enforced, but I got many run-time aborts when items are outside these bounds so you better make the world large enough to cover all your items.
// Define world, gravity b2AABB worldAABB; worldAABB.lowerBound.Set(-200, -100); worldAABB.upperBound.Set(200, 500); b2World world = new b2World(worldAABB, /* gravity = */ b2Vec2(0.0f, -10.0f), /* doSleep = */ true);
Bodies in Box2D have a position and an angle, and you can assign a shape to it (convex polygon or circular). This feel similar to how QGraphicsItem has a position and a transform. In fact with 4.6 the rotation property fits perfectly with the angle in Box2D (except Box2D uses radians and rotates the opposite direction from QGraphicsItem). This example shows how to create a body, and then assign a rectangular shape:
b2BodyDef bodyDef; bodyDef.position.Set(0.0f, -10); b2Body *body = world->CreateBody(&bodyDef); b2PolygonDef shapeDef; shapeDef.SetAsBox(100.0f, 10.0f); body->CreateShape(&shapeDef);
Bodies can either be static or dynamic. Static bodies simply don’t move. By default, bodies are static. To make a body dynamic, you assign a positive mass. The easiest way to do that is to ask Box2D to calculate mass and rotational inertia by looking at the shape of the body. So modifying the above slightly:
shapeDef.density = 1.0f; shapeDef.friction = 0.5f; body->CreateShape(&shapeDef); body->SetMassFromShapes();
That’s really all there is to it. When you’re ready, advance the simulation step by step by calling b2World::Step like this:
world->Step(B2_TIMESTEP, B2_ITERATIONS);
After calling this function, Box2D will have adjusted positions and angles of all bodies. So if you have corresponding items in Graphics View, you can just update their positions and rotations like this:
void adjust() { // Update QGraphicsItem's position and rotation from body. b2Vec2 position = _body->GetPosition(); float32 angle = _body->GetAngle(); setPos(position.x, -position.y); setRotation(-(angle * 360.0) / (2 * PI)); }
Notice the negative Y (as Graphics View, like the rest of Qt, has a Y component that points downwards), and the negative rotation which is converted to degrees.
That’s really all there is. Create the world, add body elements, assign mass, and start the simulation. Use the angle and position to adjust your QGraphiscItems, and enjoy .
The above video shows my first Box2D + Graphics View application in action. You can find the full sources here: qgv-box2dtar.gz. I’ve tried to experiment a bit with how Box2D bindings for Qt could be done. For now I’ll leave it as an experiment.