Tutorial Details
- Difficulty: Intermediate
- Platform: Flash (Flash Player 9)
- Language: AS3
- Software used: Flash Pro CS3 and FlashDevelop, but you can use any AS3 compiler and editor
- Estimated Completion Time: One hour
- Introduction to Box2D for Flash and AS3
- Box2D for Flash and AS3: Bitmaps and Boxes
- Box2D for Flash and AS3: Building Structure
In the first two parts of this series, we created two simple types of object, rendered using Flash’s display list. In this part, we’ll see how to extend this to let us create as many different types of object as we want, without our code getting all tangled up!
I recommend reading this series from the start, but if you want to start here, then grab this zip file — it contains the source code from the end of the the second part. (The Download button above contains the source code from the end of this part.)
Step 1: New Balls Please
Let’s take a quick look at where we left off:
Bunch of wheels and crates bouncing around. The wheels don’t all have the same properties; each wheel has a random size, restitution, friction, and density. Ditto for the crates.
That’s been fine for testing, but now let’s make things a little less random. We’ll start by replacing the wheels with tennis balls, which all have the same properties.
First, draw a tennis ball (or get a photo of one). Here’s mine:

To make yours consistent with mine, make sure that it’s called TennisBall.png, that you save it in \lib\, that it’s 35x35px, and that it has a transparent background. (You can change any of these points, if you wish, but you’ll have to figure out how to modify your code accordingly.)
Embed the PNG into your Main.as file, just like we’ve done before:
[Embed(source='../lib/TennisBall.png')] public var TennisBall:Class;
Now, create two arrays: one for your tennis ball Box2D objects, and one for the corresponding images:
public var tennisBallArray:Array; public var tennisBallImages:Array;
tennisBallArray = new Array(); tennisBallImages = new Array();
(This is all much the same as we did in the second part of this series, so take another look at that if you need a refresher.)
Now we need a function to create a single tennis ball. Here’s what we use to create a single wheel:
private function createWheel(mRadius:Number, pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
var wheelBodyDef:b2BodyDef = new b2BodyDef();
wheelBodyDef.type = b2Body.b2_dynamicBody;
wheelBodyDef.position.Set(pxStartX * pxToM, pxStartY * pxToM);
var wheelBody:b2Body = world.CreateBody(wheelBodyDef);
var circleShape:b2CircleShape = new b2CircleShape(mRadius);
var wheelFixtureDef:b2FixtureDef = new b2FixtureDef();
wheelFixtureDef.shape = circleShape;
wheelFixtureDef.restitution = (Math.random() * 0.5) + 0.5;
wheelFixtureDef.friction = (Math.random() * 1.0);
wheelFixtureDef.density = Math.random() * 20;
var wheelFixture:b2Fixture = wheelBody.CreateFixture(wheelFixtureDef);
var startingVelocity:b2Vec2 = new b2Vec2(mVelocityX, mVelocityY);
wheelBody.SetLinearVelocity(startingVelocity);
wheelArray.push(wheelBody);
var wheelImage:Bitmap = new SimpleWheel();
wheelImage.width = mRadius * 2 * mToPx;
wheelImage.height = mRadius * 2 * mToPx;
var wheelSprite:Sprite = new Sprite();
wheelSprite.addChild(wheelImage);
wheelSprite.x = pxStartX;
wheelSprite.y = pxStartY;
wheelImages.push(wheelSprite);
this.addChild(wheelSprite);
wheelSprite.addEventListener(MouseEvent.CLICK, onClickWheelImage);
}
We can copy this and modify it to our needs. Since all tennis balls will have the same properties (size, restitution, friction, and density), we don’t need the mRadius argument, and we don’t need to use random numbers anywhere. It’s hard to guess what values of restitution and so on will be correct, so just have a go – we can always correct them later.
Here’s my createTennisBall() function:
private function createTennisBall(pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
var tennisBallBodyDef:b2BodyDef = new b2BodyDef();
tennisBallBodyDef.type = b2Body.b2_dynamicBody;
tennisBallBodyDef.position.Set(pxStartX * pxToM, pxStartY * pxToM);
var tennisBallBody:b2Body = world.CreateBody(tennisBallBodyDef);
var circleShape:b2CircleShape = new b2CircleShape(35 * .5 * pxToM);
var tennisBallFixtureDef:b2FixtureDef = new b2FixtureDef();
tennisBallFixtureDef.shape = circleShape;
tennisBallFixtureDef.restitution = 0.8;
tennisBallFixtureDef.friction = 0.75;
tennisBallFixtureDef.density = 1.0;
var tennisBallFixture:b2Fixture = tennisBallBody.CreateFixture(tennisBallFixtureDef);
var startingVelocity:b2Vec2 = new b2Vec2(mVelocityX, mVelocityY);
tennisBallBody.SetLinearVelocity(startingVelocity);
tennisBallArray.push(tennisBallBody);
var tennisBallImage:Bitmap = new TennisBall();
var tennisBallSprite:Sprite = new Sprite();
tennisBallSprite.addChild(tennisBallImage);
tennisBallSprite.x = pxStartX;
tennisBallSprite.y = pxStartY;
tennisBallImages.push(tennisBallSprite);
this.addChild(tennisBallSprite);
//tennisBallSprite.addEventListener(MouseEvent.CLICK, onClickWheelImage);
}
A few things to note:
- The
b2CircleShape()constructor is expecting to receive the radius of the tennis ball, in meters, and I don’t want to squash or enlarge the image I’ve made. I guess I could look up the radius of an actual tennis ball, enter this value in here, and alter thepxToMscale factor to make everything else fit, but instead I’ve just told the constructor to convert the pixel radius of my tennis ball image to meters. - Restituion is the bounciness of the object, on a scale of 0 to 1; tennis balls are pretty bouncy so I’ve set this quite high.
- Friction is also between 0 and 1; tennis balls are quite rough so I set this fairly high, too.
- Density is not limited to any particular range; tennis balls are mostly air, so I set this to the same value as my crates (which I’m also assuming are empty).
- Unlike in
createWheel(), there’s no need to resize the image (as I mentioned above), so I’ve deleted those lines. - I’ve just commented the
onClickWheelImageevent listener out for now, as it’s not relevant.
Try to compile the SWF. It should work – if it didn’t, you missed something out – but since we aren’t actually calling createTennisBall() from anywhere, nothing will be different.
Step 2: Creating Tennis Balls
Let’s just remove the wheels straight away, and replace them with tennis balls. Change this code, in getStarted():
for (var i:int = 0; i < 20; i++)
{
createWheel(
Math.random() * 0.5,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createCrate(
Math.random() * 1.5,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
)
}
…to this:
for (var i:int = 0; i < 20; i++)
{
createTennisBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createCrate(
Math.random() * 1.5,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
)
}
Note that the first argument has been deleted, since we don’t manually set the size any more, but all other arguments remain the same. Try it out:
Hmm… not quite right. Ah, of course – we didn’t add any code to render the tennis balls. We’ll do that next. Change onTick() from this:
private function onTick(a_event:TimerEvent):void
{
world.Step(0.025, 10, 10);
world.DrawDebugData();
var wheelImage:Sprite;
for each (var wheelBody:b2Body in wheelArray)
{
wheelImage = wheelImages[wheelArray.indexOf(wheelBody)];
wheelImage.x = (wheelBody.GetPosition().x * mToPx) - (wheelImage.width * 0.5);
wheelImage.y = (wheelBody.GetPosition().y * mToPx) - (wheelImage.height * 0.5);
}
var crateImage:Sprite;
for each (var crateBody:b2Body in crateArray)
{
crateImage = crateImages[crateArray.indexOf(crateBody)];
crateImage.x = (crateBody.GetPosition().x * mToPx);// - (crateImage.width * 0.5);
crateImage.y = (crateBody.GetPosition().y * mToPx);// - (crateImage.height * 0.5);
crateImage.rotation = crateBody.GetAngle() * radToDeg;
}
}
…to this:
private function onTick(a_event:TimerEvent):void
{
world.Step(0.025, 10, 10);
world.DrawDebugData();
var tennisBallImage:Sprite;
for each (var tennisBallBody:b2Body in tennisBallArray)
{
tennisBallImage = tennisBallImages[tennisBallArray.indexOf(tennisBallBody)];
tennisBallImage.x = (tennisBallBody.GetPosition().x * mToPx) - (tennisBallImage.width * 0.5);
tennisBallImage.y = (tennisBallBody.GetPosition().y * mToPx) - (tennisBallImage.height * 0.5);
}
var crateImage:Sprite;
for each (var crateBody:b2Body in crateArray)
{
crateImage = crateImages[crateArray.indexOf(crateBody)];
crateImage.x = (crateBody.GetPosition().x * mToPx);// - (crateImage.width * 0.5);
crateImage.y = (crateBody.GetPosition().y * mToPx);// - (crateImage.height * 0.5);
crateImage.rotation = crateBody.GetAngle() * radToDeg;
}
}
(It’s really just a case of doing a find-and-replace, switching “wheel” to “tennisBall”.) Try the SWF now:
Better, but we don’t have any rotation (we didn’t need it before, because the wheels were all rotationally symmetric). Add that now:
var tennisBallImage:Sprite;
for each (var tennisBallBody:b2Body in tennisBallArray)
{
tennisBallImage = tennisBallImages[tennisBallArray.indexOf(tennisBallBody)];
tennisBallImage.x = (tennisBallBody.GetPosition().x * mToPx) - (tennisBallImage.width * 0.5);
tennisBallImage.y = (tennisBallBody.GetPosition().y * mToPx) - (tennisBallImage.height * 0.5);
tennisBallImage.rotation = tennisBallBody.GetAngle() * radToDeg;
}
Ah. Remember we had this problem with the crates? Remember how we solved it? Have a go at doing the same here, on your own. My solution is below if you want to check:
private function createTennisBall(pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
var tennisBallBodyDef:b2BodyDef = new b2BodyDef();
tennisBallBodyDef.type = b2Body.b2_dynamicBody;
tennisBallBodyDef.position.Set(pxStartX * pxToM, pxStartY * pxToM);
var tennisBallBody:b2Body = world.CreateBody(tennisBallBodyDef);
var circleShape:b2CircleShape = new b2CircleShape(35 * .5 * pxToM);
var tennisBallFixtureDef:b2FixtureDef = new b2FixtureDef();
tennisBallFixtureDef.shape = circleShape;
tennisBallFixtureDef.restitution = 0.8;
tennisBallFixtureDef.friction = 0.75;
tennisBallFixtureDef.density = 1.0;
var tennisBallFixture:b2Fixture = tennisBallBody.CreateFixture(tennisBallFixtureDef);
var startingVelocity:b2Vec2 = new b2Vec2(mVelocityX, mVelocityY);
tennisBallBody.SetLinearVelocity(startingVelocity);
tennisBallArray.push(tennisBallBody);
var tennisBallImage:Bitmap = new TennisBall();
var tennisBallSprite:Sprite = new Sprite();
tennisBallSprite.addChild(tennisBallImage);
tennisBallImage.x -= tennisBallImage.width / 2;
tennisBallImage.y -= tennisBallImage.height / 2;
tennisBallSprite.x = pxStartX;
tennisBallSprite.y = pxStartY;
tennisBallImages.push(tennisBallSprite);
this.addChild(tennisBallSprite);
//tennisBallSprite.addEventListener(MouseEvent.CLICK, onClickWheelImage);
}
//...
private function onTick(a_event:TimerEvent):void
{
world.Step(0.025, 10, 10);
world.DrawDebugData();
var tennisBallImage:Sprite;
for each (var tennisBallBody:b2Body in tennisBallArray)
{
tennisBallImage = tennisBallImages[tennisBallArray.indexOf(tennisBallBody)];
tennisBallImage.x = (tennisBallBody.GetPosition().x * mToPx);// - (tennisBallImage.width * 0.5);
tennisBallImage.y = (tennisBallBody.GetPosition().y * mToPx);// - (tennisBallImage.height * 0.5);
tennisBallImage.rotation = tennisBallBody.GetAngle() * radToDeg;
}
var crateImage:Sprite;
for each (var crateBody:b2Body in crateArray)
{
crateImage = crateImages[crateArray.indexOf(crateBody)];
crateImage.x = (crateBody.GetPosition().x * mToPx);// - (crateImage.width * 0.5);
crateImage.y = (crateBody.GetPosition().y * mToPx);// - (crateImage.height * 0.5);
crateImage.rotation = crateBody.GetAngle() * radToDeg;
}
}
Test it out:
Ace.
Step 3: Creating Bowling Balls
I’d like to add three more types of ball: a bowling ball, a basketball, and a rubber ball. As you can see from the above, it’s a pain to do the same thing over and over again. We’ll add the bowling ball next, and try to simplify the process so we don’t have to keep making this irritating little changes.
Here’s my bowling ball image. Again, feel free to use your own, but try to make it 60x60px with the name BowlingBall.png:

Embed the image and add the necessary arrays:
[Embed(source='../lib/BowlingBall.png')] public var BowlingBall:Class; //... public var bowlingBallArray:Array; public var bowlingBallImages:Array; //... bowlingBallArray = new Array(); bowlingBallImages = new Array();
Write a createBowlingBall() function:
private function createBowlingBall(pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
var bowlingBallBodyDef:b2BodyDef = new b2BodyDef();
bowlingBallBodyDef.type = b2Body.b2_dynamicBody;
bowlingBallBodyDef.position.Set(pxStartX * pxToM, pxStartY * pxToM);
var bowlingBallBody:b2Body = world.CreateBody(bowlingBallBodyDef);
var circleShape:b2CircleShape = new b2CircleShape(60 * .5 * pxToM);
var bowlingBallFixtureDef:b2FixtureDef = new b2FixtureDef();
bowlingBallFixtureDef.shape = circleShape;
bowlingBallFixtureDef.restitution = 0.0; //not bouncy
bowlingBallFixtureDef.friction = 0.0; //very smooth
bowlingBallFixtureDef.density = 20.0; //very heavy
var bowlingBallFixture:b2Fixture = bowlingBallBody.CreateFixture(bowlingBallFixtureDef);
var startingVelocity:b2Vec2 = new b2Vec2(mVelocityX, mVelocityY);
bowlingBallBody.SetLinearVelocity(startingVelocity);
bowlingBallArray.push(bowlingBallBody);
var bowlingBallImage:Bitmap = new BowlingBall();
var bowlingBallSprite:Sprite = new Sprite();
bowlingBallSprite.addChild(bowlingBallImage);
bowlingBallImage.x -= bowlingBallImage.width / 2;
bowlingBallImage.y -= bowlingBallImage.height / 2;
bowlingBallSprite.x = pxStartX;
bowlingBallSprite.y = pxStartY;
bowlingBallImages.push(bowlingBallSprite);
this.addChild(bowlingBallSprite);
//bowlingBallSprite.addEventListener(MouseEvent.CLICK, onClickWheelImage);
}
…and create the actual objects:
for (var i:int = 0; i < 20; i++)
{
createTennisBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBowlingBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createCrate(
Math.random() * 1.5,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
)
}
Phew. Okay, now for the rendering. Let’s take another look at our rendering code so far:
private function onTick(a_event:TimerEvent):void
{
world.Step(0.025, 10, 10);
world.DrawDebugData();
var tennisBallImage:Sprite;
for each (var tennisBallBody:b2Body in tennisBallArray)
{
tennisBallImage = tennisBallImages[tennisBallArray.indexOf(tennisBallBody)];
tennisBallImage.x = (tennisBallBody.GetPosition().x * mToPx);// - (tennisBallImage.width * 0.5);
tennisBallImage.y = (tennisBallBody.GetPosition().y * mToPx);// - (tennisBallImage.height * 0.5);
tennisBallImage.rotation = tennisBallBody.GetAngle() * radToDeg;
}
var crateImage:Sprite;
for each (var crateBody:b2Body in crateArray)
{
crateImage = crateImages[crateArray.indexOf(crateBody)];
crateImage.x = (crateBody.GetPosition().x * mToPx);// - (crateImage.width * 0.5);
crateImage.y = (crateBody.GetPosition().y * mToPx);// - (crateImage.height * 0.5);
crateImage.rotation = crateBody.GetAngle() * radToDeg;
}
}
Notice anything? Thanks to the change we made in the last step – to fix the rotation problem – the two highlighted sections are almost identical; just swap out the word “tennisBall” for “crate” in each case and they’re a perfect match.
This suggests that we could combine them into a single loop, which could be used to render all objects. That way, we wouldn’t need to create a new loop to render the bowling balls. Let’s look at this next.
Step 4: Rendering in a Single Loop
To do this, we just need to combine the tennisBallArray and crateArray into a single array. It’s easier if I show you:
private function onTick(a_event:TimerEvent):void
{
world.Step(0.025, 10, 10);
world.DrawDebugData();
var generalB2BodyArray:Array = new Array();
generalB2BodyArray = generalB2BodyArray.concat(tennisBallArray, crateArray);
var generalImages:Array = new Array();
generalImages = generalImages.concat(tennisBallImages, crateImages);
var generalImage:Sprite;
for each (var generalBody:b2Body in generalB2BodyArray)
{
generalImage = generalImages[generalB2BodyArray.indexOf(generalBody)];
generalImage.x = (generalBody.GetPosition().x * mToPx);
generalImage.y = (generalBody.GetPosition().y * mToPx);
generalImage.rotation = generalBody.GetAngle() * radToDeg;
}
}
See how that works? In lines 6-10, we create a couple of new arrays: one contains all the tennis ball and crate b2Body objects, and the other contains all the tennis ball and crate Sprites. Most importantly, they contain them in the same order – if the 17th element of generalImages[] is a tennis ball, then the 17th eleemnt of generalB2BodyArray[] is the same tennis ball’s b2Body.
The for loop then does exactly the same as the two before it did; it’s just way more general. Try it out:
The bowling ball images don’t move, but look how easy it is to add them to the renderer:
private function onTick(a_event:TimerEvent):void
{
world.Step(0.025, 10, 10);
world.DrawDebugData();
var generalB2BodyArray:Array = new Array();
generalB2BodyArray = generalB2BodyArray.concat(tennisBallArray, crateArray, bowlingBallArray);
var generalImages:Array = new Array();
generalImages = generalImages.concat(tennisBallImages, crateImages, bowlingBallImages);
var generalImage:Sprite;
for each (var generalBody:b2Body in generalB2BodyArray)
{
generalImage = generalImages[generalB2BodyArray.indexOf(generalBody)];
generalImage.x = (generalBody.GetPosition().x * mToPx);
generalImage.y = (generalBody.GetPosition().y * mToPx);
generalImage.rotation = generalBody.GetAngle() * radToDeg;
}
}
That’s all it takes! Check it out:
It’s beginning to run a little slowly on my machine, so I’m going to reduce the number of objects from 60 to 30:
for (var i:int = 0; i < 10; i++)
{
createTennisBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBowlingBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createCrate(
Math.random() * 1.5,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
)
}
There’s more room for them now, too:
Step 5: Creating Basketballs
Basketballs, next. My image is 75x75px and called Basketball.png:

As usual, start by embedding the image as a class:
[Embed(source='../lib/Basketball.png')] public var Basketball:Class;
Now we’ll add the two arrays for the basketball b2Body objects and Sprites. Except… wait. Why do we need a separate array for these?
We don’t, actually! Not any more. Let’s combine all our b2Body objects into one array, and all our Sprites into another. Change this:
public var crateArray:Array; public var crateImages:Array; public var tennisBallArray:Array; public var tennisBallImages:Array; public var bowlingBallArray:Array; public var bowlingBallImages:Array;
…to this:
public var generalB2BodyArray:Array; public var generalImages:Array;
Change this:
wheelArray = new Array(); wheelImages = new Array(); crateArray = new Array(); crateImages = new Array(); tennisBallArray = new Array(); tennisBallImages = new Array(); bowlingBallArray = new Array(); bowlingBallImages = new Array();
…to this:
generalB2BodyArray = new Array(); generalImages = new Array();
In every create«Whatever» function, change the equivalent of this:
tennisBallArray.push(tennisBallBody);
…to this:
generalB2BodyArray.push(tennisBallBody); //or .push(crateBody), or whatever
…and this:
tennisBallImages.push(tennisBallSprite);
…to this:
generalImages.push(tennisBallSprite); //or .push(crateSprite), or whatever
We can make onClickWheelImage() applicable to all types of objects very easily now; just change this one line:
private function onClickWheelImage(event:MouseEvent):void
{
var spriteThatWasClicked:Sprite = event.target as Sprite;
var relatedBody:b2Body = wheelArray[wheelImages.indexOf(spriteThatWasClicked)];
var centerX:Number = spriteThatWasClicked.x + (spriteThatWasClicked.width / 2);
var centerY:Number = spriteThatWasClicked.y + (spriteThatWasClicked.height / 2);
var xDistance:Number = centerX - event.stageX;
var yDistance:Number = centerY - event.stageY;
var xImpulse:Number = 60 * xDistance * pxToM; //I picked 60 simply because
var yImpulse:Number = 60 * yDistance * pxToM; //it "felt right" to me
relatedBody.ApplyImpulse(
new b2Vec2(xImpulse, yImpulse),
relatedBody.GetPosition()
);
}
…like so:
private function onClickWheelImage(event:MouseEvent):void
{
var spriteThatWasClicked:Sprite = event.target as Sprite;
var relatedBody:b2Body = generalB2BodyArray[generalImages.indexOf(spriteThatWasClicked)];
var centerX:Number = spriteThatWasClicked.x + (spriteThatWasClicked.width / 2);
var centerY:Number = spriteThatWasClicked.y + (spriteThatWasClicked.height / 2);
var xDistance:Number = centerX - event.stageX;
var yDistance:Number = centerY - event.stageY;
var xImpulse:Number = 60 * xDistance * pxToM; //I picked 60 simply because
var yImpulse:Number = 60 * yDistance * pxToM; //it "felt right" to me
relatedBody.ApplyImpulse(
new b2Vec2(xImpulse, yImpulse),
relatedBody.GetPosition()
);
}
Finally, in onTick(), we can delete the lines highlighted below:
private function onTick(a_event:TimerEvent):void
{
world.Step(0.025, 10, 10);
world.DrawDebugData();
var generalB2BodyArray:Array = new Array();
generalB2BodyArray = generalB2BodyArray.concat(tennisBallArray, crateArray, bowlingBallArray);
var generalImages:Array = new Array();
generalImages = generalImages.concat(tennisBallImages, crateImages, bowlingBallImages);
var generalImage:Sprite;
for each (var generalBody:b2Body in generalB2BodyArray)
{
generalImage = generalImages[generalB2BodyArray.indexOf(generalBody)];
generalImage.x = (generalBody.GetPosition().x * mToPx);
generalImage.y = (generalBody.GetPosition().y * mToPx);
generalImage.rotation = generalBody.GetAngle() * radToDeg;
}
}
Compile your SWF to make sure it works:
If it doesn’t, it’s probably due to a missed array somewhere; just check your error messages and try again.
That took a while, but it makes it much simpler to add new objects. We’re gradually streamlining the whole process.
There’s another simplification we can make to these arrays, though…
Step 6: Linking Images to b2Body Objects
Why do we have the generalImages[] array? It’s simply so that we can link a b2Body to its associated image, and vice-versa. But there’s a better way to do this.
Every b2Body has an internal “user data” object – this can be set to any object you like, and can be retrieved from anywhere. We can set this to point to the b2Body object’s Sprite.
It’s simple to do; in each create«Whatever»() function, alter the equivalent of this line:
generalImages.push(tennisBallSprite);
…to the equivalent of this:
tennisBallBody.SetUserData(tennisBallSprite);
Then, in onTick(), instead of finding the image from an array, we can find it using the user data:
private function onTick(a_event:TimerEvent):void
{
world.Step(0.025, 10, 10);
world.DrawDebugData();
var generalImage:Sprite;
for each (var generalBody:b2Body in generalB2BodyArray)
{
generalImage = generalBody.GetUserData() as Sprite;
generalImage.x = (generalBody.GetPosition().x * mToPx);
generalImage.y = (generalBody.GetPosition().y * mToPx);
generalImage.rotation = generalBody.GetAngle() * radToDeg;
}
}
(If you wanted to, you could replace the user data with a sprite sheet and use blitting to render the objects here. It’s quite simple to do so, with this structure.)
You could actually remove the generalImages[] array altogether now… apart from one thing: it’s still used in onClickWheelImage() to get a reference from the image to the b2Body.
We could just comment onClickWheelImage() out, because we’re not using it right now, but that would be lazy. And this raises a good point, anyway: we need to be able to find any image’s associated b2Body. Unfortunately, we’re using the Sprite class, and it doesn’t have a built-in “user data” – but we could give it one.
Create a new class, called B2Sprite, extending Sprite, in your \src\ folder:
package
{
import flash.display.Sprite;
public class B2Sprite extends Sprite
{
public function B2Sprite()
{
}
}
}
(I’ve used a capital B to distinguish it from the official Box2D classes.)
Create a b2Body property:
package
{
import Box2D.Dynamics.b2Body;
import flash.display.Sprite;
public class B2Sprite extends Sprite
{
public var body:b2Body;
public function B2Sprite()
{
}
}
}
Because this extends Sprite, it’ll act exactly the same as a Sprite, except with that extra property that we need.
Now, in every create«Whatever»() function, change the equivalent of this code:
var tennisBallSprite:Sprite = new Sprite();
…to this:
var tennisBallSprite:B2Sprite = new B2Sprite();
Also, wherever you have the equivalent of this line:
tennisBallBody.SetUserData(tennisBallSprite);
…add another line underneath it, to create a connection from the B2Sprite to the b2Body:
tennisBallBody.SetUserData(tennisBallSprite); tennisBallSprite.body = tennisBallBody;
Then, you can alter onClickWheelImage(), like so:
private function onClickWheelImage(event:MouseEvent):void
{
if (event.target is B2Sprite)
{
var spriteThatWasClicked:B2Sprite = event.target as B2Sprite;
var relatedBody:b2Body = spriteThatWasClicked.body;
var centerX:Number = spriteThatWasClicked.x + (spriteThatWasClicked.width / 2);
var centerY:Number = spriteThatWasClicked.y + (spriteThatWasClicked.height / 2);
var xDistance:Number = centerX - event.stageX;
var yDistance:Number = centerY - event.stageY;
var xImpulse:Number = 60 * xDistance * pxToM; //I picked 60 simply because
var yImpulse:Number = 60 * yDistance * pxToM; //it "felt right" to me
relatedBody.ApplyImpulse(
new b2Vec2(xImpulse, yImpulse),
relatedBody.GetPosition()
);
}
}
Now that you’ve done all of that, you can uncomment out the lines that add event listeners for onClickWheelImage() (in the tennis ball and bowling ball creation functions), and try out your SWF:
Click a tennis ball or bowling ball to see the effect.
Rename the onClickWheelImage() function to onClickClickableImage(). If you like, you could try making the crates clickable, too!
Step 7: Creating Basketballs (Still)
Hey, weren’t we adding basketballs? Let’s get back to that.
So, we’ve removed the image array, which means we were about to write the createBasketball() function. But, wait… I can’t help but wonder whether we need the generalB2BodyArray[].
It turns out that we don’t. The b2world contains a list of all b2Body objects inside it, so the array is unnecessary. We’ll just make a quick change – I promise – and then we can add the basketball.
This list is not an array. It works like this:
- You request
world.GetBodyList(), and this gives you the first b2Body object in the list. - With this body, you call
.GetNext()to retrieve the next b2Body object in the list. - If
.GetNext()returnsnull, you’ve reached the end of the list.
Have a go at re-writing the onTick() rendering code to use this list instead of the generalB2BodyArray[]. Here’s my solution if you want to check:
private function onTick(a_event:TimerEvent):void
{
world.Step(0.025, 10, 10);
world.DrawDebugData();
var currentBody:b2Body = world.GetBodyList();
var currentImage:B2Sprite;
while (currentBody != null)
{
if (currentBody.GetUserData() is B2Sprite) //important, since the boundaries don't have associated images
{
currentImage = currentBody.GetUserData() as B2Sprite;
currentImage.x = (currentBody.GetPosition().x * mToPx);
currentImage.y = (currentBody.GetPosition().y * mToPx);
currentImage.rotation = currentBody.GetAngle() * radToDeg;
}
currentBody = currentBody.GetNext();
}
}
(I’ve taken the opportunity to add some additional checks and change the name of some variables; you don’t have to do this, but it’s probably easier if you do, to keep our code consistent.)
You can now remove all references to generalB2BodyArray[]. Make sure you test your SWF to check it still runs fine:
Creating that basketball is now pretty simple. First, write createBasketball():
private function createBasketball(pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
var basketballBodyDef:b2BodyDef = new b2BodyDef();
basketballBodyDef.type = b2Body.b2_dynamicBody;
basketballBodyDef.position.Set(pxStartX * pxToM, pxStartY * pxToM);
var basketballBody:b2Body = world.CreateBody(basketballBodyDef);
var circleShape:b2CircleShape = new b2CircleShape(75 * .5 * pxToM);
var basketballFixtureDef:b2FixtureDef = new b2FixtureDef();
basketballFixtureDef.shape = circleShape;
basketballFixtureDef.restitution = 0.6; //fairly bouncy
basketballFixtureDef.friction = 0.4; //a little rough
basketballFixtureDef.density = 2.5; //pretty light
var basketballFixture:b2Fixture = basketballBody.CreateFixture(basketballFixtureDef);
var startingVelocity:b2Vec2 = new b2Vec2(mVelocityX, mVelocityY);
basketballBody.SetLinearVelocity(startingVelocity);
var basketballImage:Bitmap = new Basketball();
var basketballSprite:B2Sprite = new B2Sprite();
basketballSprite.addChild(basketballImage);
basketballImage.x -= basketballImage.width / 2;
basketballImage.y -= basketballImage.height / 2;
basketballSprite.x = pxStartX;
basketballSprite.y = pxStartY;
basketballBody.SetUserData(basketballSprite);
this.addChild(basketballSprite);
basketballSprite.body = basketballBody;
basketballSprite.addEventListener(MouseEvent.CLICK, onClickClickableImage);
}
…then, create a whole bunch of them:
for (var i:int = 0; i < 10; i++)
{
createTennisBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBowlingBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBasketball(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createCrate(
Math.random() * 1.5,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
)
}
Done! Try it:
He shoots, he scores.
Step 8: Making Object Construction Easier
Every time we write a new create«Whatever»() function, we copy and paste an old one. They’re almost identical. And, as you’ve seen, that usually means we can improve our code structure to prevent us from having to copy and paste the same lines over and over again; it means there’s a better way to do things, which will make it easier for us to create new objects in the future.
Look at this:
private function createTennisBall(pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
var tennisBallBodyDef:b2BodyDef = new b2BodyDef();
tennisBallBodyDef.type = b2Body.b2_dynamicBody;
tennisBallBodyDef.position.Set(pxStartX * pxToM, pxStartY * pxToM);
var tennisBallBody:b2Body = world.CreateBody(tennisBallBodyDef);
var circleShape:b2CircleShape = new b2CircleShape(35 * .5 * pxToM);
var tennisBallFixtureDef:b2FixtureDef = new b2FixtureDef();
tennisBallFixtureDef.shape = circleShape;
tennisBallFixtureDef.restitution = 0.8;
tennisBallFixtureDef.friction = 0.75;
tennisBallFixtureDef.density = 1.0;
var tennisBallFixture:b2Fixture = tennisBallBody.CreateFixture(tennisBallFixtureDef);
var startingVelocity:b2Vec2 = new b2Vec2(mVelocityX, mVelocityY);
tennisBallBody.SetLinearVelocity(startingVelocity);
var tennisBallImage:Bitmap = new TennisBall();
var tennisBallSprite:B2Sprite = new B2Sprite();
tennisBallSprite.addChild(tennisBallImage);
tennisBallImage.x -= tennisBallImage.width / 2;
tennisBallImage.y -= tennisBallImage.height / 2;
tennisBallSprite.x = pxStartX;
tennisBallSprite.y = pxStartY;
tennisBallBody.SetUserData(tennisBallSprite);
tennisBallSprite.body = tennisBallBody;
this.addChild(tennisBallSprite);
tennisBallSprite.addEventListener(MouseEvent.CLICK, onClickClickableImage);
}
I’ve been using it as the template for all my b2Body creation functions. Here’s how they all boil down:
private function create«Whatever»(pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
//create body def and set its starting position
//create body
//create shape and set its type and size
//create fixture def and set its properties
//create fixture
//set object's starting velocity
//create instance of image and add it to stage at correct position
//link image to body (and vice-versa)
//add required event listener to image
}
For our round objects, some of these steps are identical, and most of them only differ by a couple of parameters or instance names. So, what if we could inherit all that identical code from some general createRoundObject() function?
We can’t do exactly that, but we can do something similar. Create a new folder in \src\ called \builders\, and create a new class inside that called RoundObjectBuilder.as:
package builders
{
public class RoundObjectBuilder
{
public function RoundObjectBuilder()
{
}
}
}
This is going to take a b2world and create a b2Body, so add appropriate code for that:
package builders
{
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2World;
public class RoundObjectBuilder
{
public var body:b2Body;
protected var world:b2World, pxToM:Number;
public function RoundObjectBuilder(world:b2World, pxToM:Number)
{
this.world = world; //sets this.world to parameter called world
this.pxToM = pxToM;
}
}
}
It’s going to create a round object, so we’ll give it a function to create such a shape:
package builders
{
import Box2D.Collision.Shapes.b2Shape;
import Box2D.Collision.Shapes.b2CircleShape;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2World;
public class RoundObjectBuilder
{
public var body:b2Body;
protected var world:b2World, pxToM:Number;
protected var shape:b2Shape;
public function RoundObjectBuilder(world:b2World, pxToM:Number)
{
this.world = world;
this.pxToM = pxToM;
}
public function createShape():void
{
shape = new b2CircleShape(mRadius);
}
}
}
(Where’s the radius? You’ll see…)
It’s going to create a fixture def, so create a function for that, too:
package builders
{
import Box2D.Collision.Shapes.b2Shape;
import Box2D.Collision.Shapes.b2CircleShape;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2FixtureDef;
import Box2D.Dynamics.b2World;
public class RoundObjectBuilder
{
public var body:b2Body;
protected var world:b2World, pxToM:Number;
protected var shape:b2CircleShape;
protected var fixtureDef:b2FixtureDef;
public function RoundObjectBuilder(world:b2World, pxToM:Number)
{
this.world = world;
this.pxToM = pxToM;
}
public function createShape():void
{
shape = new b2CircleShape(mRadius);
}
public function createFixtureDef():void
{
fixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
fixtureDef.restitution = restitution;
fixtureDef.friction = friction;
fixtureDef.density = density;
}
}
}
(Where do we define the properties? Again, you’ll see…)
It’s going to create a body def, too:
package builders
{
import Box2D.Collision.Shapes.b2Shape;
import Box2D.Collision.Shapes.b2CircleShape;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2FixtureDef;
import Box2D.Dynamics.b2World;
public class RoundObjectBuilder
{
public var body:b2Body;
protected var world:b2World, pxToM:Number;
protected var shape:b2CircleShape;
protected var fixtureDef:b2FixtureDef;
protected var bodyDef:b2BodyDef;
public function RoundObjectBuilder(world:b2World, pxToM:Number)
{
this.world = world;
this.pxToM = pxToM;
}
public function createShape():void
{
shape = new b2CircleShape(mRadius);
}
public function createFixtureDef():void
{
fixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
fixtureDef.restitution = restitution;
fixtureDef.friction = friction;
fixtureDef.density = density;
}
public function createBodyDef(mStartX:Number, mStartY:Number):void
{
bodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.Set(mStartX, mStartY);
}
}
}
It’s going to create an actual body and fixture, and set the body’s initial velocity:
package builders
{
import Box2D.Collision.Shapes.b2Shape;
import Box2D.Collision.Shapes.b2CircleShape;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2Fixture;
import Box2D.Dynamics.b2FixtureDef;
import Box2D.Dynamics.b2World;
public class RoundObjectBuilder
{
public var body:b2Body;
protected var world:b2World, pxToM:Number;
protected var shape:b2CircleShape;
protected var fixtureDef:b2FixtureDef;
protected var bodyDef:b2BodyDef;
protected var fixture:b2Fixture;
protected var startingVelocity:b2Vec2;
public function RoundObjectBuilder(world:b2World, pxToM:Number)
{
this.world = world;
this.pxToM = pxToM;
}
public function createShape():void
{
shape = new b2CircleShape(mRadius);
}
public function createFixtureDef():void
{
fixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
fixtureDef.restitution = restitution;
fixtureDef.friction = friction;
fixtureDef.density = density;
}
public function createBodyDef(mStartX:Number, mStartY:Number):void
{
bodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.Set(mStartX, mStartY);
}
public function createBody():void
{
body = world.CreateBody(bodyDef);
}
public function createFixture():void
{
fixture = body.CreateFixture(fixtureDef);
}
public function setStartingVelocity(mVelocityX:Number, mVelocityY:Number):void
{
startingVelocity = new b2Vec2(mVelocityX, mVelocityY);
body.SetLinearVelocity(startingVelocity);
}
}
}
Next, we add the image:
package builders
{
import Box2D.Collision.Shapes.b2Shape;
import Box2D.Collision.Shapes.b2CircleShape;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2Fixture;
import Box2D.Dynamics.b2FixtureDef;
import Box2D.Dynamics.b2World;
import flash.display.Bitmap;
public class RoundObjectBuilder
{
public var body:b2Body;
protected var world:b2World, pxToM:Number;
protected var shape:b2CircleShape;
protected var fixtureDef:b2FixtureDef;
protected var bodyDef:b2BodyDef;
protected var fixture:b2Fixture;
protected var startingVelocity:b2Vec2;
protected var sprite:B2Sprite;
public function RoundObjectBuilder(world:b2World, pxToM:Number)
{
this.world = world;
this.pxToM = pxToM;
}
public function createShape():void
{
shape = new b2CircleShape(mRadius);
}
public function createFixtureDef():void
{
fixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
fixtureDef.restitution = restitution;
fixtureDef.friction = friction;
fixtureDef.density = density;
}
public function createBodyDef(mStartX:Number, mStartY:Number):void
{
bodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.Set(mStartX, mStartY);
}
public function createBody():void
{
body = world.CreateBody(bodyDef);
}
public function createFixture():void
{
fixture = body.CreateFixture(fixtureDef);
}
public function setStartingVelocity(mVelocityX:Number, mVelocityY:Number):void
{
startingVelocity = new b2Vec2(mVelocityX, mVelocityY);
body.SetLinearVelocity(startingVelocity);
}
public function setImage():void
{
sprite = new B2Sprite();
var bitmap:Bitmap = new Image();
sprite.addChild();
bitmap.x -= bitmap.width / 2;
bitmap.y -= bitmap.height / 2;
body.SetUserData(sprite);
sprite.body = body;
}
public function linkImageAndBody():void
{
body.SetUserData(sprite);
sprite.body = body;
}
}
}
Do you see where we’re going with this? You might have guessed that we’ll eventually create an instance of RoundObjectBuilder and then call createShape(), createFixtureDef(), and so on, all in the right order, with the right parameters, then retrieve the b2Body object after everything’s set up. That’s correct, but there is a little more to it than that.
Step 9: Creating a Specific Builder
First, create protected variables to hold all the properties that are missing:
public var body:b2Body; protected var world:b2World, pxToM:Number; protected var shape:b2CircleShape; protected var fixtureDef:b2FixtureDef; protected var bodyDef:b2BodyDef; protected var fixture:b2Fixture; protected var startingVelocity:b2Vec2; protected var sprite:B2Sprite; protected var mRadius:Number; protected var restitution:Number, friction:Number, density:Number; protected var Image:Class;
Now, create a new class, in your \builders\ folder, called TennisBallBuilder.as, and make sure it extends RoundObjectBuilder:
package builders
{
import Box2D.Dynamics.b2World;
public class TennisBallBuilder extends RoundObjectBuilder
{
public function TennisBallBuilder(world:b2World, pxToM:Number)
{
super(world, pxToM);
}
}
}
(Remember, super() will run the code in the constructor function of RoundObjectBuilder, because we extended it.)
Now, underneath that call to super(), set the restitution, friction, and density of the tennis ball:
package builders
{
import Box2D.Dynamics.b2World;
public class TennisBallBuilder extends RoundObjectBuilder
{
public function TennisBallBuilder(world:b2World, pxToM:Number)
{
super(world, pxToM);
restitution = 0.8;
friction = 0.75;
density = 1.0;
}
}
}
Next, embed the tennis ball image, and use it to set the Image class which will be used to create the bitmap; also, set the radius:
package builders
{
import Box2D.Dynamics.b2World;
public class TennisBallBuilder extends RoundObjectBuilder
{
[Embed(source='../../lib/TennisBall.png')]
public var TennisBall:Class;
public function TennisBallBuilder(world:b2World, pxToM:Number)
{
super(world, pxToM);
restitution = 0.8;
friction = 0.75;
density = 1.0;
Image = TennisBall;
mRadius = 35 * 0.5 * pxToM;
}
}
}
So now we have a class that encapsulates all of the information and all the functions needed to create a tennis ball. We’ve just got to put it all together. Modify Main.createTennisBall() like so:
private function createTennisBall(pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
var tennisBallBuilder:TennisBallBuilder = new TennisBallBuilder(world, pxToM);
tennisBallBuilder.createShape();
tennisBallBuilder.createFixtureDef();
tennisBallBuilder.createBodyDef(pxStartX * pxToM, pxStartY * pxToM);
tennisBallBuilder.createBody();
tennisBallBuilder.createFixture();
tennisBallBuilder.setStartingVelocity(mVelocityX, mVelocityY);
tennisBallBuilder.setImage();
tennisBallBuilder.linkImageAndBody();
var tennisBallSprite:B2Sprite = tennisBallBuilder.body.GetUserData() as B2Sprite;
tennisBallSprite.x = pxStartX;
tennisBallSprite.y = pxStartY;
this.addChild(tennisBallSprite);
tennisBallSprite.addEventListener(MouseEvent.CLICK, onClickClickableImage);
}
If you test your SWF, you’ll see that this still works:
But so what? We haven’t gained much here. In fact, we’ve ended up with more code than we started with, and it still requires copying and pasting.
Time for one more improvement. Create a new class, in \builders\, called RoundObjectDirector.as:
package builders
{
public class RoundObjectDirector
{
public function RoundObjectDirector()
{
}
}
}
Here’s how this will work:
- We’ll tell the RoundObjectDirector to use a specific RoundObjectBuilder.
- We’ll tell the RoundObjectDirector to create a new round object.
- The RoundObjectDirector will do so, using the RoundObjectBuilder that we specified, and will return said object.
Here’s the code:
package builders
{
import Box2D.Dynamics.b2Body;
public class RoundObjectDirector
{
protected var roundObjectBuilder:RoundObjectBuilder;
public function RoundObjectDirector()
{
}
public function setRoundObjectBuilder(builder:RoundObjectBuilder):void
{
this.roundObjectBuilder = builder;
}
public function createRoundObject(mStartX:Number, mStartY:Number, mVelocityX:Number, mVelocityY:Number):b2Body
{
roundObjectBuilder.createShape();
roundObjectBuilder.createFixtureDef();
roundObjectBuilder.createBodyDef(mStartX, mStartY);
roundObjectBuilder.createBody();
roundObjectBuilder.createFixture();
roundObjectBuilder.setStartingVelocity(mVelocityX, mVelocityY);
roundObjectBuilder.setImage();
roundObjectBuilder.linkImageAndBody();
return roundObjectBuilder.body;
}
}
}
See how most of the code in createRoundObject() is taken from our Main.createNewTennisBall() function? All you have to do is replace “tennisBall” with “roundObject” in each case. (Well, okay, I’ve also changed all measurements to be in meters rather than pixels, since we’re not dealing with any images here.)
Now we can modify Main.createNewTennisBall() like so:
private function createTennisBall(pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
var tennisBallBuilder:TennisBallBuilder = new TennisBallBuilder(world, pxToM);
var roundObjectDirector:RoundObjectDirector = new RoundObjectDirector();
roundObjectDirector.setRoundObjectBuilder(tennisBallBuilder);
var tennisBallBody:b2Body = roundObjectDirector.createRoundObject(pxStartX * pxToM, pxStartY * pxToM, mVelocityX, mVelocityY);
var tennisBallSprite:B2Sprite = tennisBallBody.GetUserData() as B2Sprite;
tennisBallSprite.x = pxStartX;
tennisBallSprite.y = pxStartY;
this.addChild(tennisBallSprite);
tennisBallSprite.addEventListener(MouseEvent.CLICK, onClickClickableImage);
}
(Note that I’ve converted the starting positions to pixels here, before passing them to the Director.)
Step 10: Creating Rubber Balls
Let’s create a brand new type of object, and see how much faster it is. We’ll use a super-bouncy rubber ball:

Mine is 15x15px and called RubberBall.png.
First step: create a new class, extending RoundObjectBuilder, to create the rubber ball:
package builders
{
import Box2D.Dynamics.b2World;
public class RubberBallBuilder extends RoundObjectBuilder
{
[Embed(source='../../lib/RubberBall.png')]
public var RubberBall:Class;
public function RubberBallBuilder(world:b2World, pxToM:Number)
{
super(world, pxToM);
restitution = 1.0;
friction = 0.0;
density = 5.0;
Image = RubberBall;
mRadius = 15 * 0.5 * pxToM;
}
}
}
Second step: create Main.createRubberBall():
private function createRubberBall(pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
var rubberBallBuilder:RubberBallBuilder = new RubberBallBuilder(world, pxToM);
var roundObjectDirector:RoundObjectDirector = new RoundObjectDirector();
roundObjectDirector.setRoundObjectBuilder(rubberBallBuilder);
var rubberBallBody:b2Body = roundObjectDirector.createRoundObject(pxStartX * pxToM, pxStartY * pxToM, mVelocityX, mVelocityY);
var rubberBallSprite:B2Sprite = rubberBallBody.GetUserData() as B2Sprite;
rubberBallSprite.x = pxStartX;
rubberBallSprite.y = pxStartY;
this.addChild(rubberBallSprite);
rubberBallSprite.addEventListener(MouseEvent.CLICK, onClickClickableImage);
}
Third step: create the actual objects:
for (var i:int = 0; i < 10; i++)
{
createTennisBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBowlingBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBasketball(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createRubberBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createCrate(
Math.random() * 1.5,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
)
}
That’s it. Easy.
Step 11: Getting Even More General
I can’t help myself. I have to make one more simplification.
Did you notice that Main.createRubberBall() and Main.createTennisBall() are almost identical? The only things that differ are the type of RoundObjectBuilder and the variable names.
Let’s combine them into a single function: Main.createRoundObject():
private function createRoundObject(roundObjectBuilder:RoundObjectBuilder, pxStartX:Number, pxStartY:Number, mVelocityX:Number, mVelocityY:Number):void
{
roundObjectDirector.setRoundObjectBuilder(roundObjectBuilder);
var body:b2Body = roundObjectDirector.createRoundObject(pxStartX * pxToM, pxStartY * pxToM, mVelocityX, mVelocityY);
var sprite:B2Sprite = body.GetUserData() as B2Sprite;
sprite.x = pxStartX;
sprite.y = pxStartY;
this.addChild(sprite);
sprite.addEventListener(MouseEvent.CLICK, onClickClickableImage);
}
I’ve also made roundObjectDirector a protected variable, belonging to Main, so that we don’t have to keep creating a new one:
protected var roundObjectDirector:RoundObjectDirector = new RoundObjectDirector();
Delete the createTennisBall() and createRubberBall() functions.
Now we can alter the code that creates objects, from this:
for (var i:int = 0; i < 10; i++)
{
createTennisBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBowlingBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBasketball(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createRubberBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createCrate(
Math.random() * 1.5,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
)
}
…to this:
var tennisBallBuilder:TennisBallBuilder = new TennisBallBuilder(world, pxToM);
var rubberBallBuilder:RubberBallBuilder = new RubberBallBuilder(world, pxToM);
for (var i:int = 0; i < 10; i++)
{
createRoundObject(
tennisBallBuilder,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBowlingBall(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createBasketball(
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createRoundObject(
rubberBallBuilder,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
);
createCrate(
Math.random() * 1.5,
Math.random() * (stage.stageWidth - 20) + 10,
Math.random() * (stage.stageHeight - 20) + 10,
(Math.random() * 100) - 50,
0
)
}
The SWF still works:
Excellent.
Conclusion
I’ll admit; that took us a long time, and didn’t give us a spectactuarly different SWF. I hope you agree that the cleaner code was worth it, though.
This new approach to creating object (called the Builder pattern) is going to make it very easy for us to create new types of object in the future, to add them to the world, and to make changes to all of them at once by modifying the base Builder.
If you’re feeling a little left behind by the pace of this tutorial, don’t worry; we ended up focusing on design patterns rather than Box2D in particular here, but in the next part of the series we’ll get back to Box2D’s built-in features. In particular, we’ll be working our way towards a physics-powered, keyboard-controlled, 2D platform game!
Until then, I suggest you do the following:
- Change your code for creating basketballs, bowling balls, and wheels so that they also use Builders.
- Create a new round object (medicine ball? ping-pong ball?), with a new image and set of properties, using Builders.
- If you’re up for a challenge, have a go at creating a brand new set of builder for rectangular objects, like the crate.
Good luck! If you’ve got any questions, please ask them below.

Hi Michael James Williams, thanks for this tutorial and before tutorial about box2d, This tutorial allow learn more.
I searched online but it is difficult to find tutorials that teach how to work with Box2D and there are little to the old version and Box2D.
thank you very much..
Great tutorial Michael!
Nice tut.
One thing to notice is that the demos don’t show up in IE9. For Firefox, Chrome and IE 8 – it’s all OK. It must be something with the object tag.
Grrr IE. Thanks, I’ll look into it.
Edit: Actually, it’s working for me in IE9, using FP11. Anyone else having problems?
Wow,great! I’m working for a flash web game with B2D. :)
Awesome tutorial and great work. Thank you for the detailed step by step instruction of how to create box2d structure. I have one question. The graphic of the balls on the example swfs looks very pixelated (especially during rotation). How to make graphic objects less pixelated?
Thanks, Jarofed!
The pixelation is due to me using PNGs that don’t rotate very well, I think. Ideally, you’d want to use either a vector Sprite (made in Flash Pro), or blit the image using a sprite sheet that has a selection of pre-drawn rotated images. My solution is not as good as either of those, I’m afraid; I picked it above vector Sprites because not every reader has Flash Pro, and I picked it above sprite sheets because I didn’t want to insist on everyone using blitting techniques.
Perhaps there’s another way to do this, that still uses embedded PNGs but removes the pixelation, but if there is I’m not aware of it. (If anyone is, please let me know!)
You can also turn on “smoothing” for the images. That would remove the pixelation but it would put a load on the processor.
Oh, good call Alex, thanks!
The ideal solution at the moment for pixelation would be to integrate the Starling API for FP 11 (http://www.starling-framework.org/). It supports sprite sheets and renders all of your graphics on the GPU, freeing up the CPU for bigger Box2D worlds. With that you can set anti-aliasing to smooth things out. I’m super busy at work but will be experimenting with this very soon!
Great tutorial by the way. I used the knowledge I gathered from the last ones to build an Angry Birds clone for a bar’s video system (called Angry Beers…see what I did there?) and this one certainly helps with organizing all my burgers, beer bottles and shot glasses :D
awesome tutorials. Very clear and very needed with something like Box2D.
Tankxxx a lot for the series !! :D
Thanks for another great tutorial.
Are there any examples of using box2d within a platformer? or another type game where the player controls the object that is reacting to the box2d physics? I assume that if I wanted to create a platformer game with box2d I would use the impulse function to control the object.
cheers
I am actually working on a Box2D platformer tutorial. I had planned to make it part of this series but unfortunately I’ve kind of written myself into a corner here ;/
Box2D isn’t really ideal for traditional platformers, since they tend not to use perfect Newtonian physics. I’ll show off some ways to get around this once I write the tut.
Also, does box2d fire an event or call back when an object collides with another?
“In particular, we’ll be working our way towards a physics-powered, keyboard-controlled, 2D platform game! ”
That would be awesome!!!! When do you think this tutorial will be available Mich? In March? April? I’m waiting for him !!! Thank you for all!
To be honest, I’m not sure. I have the basic structure all sketched out, but it feels like I’m manipulating Box2D to do something it was never intended to do. So I’m not sure it would make for a very helpful tutorial.
Oh man, that’s a bad news, really. It would be the only tutorial on the web about a platform game mixing Box2D and AS3 with all the construction. I search that (and I know I’m not alone) for two years now (it’s incredible it doesn’t exist on the web). I could even pay for a tuto like that.
For sure it would make a very helpful tutorial for all programmers or students like me. I hope you’ll do it, but I understand that you want create something else.
Oh really? Well… I’ll look into it some more :)
Thank you so much! I’ll be here everyday, waiting for this :)
Any news about it? :)
I am desperate. =( I hope you always think about it …
Jajaja……I’ll be here too waiting for big news……Michael you’re the best teacher ever…………I’m learning all from you……..first with “The avoider” and now with this series of tutorials……..
Greetings from Madrid !!!!
Great tutorial !!! A 2D platform game with Starling would be very interesting and useful. Do you think that you will make it someday? Thanks for all !!!!
Well, in the meantime, Michael, I am going to use all of this skills to try to make a platform game……..uf………what a difficult mission……….I am sure, then, I will get back here and I’be able to correct many things……jejeje, you are my personal teacher……….thank to you, yesterday I could correct my teacher in the university about a mistake he always does with arrays………..I learnt the solution from your avoider tutorial……………sincerely, you are the best……..
You coul come invited to my university here in Madrid to do a master class about as3 or Html5……jejeje
A big hug !!!!!
These are the best tutorial series I`ve ever read. I really enjoyed the way you write an the step-by-step was really really helpful, showing all the logic behind, not only the code.
Thanks
Thanks, Luckas!