The testbed makes use of a feature known as "debug draw" to draw the shapes you see. Obviously if you are making a game you will want to have all kinds of eye-popping fancy graphics instead of these boring polygons, but the debug draw feature can be very useful when you are having trouble getting the physics scene to work right. Sometimes the problem can be in the rendering part of your game, for example a sprite may be drawn at the wrong position or rotation, giving the appearance of incorrect physics. I recommend keeping the debug draw ready to hand for times when you want to check exactly what is going on in the Box2D world.
The way it works is quite simple. Box2D tells you all the shapes it sees and where they are eg. "a circle of radius r at x,y", or "an edge from a to b", etc and you draw what you are told. You don't have to do any transforming or worry about where the actual body positions are or which shapes belong to which bodies, you simply draw the geometry. The idea is that if all you have to do is draw a few lines, you can't mess it up :-)
Phaser Box2D debug draw
For Phaser Box2D we're drawing to canvas using the javascript canvas functions. This is currently supported by the file debug_draw.js which implements all of the functions described in this article. The best I can say about debug_draw.js is that it works, mostly, but it's not readily extensible or very efficient.
Creating your own debug draw
Debug draw functionality is implemented by creating an object with the required drawing methods. Here are the main functions that need to be implemented:
const debugDraw = {
DrawPolygon: function(vertices, vertexCount, color) {},
DrawSolidPolygon: function(vertices, vertexCount, color) {},
DrawCircle: function(center, radius, color) {},
DrawSolidCircle: function(center, radius, axis, color) {},
DrawSegment: function(p1, p2, color) {},
DrawTransform: function(xf) {}
};
Separating the rendering code into one location like this makes it easy to implement debug drawing for different APIs like WebGL or Canvas 2D, or switch between different methods easily.
Although the default debug draw does the job and we don't really need to customize it, since we're on the topic let's try using our own implementation to alter the appearance a bit. Any scene from the previous topics will be ok to use - I'll start with the same scene as for the moving at constant speed topic. To make our own debug draw object, we will need to implement all of the required functions. For now let's just make them all empty:
const customDebugDraw = {
DrawPolygon: function(vertices, vertexCount, color) {},
DrawSolidPolygon: function(vertices, vertexCount, color) {},
DrawCircle: function(center, radius, color) {},
DrawSolidCircle: function(center, radius, axis, color) {},
DrawSegment: function(p1, p2, color) {},
DrawTransform: function(xf) {}
};
To tell the Box2D world to use this implementation instead of the default one, we use the b2World_Draw function and pass our debug draw object:
// Create world
const worldDef = b2DefaultWorldDef();
const worldId = b2CreateWorld(worldDef);
// Set up debug draw
customDebugDraw.flags = 1; // e_shapeBit
// In your render loop
b2World_Draw(worldId, customDebugDraw);
Notice we set a flags property which selects certain categories of debug information to display. Right now we are interested in seeing the shapes in the world, but you can also set this flag to include the following:
- 0x0001 (draw shapes)
- 0x0002 (draw joint connections)
- 0x0004 (draw axis aligned bounding boxes)
- 0x0008 (draw broad-phase pairs)
- 0x0010 (draw a marker at body CoM)
You might want to alter these settings at run time, like in the situation I mentioned above when you might occasionally want to confirm that your rendered game entities and Box2D are doing the same thing. In the testbed, you can see these settings as the checkboxes on the right hand panel.
If you use debug draw in your own projects, there is one important point to be aware of: you need to call b2World_Draw in your render loop, which will cause Box2D to call back to your debug draw object's functions for each shape that needs drawing.
Running the testbed now you should see nothing showing in the scene. This is because we still have a completely empty debug draw implementation. From this point, what you fill in the drawing functions depends on what platform and rendering API you are using. As an example, let's implement the DrawSolidPolygon function using WebGL instead of the Javascript canvas drawing functions:
const customDebugDraw = {
DrawSolidPolygon: function(vertices, vertexCount, color) {
// Set up vertex array
const glverts = new Float32Array(16); // allow for polygons up to 8 vertices
// Fill in vertex positions as directed by Box2D
for (let i = 0; i < vertexCount; i++) {
glverts[i * 2] = vertices[i].x;
glverts[i * 2 + 1] = vertices[i].y;
}
// Set up WebGL buffers and attributes
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, glverts, gl.DYNAMIC_DRAW);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// Draw solid area
gl.uniform4f(colorLocation, color.r, color.g, color.b, 1.0);
gl.drawArrays(gl.TRIANGLE_FAN, 0, vertexCount);
// Draw outline
gl.lineWidth(3.0); // fat lines
gl.uniform4f(colorLocation, 1.0, 0.0, 1.0, 1.0); // purple
gl.drawArrays(gl.LINE_LOOP, 0, vertexCount);
}
};
The other drawing functions can be implemented in much the same way. Depending on what rendering API you are using, circles might have to be drawn as a polygon with many sides.
Credits
This tutorial is adapted from an original piece of work created by Chris Campbell and is used under license.