Monday, July 16, 2012

Getting started with Box2D

Was playing with idea of using physics in my game, and found this amazing open source physics gaming engine called Box2D. They have ports of the game engine for javascript and AS3. The only problem being there isn't much documentation out there for js.
I will be using the Box2D-Web port of Box 2D since this version is kept up to date.

here is the code,

    var world;
    var   b2Vec2 = Box2D.Common.Math.b2Vec2
             ,    b2BodyDef = Box2D.Dynamics.b2BodyDef
             ,    b2Body = Box2D.Dynamics.b2Body
             ,    b2FixtureDef = Box2D.Dynamics.b2FixtureDef
             ,    b2Fixture = Box2D.Dynamics.b2Fixture
             ,    b2World = Box2D.Dynamics.b2World
             ,    b2MassData = Box2D.Collision.Shapes.b2MassData
             ,    b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
             ,    b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
             ,    b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,   b2Vec2 = Box2D.Common.Math.b2Vec2
            ;
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");
    var mouseX, mouseY, isMouseDown; 
    function init() {
        world = new b2World(
               new b2Vec2(0, 10)    //gravity
            ,  true                 //allow sleep
        );
        setupWorld() ;
        //setup debug draw
        var debugDraw = new b2DebugDraw();
            debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
            debugDraw.SetDrawScale(1.0);
            debugDraw.SetFillAlpha(0.3);
            debugDraw.SetLineThickness(1.0);
            debugDraw.SetFlags(b2DebugDraw.e_shapeBit  |  b2DebugDraw.e_jointBit);       
        world.SetDebugDraw(debugDraw);
        window.setInterval(update, 1000 / 60);
      };
      function setupWorld() {
            setupGround();
           addBouncingBall();
      }
      function setupGround() {
           var fixDef = new b2FixtureDef;
         fixDef.density = 1.0;
         fixDef.friction = 0.5;
         fixDef.restitution = 0.2;
         var bodyDef = new b2BodyDef;
         //create ground
         bodyDef.type = b2Body.b2_staticBody;
         bodyDef.position.x = 300;
         bodyDef.position.y = 400;
         fixDef.shape = new b2PolygonShape;
         fixDef.shape.SetAsBox(290, 10);
         world.CreateBody(bodyDef).CreateFixture(fixDef);
      }
      function addBouncingBall() {     
         var fixDef = new b2FixtureDef;
         fixDef.density = 1.0;
         fixDef.friction = 1.0;
         fixDef.restitution = 0.1;
         var bBallbodyDef = new b2BodyDef;
         bBallbodyDef.type = b2Body.b2_dynamicBody;
         fixDef.shape = new b2CircleShape(Math.random() + 30);
         bBallbodyDef.position.x = Math.random() * 300;
         bBallbodyDef.position.y = Math.random() * 300;   
         obj = new Object();
         obj.val = "Ball";
         bBallbodyDef.userData = obj;
         bBallBody = world.CreateBody(bBallbodyDef).CreateFixture(fixDef);
      };
      document.addEventListener("mousedown", handleMouseMove, true);
      function handleMouseMove(e) {
          isMouseDown = true;
          mouseX = e.clientX;
          mouseY = e.clientY;
      };
      function redraw_world(){
          if(isMouseDown)
          {
              for (b = world.GetBodyList() ; b; b = b.GetNext()) {
                  if( b.GetUserData() != null)
                  {
                      if(b.GetUserData().val == "Ball")
                      {
                            var force = new b2Vec2(4000,4000);
                            var point = new b2Vec2(100,100);
                            b.ApplyForce(force, point);      
                      }
                  }           
              }
          isMouseDown = false;
           }
      };
      function update() {
          redraw_world();
           world.Step(1 / 2, 5, 5);
           world.DrawDebugData();
           world.ClearForces();
      };

Most of the code above is from online tutorials and I am sure you have come across it already if you are interested in Box2D, check the links in the Reference if you haven't. Well the bummers in this code was figuring out which body should be moved. Online there are a zillion questions posted on usage of GetUserData()/SetUserData(), even i posted one, but no definite answer for js

Initially i was doing it as
function addBouncingBall(){
     ....
     bBallbodyDef.userData = obj;
     bBallBody = world.CreateBody(bBallbodyDef).CreateFixture(fixDef);  // note: bBallBody is declared global
     bBallBody.SetUserData(obj);
    ....

So
redraw_world()
{
      ...  
      console.log(bBallBody.GetsUserData().val);  // Was on the money, and it printed "Ball" to the console
      for (b = world.GetBodyList() ; b; b = b.GetNext()) {
          ...
          console.log(b.GetsUserData());  //returned null everytime.
      ...
 }

Finally after much Googling and ogling, found out that the userdata can be assigned to the bodydef as follows, and that solved most  of the problems,
bBallbodyDef.userData = obj;

There is lack of documentation for javascript port of Box2D, but you can refer the documentation for Box2dFlash. They are almost the same.


Resources:
Box2DWeb

1 comment: