Box2DFlashAS3 v2.1a HelloWorld

Box2D demo

Box2D demo

Click the image above for the demo.
Click here or right click on the demo to view the source code.

Inspired by of Box2D running on Android, I decided to dive into this 2D physics engine I have heard so much about.  While the version of Box2D I used is the AS3 version called Box2DFlashAS3, the original version is written in C++.  Basically it very simply lets you apply 2D physics to your objects, or “bodies,” in AS3.

Most of the examples and tutorials I saw were lacking 2 things:

  1. A way to apply sprites to my “bodies” without the use of a flash project (FLA) file.
  2. Code that was compatible with the latest version of Box2DFlashAS3, v2.1a at the time of this post.

So to resolve that situation, or to account for my search engine deficiency, I present the HelloWorld example from the Box2DFlashAS3 2.1a distribution modified to be pure AS3:

package{
package {
  /*
   * Tony Lukasavage - SavageLook.com - 8/18/2010
   * Box2DFlashAS3 2.1a HelloWorld example, minus the need for an accompanying FLA
   *
   * This the basic Box2DFlashAS3 HelloWorld.as file from the source distribution
   * with some adjustments made so that you do not need an FLA file to compile and
   * run the code.  A simple bonus for us pure AS3 guys.  Also a few minor modifications
   * are made to account for changes between version 2.0 and 2.1, like adding a type
   * for body definitions.  Finally, I threw in an click handler to toggle between
   * normal and debug drawing.
   *
   */
 
  import Box2D.Collision.*;
  import Box2D.Collision.Shapes.*;
  import Box2D.Common.Math.*;
  import Box2D.Dynamics.*;
 
  import __AS3__.vec.Vector;
 
  import com.adobe.viewsource.ViewSource;
 
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.geom.Matrix;
  import flash.text.TextField;
  import flash.text.TextFormat;
 
  [SWF(width="800", height="600", frameRate="30")]
  public class box2d extends Sprite
  {
    private var _world:b2World;
    private var _velocityIterations:int = 10;
    private var _positionIterations:int = 10;
    private var _timeStep:Number = 1.0 / 30.0;
    private var _showDebug:Boolean = true;
    private var _debugSprite:Sprite;
    private var _bodySprites:Vector. = new Vector.();
 
    // Box2D uses meters for measurement, AS3 uses pixels.  1 meter = 30 pixels
    public var _worldRatio:int = 30;
 
    public function box2d()
    {
      // Add event for main loop
      addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
      stage.addEventListener(MouseEvent.CLICK, onClick );
 
      // add background gradient
      var bg:Sprite = new Sprite();
      var matrix:Matrix = new Matrix();
      matrix.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI/2, 0, 0);
      bg.graphics.beginGradientFill("linear", [0x9999ff, 0xffffff], [1, 1], [0, 255], matrix);
      bg.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
      bg.graphics.endFill();
      addChild(bg);
 
      // 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
      _world = new b2World(gravity, doSleep);
 
      // set debug draw
      var debugDraw:b2DebugDraw = new b2DebugDraw();
      _debugSprite = new Sprite();
      addChild(_debugSprite);
      debugDraw.SetSprite(_debugSprite);
      debugDraw.SetDrawScale(_worldRatio);
      debugDraw.SetFillAlpha(0.5);
      debugDraw.SetLineThickness(2);
      debugDraw.SetAlpha(1);
      debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
      _world.SetDebugDraw(debugDraw);
 
      // Vars used to create bodies
      var body:b2Body;
      var bodyDef:b2BodyDef;
      var boxShape:b2PolygonShape;
      var circleShape:b2CircleShape;
 
      // Adding sprite variable for dynamically creating body userData
      var sprite:Sprite;
      var groundHeight:int = 60;
 
      sprite = new Sprite();
      sprite.graphics.lineStyle(1);
      sprite.graphics.beginFill(0x444444);
      sprite.graphics.drawRect(-stage.stageWidth/2, -groundHeight/2, stage.stageWidth, groundHeight);
      sprite.graphics.endFill();
 
      bodyDef = new b2BodyDef();
      bodyDef.type = b2Body.b2_staticBody;
      bodyDef.position.Set(stage.stageWidth / _worldRatio / 2, (stage.stageHeight - sprite.height/2) / _worldRatio);
      bodyDef.userData = sprite;
      addChild(bodyDef.userData);
 
      boxShape = new b2PolygonShape();
      boxShape.SetAsBox(sprite.width/_worldRatio/2, sprite.height/_worldRatio/2);
 
      var fixtureDef:b2FixtureDef = new b2FixtureDef();
      fixtureDef.shape = boxShape;
      fixtureDef.friction = 0.3;
      fixtureDef.density = 0; // static bodies require zero density
 
      body = _world.CreateBody(bodyDef);
      body.CreateFixture(fixtureDef);
 
      // Add some objects
      for (var i:int = 1; i < 20; i++) {
        // create generic body definition
        bodyDef = new b2BodyDef();
        bodyDef.type = b2Body.b2_dynamicBody;
        bodyDef.position.x = Math.random() * 15 + 5;
        bodyDef.position.y = Math.random() * 10;
        var rX:Number = Math.random() + 0.5;
        var rY:Number = Math.random() + 1;
        var spriteX:Number = rX * 30 * 2;
        var spriteY:Number = rY * 30 * 2;
 
        // Box
        if (Math.random() < 0.5) {
          sprite = new Sprite();
          sprite.graphics.lineStyle(1);
          sprite.graphics.beginFill(0xff6666);
          sprite.graphics.drawRect(-spriteX/2, -spriteY/2, spriteX, spriteY);
          sprite.graphics.endFill();
          bodyDef.userData = sprite;
 
          boxShape = new b2PolygonShape();
          boxShape.SetAsBox(rX, rY);
 
          fixtureDef.shape = boxShape;
          fixtureDef.density = 1.0;
          fixtureDef.friction = 0.5;
          fixtureDef.restitution = 0.2;
 
          body = _world.CreateBody(bodyDef);
          body.CreateFixture(fixtureDef);
        }
        // Circle
        else {
          sprite = new Sprite();
          sprite.graphics.lineStyle(1);
          sprite.graphics.beginFill(0x44ff44);
          sprite.graphics.drawCircle(0, 0, spriteX/2);
          sprite.graphics.endFill();
          bodyDef.userData = sprite;
 
          circleShape = new b2CircleShape(rX);
 
          fixtureDef.shape = circleShape;
          fixtureDef.density = 1.0;
          fixtureDef.friction = 0.5;
          fixtureDef.restitution = 0.2;
 
          body = _world.CreateBody(bodyDef);
          body.CreateFixture(fixtureDef);
        }
 
        _bodySprites.push(bodyDef.userData as Sprite);
        addChild(bodyDef.userData);
      }
 
      // enable view source
      ViewSource.addMenuItem(this, "srcview/index.html");
      var text:TextField = new TextField();
      text.text = "Right click to view source";
      text.setTextFormat(new TextFormat("arial", 14, 0, true));
      text.x = 20;
      text.y = 20;
      text.width = 200;
      addChild(text);
    }
 
    public function onClick(e:MouseEvent):void {
      _showDebug = !_showDebug;
      if (!_showDebug) {
        _debugSprite.graphics.clear();
      }
      for each (var sprite:Sprite in _bodySprites) {
        sprite.visible = !_showDebug;
      }
    }
 
    public function Update(e:Event):void{
      _world.Step(_timeStep, _velocityIterations, _positionIterations);
      if (_showDebug) {
        _world.DrawDebugData();
      }
 
      // Go through body list and update sprite positions/rotations
      for (var bb:b2Body = _world.GetBodyList(); bb; bb = bb.GetNext()){
        if (bb.GetUserData() is Sprite){
          var sprite:Sprite = bb.GetUserData() as Sprite;
          sprite.x = bb.GetPosition().x * 30;
          sprite.y = bb.GetPosition().y * 30;
          sprite.rotation = bb.GetAngle() * (180/Math.PI);
        }
      }
    }
  }
}

Very cool stuff that adds lots of possibilities to your Flash projects.  I can’t wait to start playing with the more complex aspects like joints, buoyancy and breakable bodies.  More intensely awesome demos sure to follow.

4 Responses to “Box2DFlashAS3 v2.1a HelloWorld”

  1. MKEason says:

    Yes! the 2 lacking thing is annoying!
    Have no idea why the official not provide such samples.

    Thanks for your great article.

  2. Glad it helped you out! I’m guessing the official copy doesn’t have current examples because they are busy increasing performance and functionality and are hoping that past examples are good enough, along with the 2.1a FAQ they posted.

  3. Neko says:

    Thank you so much! From one pure AS3 guy to another, I salute you! Finally an example that I can get working with the latest Box2D and my trusty text editor.

  4. Thanks, I had been reading Feronato’s blog and tried to apply sprites, but to no avail. This is quite useful, thanks for commenting your prototype on his blog!