Math and (Quasi) Physics in Action Script 3

Box 2D - A snake with DistanceJoints

Testing how to use DistanceJoint.

Main.as

´╗┐package {
  import Box2D.Collision.b2AABB;
  import Box2D.Collision.Shapes.*;
  import Box2D.Common.Math.b2Vec2;
  import Box2D.Dynamics.b2Body;
  import Box2D.Dynamics.b2BodyDef;
  import Box2D.Dynamics.b2DebugDraw;
  import Box2D.Dynamics.Joints.*;
  import Box2D.Dynamics.b2World;
  import General.Input;

  import flash.display.Sprite;
  import flash.events.Event;

  public class Main extends Sprite {
    private const DRAW_SCALE:uint = 100;
    private var world:b2World;
    private var fps:int;
    private var wallDef:Array = [[2.5, -1, 2.5, 1],
      [6, 2.5, 1, 2.5],
    [2.5, 6, 2.5, 1],
    [-1, 2.5, 1, 2.5]
    ];
    private var sbdDef:Array = [[1, 0.5, 0.1, 0.05],
      [1.25, 0.5, 0.1, 0.05],
      [1.5, 0.5, 0.1, 0.05],
      [1.75, 0.5, 0.1, 0.05],
      [2, 0.5, 0.1, 0.05],
      [2.25, 0.5, 0.1, 0.05],
      [2.5, 0.5, 0.1, 0.05],
      [2.75, 0.5, 0.1, 0.05],
      [3, 0.5, 0.1, 0.05],
      [3.25, 0.5, 0.1, 0.05],
      [3.5, 0.5, 0.1, 0.05],
      [3.75, 0.5, 0.1, 0.05],
      [4, 0.5, 0.1, 0.05]];
    private var stageSp:Sprite;
    private var sbds:Array = [];
    
    public function Main():void {
      fps = stage.frameRate;
      addEventListener(Event.ENTER_FRAME, h_enterFrame);
      addChild(stageSp = new Sprite());
      init();
    }

    private function init():void {
      input = new Input(this);

      // create the world
      var worldAABB:b2AABB = new b2AABB();
      worldAABB.lowerBound.Set(-100, -100);
      worldAABB.upperBound.Set(100, 100);
      var gravity:b2Vec2 = new b2Vec2(0,0);
      world = new b2World(worldAABB,gravity,true);

      // * entities *
      var bodyDef:b2BodyDef;
      var shapeDef:b2PolygonDef;
      var body:b2Body;
      var sp:Sprite;
      var i:uint;

      // create walls
      for (i = 0; i < wallDef.length; i ++) {
        bodyDef = new b2BodyDef();
        bodyDef.position.Set(wallDef[i][0],wallDef[i][1]);
        shapeDef = new b2PolygonDef();
        shapeDef.SetAsBox(wallDef[i][2],wallDef[i][3]);
        body = world.CreateBody(bodyDef);
        body.CreateShape(shapeDef);
      }

      // create body
      for (i = 0; i < sbdDef.length; i ++) {
        bodyDef = new b2BodyDef();
        bodyDef.position.Set(sbdDef[i][0],sbdDef[i][1]);
        shapeDef = new b2PolygonDef();
        shapeDef.SetAsBox(sbdDef[i][2],sbdDef[i][3]);
        shapeDef.density = 1;
        shapeDef.restitution = 0.05;
        body = world.CreateBody(bodyDef);
        body.CreateShape(shapeDef);
        body.SetMassFromShapes();
        sbds.push(body);
      }
      // initial velocity
      sbds[0].SetLinearVelocity(new b2Vec2(0,10));

      // create joints
      var djDef:b2DistanceJointDef;
      for (i = 1; i < sbds.length; i ++) {
        djDef = new b2DistanceJointDef();
        djDef.Initialize(sbds[i-1], sbds[i], sbds[i-1].GetPosition(), sbds[i].GetPosition());
        djDef.collideConnected = true;
        world.CreateJoint(djDef);
      }
      
      // debug draw
      var debugDraw:b2DebugDraw = new b2DebugDraw();
      debugDraw.m_sprite = stageSp;
      debugDraw.m_drawScale = 100;
      debugDraw.m_fillAlpha = 0.3;
      debugDraw.m_lineThickness = 1;
      debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;;
      world.SetDebugDraw(debugDraw);
    }

    private function h_enterFrame(event:Event):void {
      if (world == null) {
        return;
      }

      // step
      world.Step(1 / fps, 10);
      UpdateMouseWorld();
      MouseDrag();
      Input.update();

    }
    
    
    // mouse controle
    public var mouseJoint:b2MouseJoint;
    private var mouseXWorldPhys:Number;
    private var mouseYWorldPhys:Number;
    private var mouseXWorld:Number;
    private var mouseYWorld:Number;
    private var input:Input;
    private var mousePVec:b2Vec2 = new b2Vec2();
    
    public function UpdateMouseWorld():void{
      mouseXWorldPhys = (Input.mouseX)/DRAW_SCALE; 
      mouseYWorldPhys = (Input.mouseY)/DRAW_SCALE;       
      mouseXWorld = (Input.mouseX); 
      mouseYWorld = (Input.mouseY); 
    }
    
    // drag
    public function MouseDrag():void{
      // mouse press
      if (Input.mouseDown && !mouseJoint){
        
        var body:b2Body = GetBodyAtMouse();
        
        if (body)
        {
          var md:b2MouseJointDef = new b2MouseJointDef();
          md.body1 = world.GetGroundBody();
          md.body2 = body;
          md.target.Set(mouseXWorldPhys, mouseYWorldPhys);
          md.maxForce = 300.0 * body.GetMass();
          md.timeStep = 1/fps;
          mouseJoint = world.CreateJoint(md) as b2MouseJoint;
          body.WakeUp();
        }
      }
      
      // mouse release
      if (!Input.mouseDown){
        if (mouseJoint)
        {
          world.DestroyJoint(mouseJoint);
          mouseJoint = null;
        }
      }
      
      // mouse move
      if (mouseJoint)
      {
        var p2:b2Vec2 = new b2Vec2(mouseXWorldPhys, mouseYWorldPhys);
        mouseJoint.SetTarget(p2);
      }
    }
    
    public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
      // Make a small box.
      mousePVec.Set(mouseXWorldPhys, mouseYWorldPhys);
      var aabb:b2AABB = new b2AABB();
      aabb.lowerBound.Set(mouseXWorldPhys - 0.001, mouseYWorldPhys - 0.001);
      aabb.upperBound.Set(mouseXWorldPhys + 0.001, mouseYWorldPhys + 0.001);
      
      // Query the world for overlapping shapes.
      var k_maxCount:int = 10;
      var shapes:Array = new Array();
      var count:int = world.Query(aabb, shapes, k_maxCount);
      var body:b2Body = null;
      for (var i:int = 0; i < count; ++i)
      {
        if (shapes[i].GetBody().IsStatic() == false || includeStatic)
        {
          var tShape:b2Shape = shapes[i] as b2Shape;
          var inside:Boolean = tShape.TestPoint(tShape.GetBody().GetXForm(), mousePVec);
          if (inside)
          {
            body = tShape.GetBody();
            break;
          }
        }
      }
      return body;
    }

  }
}