Wednesday, April 3, 2013

Box2D & Javascript: Part 3

In part 2: I covered a basic setup for box2d based simulations and in this long promised final installment  of the tutorial series, I will cover the integration with KineticJS library. The most important aspect of this part is how to use a framework like KineticJS and use their features for image rendering and manipulation in physics based simulations without having to write your own physics engine for it.



Press 'd' to toggle debugDraw mode
Box2d Demo - by Aniruddha Loya

Now let's break up the code and see how it works

First I define the two functions `loadImages` & `init` to initialize the image sources and load them before we start manipulating them. To read more about loadingImages, check this post.

Next, lets initialize KineticJS related variables - stage and layer as below. Parameter passed as "container" is the name of the div that will be used to create the canvas object by KineticJS.

stage = new Kinetic.Stage({container:"canvas", width:screenW, height:screenH});
gameLayer = new Kinetic.Layer();


Then we'll define a KineticJS Image type object to create a background image using one of the images we earlier loaded. Then we add this object to our gameLayer and finally add the gameLayer to our stage.

var gameBg = new Kinetic.Image(
{
 image:images.gameBg,
 x:0, y:0,
 width:screenW, height:screenH
});
gameLayer.add(gameBg); // Add the gameBg object to our gameLayer
stage.add(gameLayer); // And add our gameLayer to our stage

After this we'll do our initialization for Box2D world and ground just like the previous post. However, the objects inside this world will be initialized differently. The code below, adds 5 wall objects to our game canvas as well as box2d world.
     
for(var i = 0; i < 5; ++i) {
 // lets create a new Image of a wall
 var w = new Kinetic.Image({
  image: images.w,
  x:Math.random() * screenW,
  y:Math.random() * screenH/4,
  width: images.w.width/4,
  height: images.w.height/4
 });
 gameLayer.add(w);

 // by default images are positioned from top-left, and box2d objects from center, so lets offset our images to be addressed from center rather than top-left
 w.setOffset(w.getWidth()/2, w.getHeight()/2);

 // now lets create the corresponding box2d object.
 // Read my previous post for box2d object details: http://aniruddhaloya.blogspot.com/2012/11/box2d-javascript-part-2.html
 fixDef.shape = new b2PolygonShape;

 var off = w.getOffset();
 fixDef.shape.SetAsBox(off.x/scale, off.y/scale); // Remember that we need to scale the canvas to box2d coordinates

 var pos = w.getAbsolutePosition();

 bodyDef.position.Set((pos.x+off.x)/scale, (pos.y+off.y)/scale); // Remember that we need to scale the canvas to box2d coordinates

 var body = world.CreateBody(bodyDef);
 body.CreateFixture(fixDef);

 // lets push the corresponding objects in their respective containers.
 walls.push(body);
 tiles.push(w);
}

Lets analyze what we are doing in the above piece of code. We first create KineticJS image objects and place it randomly in the screen and add it to the gameLayer. Next, we use setOffest() function of KineticJS to move our image anchor position to its center rather than the default top-left. This is required for proper rotation and correspondance with the corresponding objects in box2d world which we create after that. The creation of corresponding box2d world objects is similar to that in previous tutorial, except that one needs to take care of the unit system (i.e. scaling factor) between the two environments. Finally, we add the two objects in corresponding containers for ease of access and mapping during the update step (below).

// Traverse through all the box2d objects and update the positions and rotations of corresponding KineticJS objects
for(var i = 0; i < walls.length; i++)
{
 var body = walls[i];
 var wall = tiles[i];
 var p = body.GetPosition();
 wall.setRotation(body.GetAngle());
 wall.setPosition(p.x*scale, p.y*scale);
}

Finally, we need to map and update both the objects. Here, box2d takes care of all the interactions which we simply map to the corresponding KineticJS image objects. Note carefully the use of "scale" parameter which unlike previous code snippet is now multiplied rather than divide.
       
And like last time, press 'd' to see the simulation in debug-Draw mode offered by box2d and be assured that the images are actually moving like the physics body they are attached to.

Wow... I finally manage to take time and complete this series. I hope my fellow game developers who are starting/ planning to develop next Angry Birds found it useful. I'll love to hear your suggestions!

Last but not the least, entire source code for this tutorial can be downloaded from Git
Post a Comment