Hi!
I'm new to box2d engine. Thus to learn it more I'm trying to create a platform game and I've encountered following problem:
after making a dynamic body moving with ApplyImpulse it moves for some distance, and then suddenly stops and doesn't react anymore to this metod.
That's the main class, that initiates all objects, gets keyboard states and has a mian loop
Code:
public class Main extends Sprite
{
private const TILE_WIDTH:int = 32;
private const TILE_HEIGHT:int = 32;
private const PLAYER_JUMP_POWER:int = 40;
private const PLAYER_SPEED:int = 3;
private var physicsEngine:PhysicsEngine;
private var keys:Keys;
private var fpsText:TextField;
private var fpsCounter:FpsCounter;
private var controlledObjectId:int;
public function Main():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
initKeyboard();
initPhysics();
initStaticTiles();
initHero();
initFpsCounter();
initLoop();
}
private function initKeyboard():void {
keys = new Keys();
keys.addKeysListeners(this.stage);
}
private function initPhysics():void {
physicsEngine = new PhysicsEngine(new Point(0, 9.8));
physicsEngine.setDebugContainer(this);
physicsEngine.setDebugMode(true);
}
private function initStaticTiles():void {
for (var i:int = 0; i < Math.floor(this.stage.stageWidth / TILE_WIDTH); i++)
physicsEngine.addObject(new Point(i * TILE_WIDTH, stage.stageHeight - TILE_HEIGHT), TILE_WIDTH, TILE_HEIGHT, false, 1, 7, 0);
}
private function initHero():void {
controlledObjectId = physicsEngine.addObject(new Point(0, stage.stageHeight - 3 * TILE_HEIGHT), TILE_WIDTH, TILE_HEIGHT, true, 1, 8, 0);
}
private function initFpsCounter():void {
fpsCounter = new FpsCounter();
//nothing useful
}
private function initLoop():void {
this.addEventListener(Event.ENTER_FRAME, update);
}
private function update(event:Event):void {
physicsEngine.update();
updateFrameRate();
updateKeyboard();
}
private function updateFrameRate():void {
//nothing useful
}
private function updateKeyboard():void {
var speedVector:Point = new Point(0, 0);
var keyWasPressed:Boolean = false;
if (keys.isLeft()) {
speedVector.x += -PLAYER_SPEED;
keyWasPressed = true;
}
if (keys.isRight()) {
speedVector.x += PLAYER_SPEED;
keyWasPressed = true;
}
if (keys.isUp()) {
speedVector.y += -PLAYER_JUMP_POWER;
keyWasPressed = true;
}
if (keys.isDown()) {
speedVector.y += PLAYER_JUMP_POWER;
keyWasPressed = true;
}
if (keyWasPressed)
physicsEngine.applyImpulse(speedVector, controlledObjectId);
}
}
The second class in PhysicsEngine, which implements all box2d logic:creates world, and manages all the objects
Code:
public class PhysicsEngine {
private const METERS_TO_PIXELS:int = 32;
private const PIXELS_TO_METERS:Number = 1 / METERS_TO_PIXELS;
private var world:b2World;
private var gravity:b2Vec2;
private var timeStep:Number = 0.03;
private var debugMode:Boolean = false;
private var gravityVector:Point;
private var bodies:Vector.<PhysicsObject>;
private var dynamicBodies:Vector.<PhysicsObject>;
public function PhysicsEngine(gravityVector:Point) {
this.gravityVector = gravityVector;
gravity = new b2Vec2(gravityVector.x, gravityVector.y);
world = new b2World(this.gravity, true);
world.SetContactListener(new CollisionListener());
bodies = new Vector.<PhysicsObject>();
dynamicBodies = new Vector.<PhysicsObject>();
}
public function setTimeStep(step:Number):void {
timeStep = step;
}
public function setDebugMode(debug:Boolean):void {
debugMode = debug;
}
public function setDebugContainer(debugContainer:Sprite):void {
var debugDraw:b2DebugDraw = new b2DebugDraw();
debugDraw.SetSprite(debugContainer);
debugDraw.SetDrawScale(METERS_TO_PIXELS);
debugDraw.SetAlpha(.3);
debugDraw.SetLineThickness(1);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
world.SetDebugDraw(debugDraw);
}
public function setGravity(gravityVector:Point):void {
this.gravity.SetV(new b2Vec2(gravityVector.x, gravityVector.y));
world.SetGravity(gravity);
}
public function update():void {
world.Step(timeStep, 10, 10);
if (debugMode)
world.DrawDebugData();
}
public function addObject(position:Point, width:Number, height:Number, isDynamic:Boolean = false, friction:Number = 0, density:Number = 0, restitution:Number = 0):int {
var object:PhysicsObject = new PhysicsObject(new Point(position.x * PIXELS_TO_METERS, position.y * PIXELS_TO_METERS), width * PIXELS_TO_METERS, height * PIXELS_TO_METERS);
object.setDensity(density);
object.setFriction(friction);
object.setRestitution(restitution);
object.setDynamic(isDynamic);
object.addToWorld(world);
if (isDynamic) dynamicBodies.push(object);
else bodies.push(object);
return object.getId();
}
public function removeObject(objectId:int):void {
for (var i:int = bodies.length; i >= 0; i-- )
if (bodies[i].getId() == objectId) {
removeFromWorld(i);
if (bodies[i].isDynamic())
removeDynamicObject(objectId);
removeStaticObject(i);
break;
}
}
private function removeFromWorld(index:int):void {
world.DestroyBody(bodies[index].getBody());
}
private function removeStaticObject(index:int):void {
bodies.splice(index, 1);
}
private function removeDynamicObject(id:int):void {
for (var i:int = dynamicBodies.length; i >= 0; i-- )
if (id == dynamicBodies[i].getId()) {
dynamicBodies.splice(i, 1);
break;
}
}
public function getDynamicObjetsNumber():int {
return dynamicBodies.length;
}
public function getDynamicObjectPosition(index:int):Point {
var pos:Point = new Point(dynamicBodies[index].getPosition().x * METERS_TO_PIXELS, dynamicBodies[index].getPosition().y * METERS_TO_PIXELS);
return pos;
}
public function applyImpulse(force:Point, objectId:int):void {
for (var i:int = 0; i < dynamicBodies.length; i++ )
if (dynamicBodies[i].getId() == objectId) {
dynamicBodies[i].applyImpulse(force);
break;
}
}
}
The last one is PhysicsObject that represents a body and all its properties.
Code:
public class PhysicsObject {
private static var bodyCount:int = 0;
private var id:int;
private var position:Point;
private var width:Number = 0;
private var height:Number = 0;
private var dynamic:Boolean = false;
private var density:Number = 0;
private var friction:Number = 0;
private var restitution:Number = 0;
private var maxVelocity:int = 5;
private var body:b2Body;
public function PhysicsObject(position:Point, width:Number, height:Number) {
this.id = bodyCount++;
this.position = position;
this.width = width;
this.height = height;
}
public function addToWorld(world:b2World):void {
var def:b2BodyDef = new b2BodyDef();
if (dynamic) def.type = b2Body.b2_dynamicBody;
def.position.Set(position.x + width / 2, position.y + height / 2);
body = world.CreateBody(def);
body.SetFixedRotation(true);
var shape:b2PolygonShape = new b2PolygonShape();
shape.SetAsBox(width / 2, height / 2);
var mainFixtureDef:b2FixtureDef = new b2FixtureDef();
mainFixtureDef.shape = shape;
mainFixtureDef.density = density;
mainFixtureDef.friction = friction;
mainFixtureDef.restitution = restitution;
var mainFixture:b2Fixture = body.CreateFixture(mainFixtureDef);
}
public function getId():int {
return id;
}
public function getPosition():Point {
position.x = body.GetPosition().x - width/2;
position.y = body.GetPosition().y - height/2;
return position;
}
public function getWidth():Number {
return width;
}
public function getHeight():Number {
return height;
}
public function setDynamic(isDynamic:Boolean):void {
dynamic = isDynamic;
}
public function isDynamic():Boolean {
return dynamic;
}
public function setDensity(density:Number):void {
this.density = density;
}
public function getDensity():Number {
return this.density;
}
public function setFriction(friction:Number):void {
this.friction = friction;
}
public function getFriction():Number {
return friction;
}
public function setRestitution(restitution:Number):void {
this.restitution = restitution;
}
public function getRestitution():Number {
return restitution;
}
public function getBody():b2Body {
return body;
}
public function isMovingRight():Boolean {
return (body.GetLinearVelocity().x > 0) ? true : false;
}
public function isMovingLeft():Boolean {
return (body.GetLinearVelocity().x < 0) ? true : false;
}
public function isMovingUp():Boolean {
return (body.GetLinearVelocity().y < 0) ? true : false;
}
public function isMovingDown():Boolean {
return (body.GetLinearVelocity().y > 0) ? true : false;
}
public function applyImpulse(forceVector:Point):void {
trace("linear speed x:" + body.GetLinearVelocity().x + " y:" + body.GetLinearVelocity().y);
trace("impulse vector x:"+forceVector.x+" y:"+forceVector.y);
var canMove:Boolean = canMakeAMove(forceVector.x);
trace("can make a move " + canMove);
if (canMove)
applyHorizontalImpulse(forceVector.x);
if (canMakeAJump())
applyVerticalImpulse(forceVector.y);
}
private function canMakeAMove(impulse:Number):Boolean {
//if wants to move right
if (impulse > 0)
return canMoveRight();
//move left
else if (impulse < 0)
return canMoveLeft();
else return true;
}
private function canMoveRight():Boolean {
var velocity:Number = body.GetLinearVelocity().x;
//and currently also is moving right
if (isMovingRight()) {
//check if max speed is reached
if (Math.abs(velocity) < maxVelocity)
return true;
else return false;
}
else
return true;
}
private function canMoveLeft():Boolean {
var velocity:Number = body.GetLinearVelocity().x;
if (isMovingLeft()) {
if (Math.abs(velocity) < maxVelocity)
return true;
else return false;
}
else
return true;
}
private function canMakeAJump():Boolean {
return (body.GetLinearVelocity().y == 0) ? true : false;
}
private function applyHorizontalImpulse(impulse:Number):void {
body.ApplyImpulse(new b2Vec2(impulse, 0), body.GetWorldCenter());
}
private function applyVerticalImpulse(impulse:Number):void {
body.ApplyImpulse(new b2Vec2(0, impulse), body.GetWorldCenter());
}
}