Box2D platform engine alternative

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:

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:

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:

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 

And this is the class handling the keyboard input

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

And this is the result:

And this is the full source code to download

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

215 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stairs
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers