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