Talking about Platform game game, Actionscript 3, Box2D, Flash, Game development and Users contributions.
I wrote about Box2D Flash version about a year ago, then I published Playing with Box2DFlashAS3 and Create a Flash game like Totem Destroyer as examples covering what you can do with this library.
But I was really impressed by a thread on TriquiTips submitted by Hawdon called Box2DAs3 For Beginners! (read steps 1 and 2) that in my opinion deserves to be posted on this blog, to reach a wider public… because I know there is a lot of people looking for AS3 and Box2D and platform tutorials… and the prototype made by Hawdon merges them all this way:
Left-right to move and up to jump
This is the source code with some comments
package {
// Imports some things into the movie.
import flash.display.Sprite;
import flash.events.*;
import flash.events.KeyboardEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
public class Box2d extends Sprite {
public function Box2d() {
addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, KeyUp);
var worldAABB:b2AABB = new b2AABB();//Creates a new world
worldAABB.lowerBound.Set(-100.0, -100.0);//Sets the lower bloundaries of the world (If a shape goes outside this it will freez)
worldAABB.upperBound.Set(100.0, 100.0);//Sets the upper bloundaries of the world (If a shape goes outside this it will freez)
var gravity:b2Vec2 = new b2Vec2(0.0, 10.0);//Sets gravity. First number is Y and second is x
var doSleep:Boolean = true;//If the shapes are allowed to sleep. (Stop simulating)
m_world = new b2World(worldAABB, gravity, doSleep);//Sets everything together to make a Box2d World
CreateBox(275, 375, 600, 50, true, new Ground());//Ground
CreateCircle(250, 100, 50, false, new Circle());//Ball
CreateHero(200, 300);//Player
CreateBox(500, 302, 200, 100, true, new Ground());//Static Box on right side
/* following code makes some stacks of blocks*/
for (var x:int = 1; x<4; x++) {
for (var i:int = 1; i<11; i++) {
CreateBox(((300+(20*x))-(.25*i)), (365-(20*i)), 20, 20, false, new Box());
}
}
}
/*
I personaly think its MUCh easier to put the code into functions,
because then you dont need to type it again, and again, and again. And it works just fine with function like createCircle and createBox.
And when its time to start making levels, you will have a lot less typing to do when you can just type in the function.
*/
/*
Following Function Creates the Hero
CreateHero(X Posision, Y Posision, Width of Hero, Height of Hero)
*/
public function CreateHero(x:Number, y:Number, width:Number = 20, height:Number = 20) {
bodyDef = new b2BodyDef();
bodyDef.position.x = (x)/physScale;
bodyDef.position.y = (y)/physScale;
boxDef = new b2PolygonDef();
boxDef.SetAsBox(GetRealWH(20), GetRealWH(20));
boxDef.density = 1.0;//Because the density is at higher then 0 it moves.
boxDef.friction = 0.3;
boxDef.restitution = 0.2;
bodyDef.userData = new PlayerBox();
bodyDef.userData.name = "Player";
bodyDef.userData.width = physScale * 2 * GetRealWH(width);
bodyDef.userData.height = physScale * 2 * GetRealWH(height);
body = m_world.CreateBody(bodyDef);
body.SetBullet(true);//Tells flash to treat it like a bullet (Continuous collision detection.)
body.CreateShape(boxDef);
body.SetMassFromShapes();
bodyDef.userData.body = body;//This is the only way I know that u can get a specific body from the World.
//If you have a better idea, tell me!
addChild(bodyDef.userData);
}
/*
Following function Creates a Circle
CreateCircle(X posision, Y Posision, Width/Height of ball, Is the ball static (Inmoveable), The movieclip you want to be shown as the circle (Example: new Circle())
*/
public function CreateCircle(x:Number, y:Number, width:Number, static:Boolean, Cover) {
/* Following Code creates a circle */
bodyDef = new b2BodyDef();
bodyDef.position.x = x/physScale;
bodyDef.position.y = y/physScale;
circleDef = new b2CircleDef();//Defines circleDef as a new b2CircleDef
circleDef.radius = GetRealWH(width);//Sets the radius of the ball
circleDef.density = 1.0;
circleDef.friction = 0.3;
circleDef.restitution = 0.2;
bodyDef.userData = Cover;//sets userData to a new Circle (See Library)
bodyDef.userData.width = 30 * 2 * GetRealWH(width);//Sets width and height. Third Number is the same as the radius.
bodyDef.userData.height = 30 * 2 * GetRealWH(width);
trace(bodyDef.userData.height);
body = m_world.CreateBody(bodyDef);
body.CreateShape(circleDef);
body.SetMassFromShapes();
addChild(bodyDef.userData);
}
/*
Following function creates a box
CreateBox(X posision, Y Posision, Width, Height, Is the box static (Inmoveable), The movieclip you want to be shown as the box (Example: new Box())
*/
public function CreateBox(x:Number, y:Number, width:Number, height:Number, static:Boolean, Cover) {
/* Creates the static box on the right side of the screen*/
bodyDef = new b2BodyDef();
bodyDef.position.x = (x)/physScale;
bodyDef.position.y = (y)/physScale;
boxDef = new b2PolygonDef();
boxDef.SetAsBox(GetRealWH(width), GetRealWH(height));
if (static) {
boxDef.density = 0.0;
}
else {
boxDef.density = 1.0;
}
boxDef.friction = 0.3;
boxDef.restitution = 0.4;
bodyDef.userData = Cover;
bodyDef.userData.width = physScale * 2 * GetRealWH(width);
bodyDef.userData.height = physScale * 2 * GetRealWH(height);
bodyDef.isBullet = true;//This tells flash to treat all boxes like bullets. This is CPU consuming with a lot of boxes.
//But the hit test is WAY better when you use this.
body = m_world.CreateBody(bodyDef);
body.CreateShape(boxDef);
body.SetMassFromShapes();
addChild(bodyDef.userData);
}
/*
I hade to make this function because I noticed that
the width and height of the shapes were 2 times to large
even to I divided it by physScale.
This is because Box2d when doing width and height only takes from the center out.
For Example: if I use this SetAsBox(100/physScale, 100/physScale)
I will get a box that is 200x200 because the 100/physScale is the distance from the
center of the box to the top or the sides.
I made this function because I like using the numbers I usualy use in flash
when making games.
*/
public function GetRealWH(Num:Number) {
return Num / physScale / 2;
}
/*
Following functions a controls when any aroow key is pressed.
*/
public var Left:Boolean = false;
public var Right:Boolean = false;
public var Up:Boolean = false;
public function KeyDown(e:KeyboardEvent) {
trace(e.keyCode);
var TheChild = getChildByName("Player");
var Body = TheChild.body;
switch (e.keyCode) {
case 37 ://Left
Left = true;
break;
case 38 ://Up
Up = true;
break;
case 39 ://Right
Right = true;
break;
}
}
public function KeyUp(e:KeyboardEvent) {
trace(e.keyCode);
var TheChild = getChildByName("Player");
var Body = TheChild.body;
switch (e.keyCode) {
case 37 ://Left
Left = false;
break;
case 38 ://Up
Up = false;
break;
case 39 ://Right
Right = false;
break;
}
}
/* Following function gets the body from a cordinate (x, y)*/
public function GetBodyAtPoint(px:Number, py:Number, includeStatic:Boolean = false) {
// Make a small box.
var px2 = px/physScale;
var py2 = py/physScale;
var PointVec:b2Vec2 = new b2Vec2();
PointVec.Set(px2, py2);
var aabb:b2AABB = new b2AABB();
aabb.lowerBound.Set(px2 - 0.001, py2 - 0.001);
aabb.upperBound.Set(px2 + 0.001, py2 + 0.001);
// Query the world for overlapping shapes.
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].m_body.IsStatic() == false || includeStatic) {
var tShape:b2Shape = shapes[i] as b2Shape;
var inside:Boolean = tShape.TestPoint(tShape.m_body.GetXForm(), PointVec);
if (inside) {
body = tShape.m_body;
break;
}
}
}
return body;
}
public function Update(e:Event):void {
/*Following code down to line 252 Makes the Player move left, right and jump*/
var TheChild = getChildByName("Player");//Getts a child named Box
var Body = TheChild.body;//Get the var body from the child.
if (Left) {//If Left Arrow is down
Body.WakeUp();//Wakes the body up if its sleeping
Body.m_linearVelocity.x = -3;//Adds to the linearVelocity of the box.
}
if (Right) {//If right arrow is down
Body.WakeUp();//Wakes the body up if its sleeping
Body.m_linearVelocity.x = 3;//Adds to the linearVelocity of the box.
}
if (Up) {
if (Body.GetLinearVelocity().y > -1) {//Stops player from sometimes jumpping higher then suppose to
/*
I have made 3 hit points.
*/
var Hit = GetBodyAtPoint(TheChild.x, TheChild.y+(TheChild.height/2+2), true);//Under
var Hit1 = GetBodyAtPoint(TheChild.x-(TheChild.width/2)+2, TheChild.y+(TheChild.height/2+2), true);//Down-Left
var Hit2 = GetBodyAtPoint(TheChild.x+(TheChild.width/2)-2, TheChild.y+(TheChild.height/2+2), true);//Down-Right
if (Hit != null && Hit.m_userData != TheChild) {
Body.ApplyImpulse(new b2Vec2(0.0, -3.0), Body.GetWorldCenter());//Applys and impuls to the player. (Makes it jump)
}
else if (Hit1 != null && Hit1.m_userData != TheChild || Hit2 != null && Hit2.m_userData != TheChild) {
Body.ApplyImpulse(new b2Vec2(0.0, -3.0), Body.GetWorldCenter());
}//Applys and impuls to the player. (Makes it jump)
}
}
};
Body.m_sweep.a = 0;//This is what stops the player from rotating
/*
But because its only called once a frame it will allow the player to rotate a little.
Which makes it more realistic.
*/
m_world.Step(m_timeStep, m_iterations);//This runns the Box2D World
//Body.m_sweep.a = 0; //If you wanned to take the tiny movement away you could move this code here.
// The follwoing code rotates, sets the x and y cordinates of the movieclips the player sees.
for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next) {//Makes a for loop scanning all the bodys in the World
if (bb.m_userData is Sprite) {//Checks if bb.m_userData is a spirit (Movieclip)
bb.m_userData.x = bb.GetPosition().x * physScale;//Changes the x
bb.m_userData.y = bb.GetPosition().y * physScale;//Changes the y
bb.m_userData.rotation = bb.GetAngle() * (180/Math.PI);//Changes the rotation
}
}
}
/* Vars used in creating shapes */
public var body:b2Body;
public var bodyDef:b2BodyDef;
public var boxDef:b2PolygonDef;
public var circleDef:b2CircleDef;
public var m_world:b2World;
public var m_iterations:int = 15;
public var physScale:Number = 30;
public var m_timeStep:Number = 1.0/physScale;
}
}
I have to say I am quite impressed about this work and I am porting my platform engine using Box2D, and release some Box2D tutorials if this post gets a good feedback.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.