Talking about Platform game game, Actionscript 3, Box2D, Flash and Game development.
In this third part of the platform engine, it’s time to make our hero jump.
First, I suggest you to read parts 1 and 2 if you already didn’t, then you should know something about the magic of compound objects and how Box2D manages collisions.
Then, you’re ready to follow the tutorial :)
The hero in order to jump must be on the ground, or over some solid object.
The main idea is creating the hero as a compound object made with the hero itself (the big rectangle) and a ground sensor (the small rectangle under the big one) that will be triggered every time the hero stands over something solid, without interacting with the world.
Then, we should check the sensor to collide with objects according to the response decide if the hero can or can’t jump.
Believe me, it’s easier than it seems. This is the main script:
package {
import flash.display.Sprite;
import flash.events.Event;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import General.Input;
import flash.events.MouseEvent;
public class HelloWorld extends Sprite {
public var m_world:b2World;
public var pixels_in_a_meter:int=30;
public var bodyDef:b2BodyDef;
public var boxDef:b2PolygonDef;
public var m_input:Input;
public var xspeed:int=0;
public var bazooka:zooka=new zooka();
public var bazooka_angle:Number;
public var m_contactListener=new b2ContactListener();
var body:b2Body;
public function HelloWorld() {
addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
var worldAABB:b2AABB = new b2AABB();
worldAABB.lowerBound.Set(-100.0, -100.0);
worldAABB.upperBound.Set(100.0, 100.0);
var gravity:b2Vec2=new b2Vec2(0.0,10.0);
var doSleep:Boolean=true;
m_world=new b2World(worldAABB,gravity,doSleep);
var m_sprite:Sprite;
m_sprite = new Sprite();
addChild(m_sprite);
var dbgDraw:b2DebugDraw = new b2DebugDraw();
var dbgSprite:Sprite = new Sprite();
m_sprite.addChild(dbgSprite);
dbgDraw.m_sprite=m_sprite;
dbgDraw.m_drawScale=30;
dbgDraw.m_alpha=1;
dbgDraw.m_fillAlpha=0.5;
dbgDraw.m_lineThickness=1;
dbgDraw.m_drawFlags=b2DebugDraw.e_shapeBit;
m_world.SetDebugDraw(dbgDraw);
var bodyDef:b2BodyDef;
var boxDef:b2PolygonDef;
var circleDef:b2CircleDef;
square_tile(250,395,500,10);
hero(100,370,20,40);
//
// adding the contact listener
m_world.SetContactListener(m_contactListener);
//
// Box2D input class - why should I bother making my one?
m_sprite = new Sprite();
addChild(m_sprite);
// input
m_input=new Input(m_sprite);
addChild(bazooka);
stage.addEventListener(MouseEvent.CLICK, shoot);
}
public function shoot(event:Event) {
bodyDef = new b2BodyDef();
bodyDef.position.Set((bazooka.x+(bazooka.width+3)*Math.cos(bazooka_angle))/pixels_in_a_meter, (bazooka.y+(bazooka.width+3)*Math.sin(bazooka_angle))/pixels_in_a_meter);
boxDef = new b2PolygonDef();
boxDef.SetAsBox(10/pixels_in_a_meter,10/pixels_in_a_meter);
boxDef.friction=0.3;
boxDef.density=1;
body=m_world.CreateBody(bodyDef);
body.CreateShape(boxDef);
body.SetMassFromShapes();
body.ApplyImpulse(new b2Vec2(Math.cos(bazooka_angle)*4, Math.sin(bazooka_angle)*4),body.GetWorldCenter());
}
public function square_tile(px:int,py:int,w:int,h:int) {
bodyDef = new b2BodyDef();
bodyDef.position.Set(px/pixels_in_a_meter, py/pixels_in_a_meter);
boxDef = new b2PolygonDef();
boxDef.SetAsBox(real_pixels(w), real_pixels(h));
boxDef.friction=0.3;
boxDef.density=0;
body=m_world.CreateBody(bodyDef);
body.CreateShape(boxDef);
body.SetMassFromShapes();
}
public function hero(px:int, py:int, w:int, h:int) {
bodyDef = new b2BodyDef();
bodyDef.position.Set(px/pixels_in_a_meter, py/pixels_in_a_meter);
boxDef = new b2PolygonDef();
boxDef.SetAsBox(real_pixels(w), real_pixels(h));
boxDef.density=1.0;
boxDef.friction=0.3;
boxDef.restitution=0.2;
bodyDef.userData = new Sprite();
bodyDef.userData.name="Player";
body=m_world.CreateBody(bodyDef);
body.SetBullet(true);
body.CreateShape(boxDef);
//
var ground_sensor:b2PolygonDef = new b2PolygonDef();
ground_sensor.isSensor=true;
ground_sensor.userData="groundsensor";
ground_sensor.SetAsOrientedBox(10/pixels_in_a_meter,5/pixels_in_a_meter,new b2Vec2(0,27/pixels_in_a_meter), 0);
body.CreateShape(ground_sensor);
//
body.SetMassFromShapes();
}
public function real_pixels(n:int) {
return (n/pixels_in_a_meter/2);
}
public function Update(e:Event):void {
m_world.Step(1/30, 10);
xspeed=0;
for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next) {
if (bb.GetUserData()!=null) {
if (Input.isKeyDown(39)) { // right arrow
xspeed=3;
} else if (Input.isKeyDown(37)) { // left arrow
xspeed=-3;
}
if (Input.isKeyDown(38)) {// up arrow
if (m_contactListener.can_jump()) { // checking if the hero can jump
bb.ApplyImpulse(new b2Vec2(0.0, -1.0), bb.GetWorldCenter());
}
}
if (xspeed) {
bb.WakeUp();
bb.m_linearVelocity.x=xspeed;
}
bb.m_sweep.a=0;
bazooka.x=bb.m_userData.x=bb.GetPosition().x*30;
bazooka.y=bb.m_userData.y=bb.GetPosition().y*30;
var dist_x=bazooka.x-mouseX;
var dist_y=bazooka.y-mouseY;
bazooka_angle=Math.atan2(- dist_y,- dist_x);
bazooka.rotation=bazooka_angle*57.2957795;
}
}
Input.update();
}
}
}
and I changed b2ContactListener.as
as follows:
/*
* Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
package Box2D.Dynamics{
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Dynamics.Contacts.*;
import Box2D.Dynamics.*;
import Box2D.Common.Math.*;
import Box2D.Common.*;
/// Implement this class to get collision results. You can use these results for
/// things like sounds and game logic. You can also get contact results by
/// traversing the contact lists after the time step. However, you might miss
/// some contacts because continuous physics leads to sub-stepping.
/// Additionally you may receive multiple callbacks for the same contact in a
/// single time step.
/// You should strive to make your callbacks efficient because there may be
/// many callbacks per time step.
/// @warning The contact separation is the last computed value.
/// @warning You cannot create/destroy Box2D entities inside these callbacks.
public class b2ContactListener {
var canjump:Boolean=false;
/// Called when a contact point is added. This includes the geometry
/// and the forces.
public function can_jump() {
return (canjump);
}
public virtual function Add(point:b2ContactPoint):void {}
/// Called when a contact point persists. This includes the geometry
/// and the forces.
public virtual function Persist(point:b2ContactPoint):void {
if (point.shape1.GetUserData()=="groundsensor"||point.shape2.GetUserData()=="groundsensor") {
canjump=true;
}
}
/// Called when a contact point is removed. This includes the last
/// computed geometry and forces.
public virtual function Remove(point:b2ContactPoint):void {
if (point.shape1.GetUserData()=="groundsensor"||point.shape2.GetUserData()=="groundsensor") {
canjump=false;
}
}
/// Called after a contact point is solved.
public virtual function Result(point:b2ContactResult):void {
}
}
}
Playing a bit with impulses and bullets size, I made my hero shooting something like crates… and he can jump over them to reach higher places… needs some tweaks but it’s an interesting gameplay in my opinion…
Left/right arrow keys to move, up to jump and mouse to aim and shoot.
Download the source code, libraries included.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.