Understanding Box2D debug draw

When you are about to create a complex script with Box2D – or even a simple one, but that’s complex for your skills – I suggest to enable debug draw.

Debug draw will provide you everything you need in order to verify your script is working correctly, and once you are satisfied with the result, you can start attaching movieclips to skin the shapes.

So I am going to explain everything you need to know about debug draw, and probably something more.

The following movieclip is the HelloWorld.fla file you can find in the distribution package, with all movieclips removed and the debug draw enabled:

Let’ see how did I modify the script in order to enable debug draw… interesting lines range from line 21 to line 35

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	import Box2D.Dynamics.Joints.*;
	import flash.events.MouseEvent;
	public class HelloWorld extends Sprite {
		var body:b2Body;
		var mouseJoint:b2MouseJoint;
		public function HelloWorld() {
			addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-100.0, -100.0);
			worldAABB.upperBound.Set(100.0, 100.0);
			var gravity:b2Vec2=new b2Vec2(0.0,10.0);
			var doSleep:Boolean=true;
			m_world=new b2World(worldAABB,gravity,doSleep);
			// debug draw start
			var m_sprite:Sprite;
			m_sprite = new Sprite();
			addChild(m_sprite);
			var dbgDraw:b2DebugDraw = new b2DebugDraw();
			var dbgSprite:Sprite = new Sprite();
			m_sprite.addChild(dbgSprite);
			dbgDraw.m_sprite=m_sprite;
			dbgDraw.m_drawScale=30;
			dbgDraw.m_alpha = 1;
			dbgDraw.m_fillAlpha=0.5;
			dbgDraw.m_lineThickness=1;
			dbgDraw.m_drawFlags=b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit|b2DebugDraw.e_coreShapeBit|b2DebugDraw.e_aabbBit|b2DebugDraw.e_obbBit|b2DebugDraw.e_pairBit|b2DebugDraw.e_centerOfMassBit;
			m_world.SetDebugDraw(dbgDraw);
			// debug draw end
			var bodyDef:b2BodyDef;
			var boxDef:b2PolygonDef;
			var circleDef:b2CircleDef;
			bodyDef = new b2BodyDef();
			bodyDef.position.Set(10, 12);
			boxDef = new b2PolygonDef();
			boxDef.SetAsBox(30, 3);
			boxDef.friction=0.3;
			boxDef.density=0;
			body=m_world.CreateBody(bodyDef);
			body.CreateShape(boxDef);
			body.SetMassFromShapes();
			for (var i:int = 1; i < 10; i++) {
				bodyDef = new b2BodyDef();
				bodyDef.position.x=Math.random()*12+2;
				bodyDef.position.y=Math.random()*5;
				var rX:Number=Math.random()+0.5;
				var rY:Number=Math.random()+0.5;
				if (Math.random()<0.5) {
					boxDef = new b2PolygonDef();
					boxDef.SetAsBox(rX, rY);
					boxDef.density=1.0;
					boxDef.friction=0.5;
					boxDef.restitution=0.2;
					body=m_world.CreateBody(bodyDef);
					body.CreateShape(boxDef);
				} else {
					circleDef = new b2CircleDef();
					circleDef.radius=rX;
					circleDef.density=1.0;
					circleDef.friction=0.5;
					circleDef.restitution=0.2;
					body=m_world.CreateBody(bodyDef);
					body.CreateShape(circleDef);
				}
				body.SetMassFromShapes();
			}
			stage.addEventListener(MouseEvent.MOUSE_DOWN, createMouse);
			stage.addEventListener(MouseEvent.MOUSE_UP, destroyMouse);
		}
		public function createMouse(evt:MouseEvent):void {
			var body:b2Body=GetBodyAtMouse();
			if (body) {
				var mouseJointDef:b2MouseJointDef=new b2MouseJointDef  ;
				mouseJointDef.body1=m_world.GetGroundBody();
				mouseJointDef.body2=body;
				mouseJointDef.target.Set(mouseX/30, mouseY/30);
				mouseJointDef.maxForce=30000;
				mouseJointDef.timeStep=m_timeStep;
				mouseJoint=m_world.CreateJoint(mouseJointDef) as b2MouseJoint;
			}
		}
		public function destroyMouse(evt:MouseEvent):void {
			if (mouseJoint) {
				m_world.DestroyJoint(mouseJoint);
				mouseJoint=null;
			}
		}
		private var mousePVec:b2Vec2 = new b2Vec2();
		public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body {
			var mouseXWorldPhys = (mouseX)/30;
			var mouseYWorldPhys = (mouseY)/30;
			mousePVec.Set(mouseXWorldPhys, mouseYWorldPhys);
			var aabb:b2AABB = new b2AABB();
			aabb.lowerBound.Set(mouseXWorldPhys - 0.001, mouseYWorldPhys - 0.001);
			aabb.upperBound.Set(mouseXWorldPhys + 0.001, mouseYWorldPhys + 0.001);
			var k_maxCount:int=10;
			var shapes:Array = new Array();
			var count:int=m_world.Query(aabb,shapes,k_maxCount);
			var body:b2Body=null;
			for (var i:int = 0; i < count; ++i) {
				if (shapes[i].GetBody().IsStatic()==false||includeStatic) {
					var tShape:b2Shape=shapes[i] as b2Shape;
					var inside:Boolean=tShape.TestPoint(tShape.GetBody().GetXForm(),mousePVec);
					if (inside) {
						body=tShape.GetBody();
						break;
					}
				}
			}
			return body;
		}
		public function Update(e:Event):void {
			m_world.Step(m_timeStep, m_iterations);
			if (mouseJoint) {
				var mouseXWorldPhys=mouseX/30;
				var mouseYWorldPhys=mouseY/30;
				var p2:b2Vec2=new b2Vec2(mouseXWorldPhys,mouseYWorldPhys);
				mouseJoint.SetTarget(p2);
			}
		}
		public var m_world:b2World;
		public var m_iterations:int=10;
		public var m_timeStep:Number=1.0/30.0;
	}
}

So now we are going to look at lines 22-34

22
23
24
25
26
27
28
29
30
31
32
33
34
var m_sprite:Sprite;
m_sprite = new Sprite();
addChild(m_sprite);
var dbgDraw:b2DebugDraw = new b2DebugDraw();
var dbgSprite:Sprite = new Sprite();
m_sprite.addChild(dbgSprite);
dbgDraw.m_sprite=m_sprite;
dbgDraw.m_drawScale=30;
dbgDraw.m_alpha = 1;
dbgDraw.m_fillAlpha=0.5;
dbgDraw.m_lineThickness=1;
dbgDraw.m_drawFlags=b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit|b2DebugDraw.e_coreShapeBit|b2DebugDraw.e_aabbBit|b2DebugDraw.e_obbBit|b2DebugDraw.e_pairBit|b2DebugDraw.e_centerOfMassBit;
m_world.SetDebugDraw(dbgDraw);

Line 22: Declaring a new sprite called m_sprite

Line 23: Creating the sprite itself

Line 24: Adding the sprite to stage

Line 25: Declaring a new b2DebugDraw variable called dbgDraw

Line 26: Declaring and creating a new sprite called dbgSprite. This line acts like lines 22 and 23

Line 27: Adding this sprite to stage as a child of m_sprite

Line 28: Setting m_sprite as the sprite to use to render the world

You should not change anything until line 28

Now, it’s time to look at the ways to custom the debug draw. We will custom it by changing its attributes.

m_sprite as said, defines the sprite to be used for rendering

m_drawScale (line 29) sets the drawing scale of the rendering. As seen in Understanding pixels and meters with Box2D and how to select an object with mouse – part 2, one meter is equal to 30 pixels, so setting m_drawscale to 30 allows me to use pixel units.

This is how my movie looks like if I don’t set m_drawscale:

m_alpha (line 30) sets the alpha (from 0 to 1) for objects drawing color

m_fillAlpha (line 31) sets the alpha (from 0 to 1) for objects filling color

m_lineThickness (line 32) sets the thickness in pixels for objects drawing line

m_drawFlags (line 33) allows you to set some drawing flags. Before I explain every flag, let’s see how our movieclip will look like if I don’t set m_drawFlags

and now let’s see the flags you can assign, separed by a pipe |:

e_shapeBit: draws shapes
e_jointBit: draws joint connections
e_coreShapeBit: draws core (TOI) shapes
e_aabbBit: draws axis aligned bounding boxes
e_obbBit: draws oriented bounding boxes
e_pairBit: draws broad-phase pairs
e_centerOfMassBit: draws center of mass frame

The only thing you cannot change from here is the color of the objects: by default, Box2D assigns a green to static objects, a red to sleeping ones and a grey for moving ones.

You can change the colors editing b2World.as at lines 1060-1074 (line numbers may vary according to Box2D distribution)

1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
for (s = b.GetShapeList(); s; s = s.m_next)
{
	if (b.IsStatic())
	{
		DrawShape(s, xf, new b2Color(0.5, 0.9, 0.5), core);
	}
	else if (b.IsSleeping())
	{
		DrawShape(s, xf, new b2Color(0.5, 0.5, 0.9), core);
	}
	else
	{
		DrawShape(s, xf, new b2Color(0.9, 0.9, 0.9), core);
	}
}

You just have to change values inside b2Color with values from 0 to 1 where 1 is the 255 value we are used to deal with when working with colors.

We are almost ready to make our first Box2D game…

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (14 votes, average: 4.93 out of 5)
Loading ... Loading ...
If you found this post useful, please consider a small donation.
» Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.

7 Responses

  1. sabatino says:

    Great explanation, thanks! :D
    viva italia :p

  2. anlik says:

    Great man, I like your all flash game tutorial, especially Box2D!!! Thanks your work!!!

  3. [...] Lines 19-27: These lines handle the debug draw. Remember Box2D does not draw anything, it just calculates position, rotation and motion of every object in the world. So it’s up to you to attach real graphic assets to worl objects. Anyway, with some lines you can enable a debug draw mode that will help you during the creation of your script. For more information about debug draw read Understanding Box2D debug draw. [...]

  4. laura says:

    Hi. Very interesting to know

  5. laura says:

    Can you tell me where to change the color of joints?
    I could not find that

  6. Cem says:

    However code fails at line 95
    1046: Type was not found or was not a compile-time constant: b2Body.

Leave a Reply