Box2d is a 2D physics engine. It realistically models the interactions between moving and colliding rigid bodies in two dimensions. Box2D does the math required to depict boxes, balls, and polygons moving, colliding, and bouncing across a virtual world. But Box2D doesn't draw anything. To turn this information into a game, you have to get information from Box2d, map it to coordinates useful for drawing, and draw your world.
Box2D uses floating point math and defines objects in MKS: Metres, Kilograms, Seconds. It is meant for objects from about a few centimetres in size up to a few metres. It isn't really meant to model aircraft
carriers or molecules. This means you will create a world in Box2d, set it in motion, then scale and translate (transform) the Box2D information to information suitable for using to draw a game: pixels.
A very useful tool Box2D has is the
DebugDraw facility. By proving a handful of simple drawing routines to box2D, you can quickly get a prototype up and running that shows
Box2D objects as simple vector drawings.
DebugDraw is also handy to see exactly what is going on inside
Box2D. Even if you have a fancy graphics front end, it could be handy to turn on
debugDraw for debugging.
This project shows how to use set up a very simple
box2d world, and use
DebugDraw to view this world in an MFC form using C++ and GDI+.
For an excellent
Box2D tutorial, see this link.
(The first thing to do is download and compile the
Box2D library from here. For our project, you will want to make a couple of changes to the Properties of the
Box2D project before compiling:
- Use of MFC: using in a shared DLL
- Character set: Use Unicode character set
These settings assume you are going to link
Box2D with a MFC project using Unicode. It is very important to have the MFC and character set options match or you will get LOTS of link errors. Now we can start our project. Fire up Visual Studio and create a new C++ MFC Application project (or download mine). First thing to do is tell our project we are using the Box2d.lib:
- Open the project configuration properties
- Check that our new project default to using Unicode and MFC in a shared DLL
- Open the Linker section
- Click on input
- Edit "Additional Dependencies"
- Add Box2d.lib (don't add a path)
- Open the VC++ directories
- Edit "Include Directories"
- Add the
Box2D library path, for example:
- C:\Users\somename\Documents\Visual Studio 20xx\Projects\box2d
- Edit "Library directories"
- Add the
Box2D path, for example:
- C:\Users\somename\Documents\Visual Studio 2010\Projects\box2d\Build\vs20xx\bin\Debug
- Note you have to add the debug version to your debug configuration, and the release version to your release version.
That takes care of the Project configuration. Next, we have to add the GDU+ required code.
Add this code to stdafx.h:
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")
In your Dialog
InitInstance function, add:
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
ExitInstance isn't normally created by default, but you have to add one to shutdown GDI+.
And finally, add this to the
yourApp::CWinApp class definition, in the
That takes care of the housekeeping.
Using the Code
Now onto the code. To implement
Box2D DebugDraw, you have to implement a class based on the
Here is the minimum that has to be implemented:
class DebugDrawGDI : public b2Draw
virtual void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
virtual void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
virtual void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color);
virtual void DrawSolidCircle(const b2Vec2& center,
float32 radius, const b2Vec2& axis, const b2Color& color);
virtual void DrawSegment(const b2Vec2& p1,
const b2Vec2& p2, const b2Color& color);
virtual void DrawTransform(const b2Transform& xf);
By implementing these six drawing functions,
Box2D can draw any world, in a simple debug drawing mode. So all we have to do is code up these six functions in GDI+. Writing these six functions is straightforward; the wrinkle is that
Box2D is going to be sending in
box2D world coordinates, and we have to convert them to Windows pixel coordinates. Most of our code is going to be devoted to setting up the transform that will allow GDI+ to correctly draw the
Box2D coordinates are in meters,
x increases from left to right, and
y increases as you go up. GDI+ coordinates are in pixels,
x increases from left to right, but
y increases as you go down.
The technique we will use is to get the size of the GDI+ window, the size of the
Box2D world, and setup a transform matrix we can pass on the
graphics.SetTransform() function. Once we do this, the transform handles all the coordinate mapping, and writing the six graphics becomes trivial.
Getting the GDI+ window is simple:
BoxD2 world size takes a bit more work. First, you have to create a
Box2D world, add some"bodies" to it, then query the "world" to see how big it is. For testing, I make a world with rectangle as ground, roof, and walls, and add a couple of dynamic objects inside these walls. Once we have created the world, we can call a few
Box2d functions to iterate over all the bodies and get the world size:
void DebugDrawGDI::GetBoundBox2DBounds(RECT *w, b2World *world)
float minX, maxX, minY, maxY;
while ( b )
while ( fix )
if ( bound.lowerBound.x < minX )
if ( bound.upperBound.x > maxX )
if ( bound.lowerBound.y < minY )
if ( bound.upperBound.y > maxY )
Now that we know how big everything is, we can calculate our transform:
void DebugDrawGDI::ScaleWorldCalculate(RECT *r, RECT *w)
int outputWidth = r->right - r->left;
int outputHeight = r->bottom - r->top;
int boundsWidth = w->right - w->left;
int boundsHeight = w->top - w->bottom;
scaleX = (float )outputWidth / (float )boundsWidth;
scaleY = (float )outputHeight / (float )boundsHeight;
scale = scaleX > scaleY ? scaleY : scaleX;
offsetX=r->left - (int )((float )w->left * scaleX);
offsetY=r->top - (int )((float )w->bottom * scaleY);
matrixTransform.Scale(scaleX, -scaleY, MatrixOrderAppend);
matrixTransform.Translate((float )offsetX, (float )(yAdjust-offsetY), MatrixOrderAppend);
Then in our main drawing loop, we just have to set the transform, something like this:
g" is a pointer to our current
Graphics object. With the transform in place, our drawing functions look like this:
void DebugDrawGDI::DrawSolidPolygon(const b2Vec2* vertices,
int32 vertexCount, const b2Color& color)
PointF *points=new PointF[vertexCount+1];
Color clr(255, (int )(color.r*255), (int )(color.g*255), (int )(color.b*255));
for (i = 0; i < vertexCount; ++i, ++vertices)
points[i].X = vertices->x;
points[i].Y = vertices->y;
points[vertexCount].X = points.X;
points[vertexCount].Y = points.Y;
gdi->FillPolygon(&sb, points, vertexCount + 1);
Note that we do no scaling or mapping in the drawing function. The transform takes care of it all for us.
All that is left is to setup the usual MFC animation code.
We have to add a timer to trigger our updates, and add an
on_paint event handler to draw everything.
Code like this to the
InitDialog function to setup a timer:
stepSeconds=(float )timerMilliseconds / 1000.0f;
SetTimer(1234567890, timerMilliseconds, NULL);
As well, the code to create the
Box2D world, an instance of the
DebugDraw class, etc. goes into
All the logical code goes into the
On_Timer event handler, and all the drawing code goes into the
On_Paint event handler. Download the project and have a look at the code to see the rest of the details.
Wouldn't it be nice if
Box2D could tell you when something collided? Happily, it does, and you don't have to look at everybody in the world each frame, you can be notified of collisions with a callback function. Here is the definition of the Collision Listener class:
class MyContactListener : public b2ContactListener
void BeginContact(b2Contact* contact);
void EndContact(b2Contact* contact);
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);
Our simple example code only implements
BeginContact and saves some data so we can draw an "explosion" each time our falling box bounces off (Collides) with something. We'll want to save some data about the collision:
float x, y;
InitDialog we tell
Box2D to use our callback. "
world" is the
Box2D object we created.
And some simple code to see what collided, and if we are interested in the collision.
void MyContactListener::BeginContact(b2Contact* contact)
if ( bd && bd->id == BALL_ID )
if ( bd && bd->id == BALL_ID )
Then we add code to the timer event handler to animate our explosion, and to paint to draw our explosion which is just an expanding and contracting circle. That covers the highlights of this project. Download the complete project to play with a very simple, but complete,
DebugDraw implementation for GDI+ under MFC.
Draw a Bitmap on Top of a Box
You might want to draw a bitmap on top of a
box2d item. This code shows the basic technique. In the initialization code, save a pointer to the box body called
b2box. Then add this code to
Maindraw, add variable declarations at the top, the rest of the code just after world->
(float )(-((int )boxImage->GetHeight())/2));
It flickers pretty bad. There are standard techniques to avoid flicker and they could be used to stop the flicker. I'm not sure a dialog is the best thing for a game, but it was easy to get working. It might be better to setup a plain Win32 application for a full screen game. There are also C# ports of the
Box2D library which you could use with XNA.
If you get linker errors, make sure that you compiled the
box2d project with the same settings as your project: language, MFC, and ATL. Details are in the article, but I mention it again in case you download everything and just try to compile it. The default
box2d project has different settings from a default MFC project.