Talking about Actionscript 3, Box2D and Flash.
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
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
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)
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...
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.