Box2D platform engine alternative
- August 7, 2009 by Emanuele Feronato
- Filed under Actionscript 3, Box2D, Flash, Game design, Users contributions | 16 Comments
Everybody should know Box2D is the best engine you can use to make a platform game.
Rick Triqui, my latest game, was made with PlayCrafter that uses Box2D, and I’m starting to code my own Box2D platform engine.
Luis Fernando Silva, author of the upcoming Darkness game (Sponsors! Contact him to see the work in progress! Awesome idea!) wanted to share with us his Box2D platform engine, that made from scratch.
The source code is fully commented, for our pleasure
This is the main file:
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 | package { import flash.display.MovieClip; // Import the levels from the 'Levels' folder: import Levels.*; public class Main extends MovieClip { // The game level, re-instanciate this object to // switch the levels public var level:GameLevel; // Keyboard listener, got the script at Emanuele's websote public var keyboard:keys = new keys(stage); // Constructor, here these 3 simple lines of code create the level // and prepare it to be player public function Main() { level = new Level1(keyboard); // Create a new level, Level1, in this case addChild(level); // Add it to the display object addEventListener("enterFrame", Update, false, 0, true); // Add a loop } // Update the game here using GameLevel.Update so you can do // some general stuff on a main loop instead of using the GameLevel. public function Update(e:*) : void { level.Update(); } } } |
At line 5 the script imports levels (just one in this example) coded in this way:
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 | package Levels { public class Level1 extends GameLevel { public function Level1(k:keys) { //// Always remember to call the superior constructor first! super(k); //// The player: createPlayer(250, 50); //// The moveable blocks: addBox(110, 110, 50, 50); addBox(190, 200, 30, 50); addBox(100, 50, 120, 20); // Small see-saw: addBox(400, 250, 30, 30); addBox(400, 220, 200, 10); addBox(480, 200, 20, 20); //// The ground block: addStaticBox(550/2, 300, 550, 20); } } } |
At line 10 the script calls the most important class, GameLevel, that’s made this way:
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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | package { import flash.display.Sprite; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; // Main Game Level class definition public class GameLevel extends Sprite { // Player: public var playerBody:b2Body; public var player:Player; // Jump trigger: public var upDown:Boolean = false; // Misc: public var lastBody:b2Body; // Vars used to create bodies public var body:b2Body; public var bodyDef:b2BodyDef; public var boxDef:b2PolygonDef; public var circleDef:b2CircleDef; // Keyboard listener: public var keyboard:keys; public function GameLevel(k:keys) { keyboard = k; // World Setup: var worldAABB:b2AABB = new b2AABB(); worldAABB.lowerBound.Set(-100.0, -100.0); worldAABB.upperBound.Set(100.0, 100.0); // Define the gravity vector var gravity:b2Vec2 = new b2Vec2(0.0, 10.0); // Allow bodies to sleep var doSleep:Boolean = true; // Construct a world object m_world = new b2World(worldAABB, gravity, doSleep); player = new Player(); } public function Update() { // Wake up the player body, if it falls asleep, the player cannot // move anymore! playerBody.WakeUp(); // Jump: if(keyboard.is_up()){ // Cheesy way to check if the button was hit, instead of being held down: if(upDown == false){ var b1:b2Body = GetBodyAtPoint(player.x, player.y + player.height/2, true); var b2:b2Body = GetBodyAtPoint(player.x-7, player.y + player.height - 4, true); var b3:b2Body = GetBodyAtPoint(player.x+7, player.y + player.height - 4, true); if((b1 != playerBody && b2 != playerBody && b3 != playerBody) && b1 != null || b2 != null || b3 != null && playerBody.m_linearVelocity.y >= 0){ var DO = true; if(DO){ playerBody.ApplyImpulse(new b2Vec2(0, -10), playerBody.GetPosition());//playerBody.m_linearVelocity.y = -10; // If you want the underliying body to react according to Newton's // second law of motion (it's pushed down), uncoment the following laws: /*var bo = (b1 != null ? b1 : (b2 != null ? b2 : b3)); bo.ApplyImpulse(new b2Vec2(0, 10), playerBody.GetPosition());*/ } } upDown = true; } }else{ upDown = false; } // Side movements: if(keyboard.is_right()){ playerBody.m_linearVelocity.x = 3; } if(keyboard.is_left()){ playerBody.m_linearVelocity.x = -3; } // Sted the world: m_world.Step(m_timeStep, m_iterations); // Go through body list and update sprite positions/rotations for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next){ if (bb.m_userData is Sprite){ bb.m_userData.x = bb.GetPosition().x * 30; bb.m_userData.y = bb.GetPosition().y * 30; bb.m_userData.rotation = bb.GetAngle() * (180/Math.PI); } } } // Create the player using this function, _x and _y are the coordinates to place // the object: public function createPlayer(_x, _y) : b2Body { bodyDef = new b2BodyDef(); bodyDef.position.x = _x / 30; bodyDef.position.y = _y / 30; circleDef = new b2CircleDef(); circleDef.radius = 30 / 2 / 30; circleDef.density = 1.0; circleDef.friction = 1; circleDef.restitution = 0.1; var mass:b2MassData = new b2MassData; mass.mass = 1; bodyDef.userData = player; player.x = _x; player.y = _y; addChild(player); body = m_world.CreateDynamicBody(bodyDef); body.SetMass(mass); body.CreateShape(circleDef); body.m_linearDamping = 1; playerBody = body; return playerBody; } // Using this function, you can create dynamic (moveable) blocks. // The parameters are self-explanatory. // The userData parameter is optional and it replaces the boring // sprite used to represent the box. public function addBox(x, y, wid, heig, userData:* = null) : b2Body { bodyDef = new b2BodyDef(); bodyDef.position.x = x/30; bodyDef.position.y = y/30; boxDef = new b2PolygonDef(); boxDef.SetAsBox(wid/30/2, heig/30/2); boxDef.density = 1.0; boxDef.friction = 0.7; boxDef.restitution = 0.2; if(userData != null){ bodyDef.userData = userData; }else{ bodyDef.userData = new PhysBox(wid, heig); //bodyDef.userData.width = wid * 2; //bodyDef.userData.height = heig * 2; addChild(bodyDef.userData); } body = m_world.CreateDynamicBody(bodyDef); body.CreateShape(boxDef); body.SetMassFromShapes(); lastBody = body; return body; } // Using this function, you can create static (non-moveable) blocks. // The parameters are self-explanatory. // The userData parameter is same as in addBox function. public function addStaticBox(x, y, wid, heig, userData:* = null) : b2Body { var bodyDef = new b2BodyDef(); bodyDef.position.x = x/30; bodyDef.position.y = y/30; boxDef = new b2PolygonDef(); boxDef.SetAsBox(wid/30/2, heig/30/2); boxDef.density = 0.0; boxDef.friction = 0.5; boxDef.restitution = 0.2; if(userData != null){ bodyDef.userData = userData; }else{ bodyDef.userData = new PhysBox(wid, heig); //bodyDef.userData.width = wid * 2; //bodyDef.userData.height = heig * 2; addChild(bodyDef.userData); } body = m_world.CreateStaticBody(bodyDef); body.CreateShape(boxDef); body.SetMassFromShapes(); lastBody = body; return body; } // Really, dunno how this works, but it does. And that's what matters. public function GetBodyAtPoint(px:Number, py:Number, includeStatic:Boolean = false) : b2Body { // Make a small box. var px2 = px/30; var py2 = py/30; 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; // The loop that seeks for the body: 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; } // Physics world and definitions public var m_world:b2World; public var m_iterations:int = 20; public var m_timeStep:Number = 1.0/25.0; } } |
And this is the class handling the keyboard input
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 | package { import flash.events.KeyboardEvent; import flash.events.FocusEvent; public class keys { private var press_left = false; private var press_right = false; private var press_up = false; private var press_down = false; private var press_space = false; public var Keys:Array = new Array(200); public function keys(movieclip) { movieclip.stage.addEventListener(FocusEvent.FOCUS_OUT, focus, false, 0); movieclip.stage.addEventListener(KeyboardEvent.KEY_DOWN, key_down, false, 0); movieclip.stage.addEventListener(KeyboardEvent.KEY_UP, key_up, false, 0); } public function focus(e:FocusEvent){ for(var i:int = 0;i<Keys.length;i++){ Keys[i] = false; } press_left = false; press_right = false; press_up = false; press_down = false; press_space = false; } public function is_left() { return press_left; } public function is_right() { return press_right; } public function is_up() { return press_up; } public function is_down() { return press_down; } public function is_space() { return press_space; } public function isDown(key:int){ return Keys[key]; } private function key_down(event:KeyboardEvent) { Keys[event.keyCode] = true; if (event.keyCode == 32) { press_space = true; } if (event.keyCode == 37 || event.keyCode == 65) { press_left = true; } if (event.keyCode == 38 || event.keyCode == 87) { press_up = true; } if (event.keyCode == 39 || event.keyCode == 68) { press_right = true; } if (event.keyCode == 40 || event.keyCode == 83) { press_down = true; } } private function key_up(event:KeyboardEvent) { Keys[event.keyCode] = false; if (event.keyCode == 32) { press_space = false; } if (event.keyCode == 37 || event.keyCode == 65) { press_left = false; } if (event.keyCode == 38 || event.keyCode == 87) { press_up = false; } if (event.keyCode == 39 || event.keyCode == 68) { press_right = false; } if (event.keyCode == 40 || event.keyCode == 83) { press_down = false; } } } } |
And this is the result:
And this is the full source code to download
They can be easily customized to meet the unique requirements of your project.
16 Responses
Leave a Reply
- Una guida completa al gioco del poker online e una selezione dei migliori casino online.
- casino online
- migliori casino online
- BlackJack online
- casinò online
- Giochi casino

(20 votes, average: 4.75 out of 5)



Wow, very simple and efficient !
Very nice. The “jet-pack” feel while jumping isn’t there anymore.
Might be a bug :
Sometimes, when the seesaw is too close to the side of the frame and you are on it, you can’t jump.
Yeah, this is caused by the jumping code, which tests collisions under the player. This can be fized with some few extra lines to the code. I’ll add this later and send you guys the source code + multilevel example.
This is wonderful. Loving these BOX2D tutorials. :)
Thanks Luiz!
nice! :D
Trying to make a tile game of it :)
If it’s Box2D, then shouldn’t the player experience some resistance when trying to change direction (whether on the ground or in midair)?
Arxanas, I think there is no resistance because the velocity is changed directly by setting the linear x velocity to 3, rather than by using impulses(or forces). (just my speculation, though)
Anyway. Moving around has a really nice feeling about it, good job. However, player is …”sticky” while moving to the sides. You can stay long in the air by jumping and then pressing left or right near a box. It’s fun to use, but not what you would expect from, say, Mario.
Have you tried to use Flash IDE as a level editor? Should be useful for a platform game.
Very nice. Lot’s can be done with this.
@RedRail – I don’t think Mario had any physics other then gravity going on in the begining. Later they add some friction and stuff.
Perfect!Thanks.
Found the tute really handy, cheers dude.
Okay, so how do I skin those object inside Flash develop, it doesn’t look really nice with only black outline.
Can I load my image and use it instead of those black outlined…
[...] Fernando Silva, author of a Box2D platform engine alternative published on the blog some time ago, created a prototype of this [...]
I keep getting this error
“1061: Call to a possibly undefined method CreateDynamicBody through a reference with static type Box2D.Dynamics:b2World.”
any idea why or how to fix it?
I got that it is trying to find the b2World.as file in the Box2D>Dynamics folder, but why it is not finding it while it is clearly there is beyond me.
I figured out my problem…
If you are doing these great tuts in order don’t keep using the same Box2D source files. Emanuele wrote these for the version when they were made. Since then there have been updates which could as in my case change some names of methods. So be sure you are using the same version of Box2D as the tutorial.
Hi Emanuele,
great stuff in your blog.
Very usefull code, snippets and son on…
A question for you: could this box2D engine be used in conjuction with the Collision Detection Kit? Maybe only the physics part. Moreover. Does this box2D Engine works with non uniform shapes?
Thank you very much.
Saluti
Davide
Roma
[...] released a Box2D platform engine some months ago, and now I am publishing his last work, a Verlet physics [...]