How to Create Physics Bodies Using PhysicsEditor for Starling and Box2D

So, you’re getting annoyed finding vertex cordinartes for your super complicated physics body and you want someone find that for you? You came to the correct neighborhood!

This tutorial will show how to use PhysicsEditor (a really cool physics body editor!) to create box2d bodies. I presume you have basic knowledge on making bodies manually. If not, I recommend you to try making some. It worth the number of lines you code. You can download this cool Editor here.

Creating PhysicsData

First of all, after opening PhysicsEditor, this screen will appear.

All you have to do is to click “add sprites” icon. I highly recommend you to resize your images first to correct dimension before working on it.

I selected two images from my computer, napoleon.png and floor.png.

Let’s start working on napoleon, and then finish the floor with exactly the same way. Click the “shape tracer” icon .

You want to make the number of vertexes as small as possible while still maintaining the shape. To do so, play around with tolerance value. The higher the value the lesser the vertexes. But don’t worry, you can add or remove vertex later in editing phase. Alpha tresshold indicates how much you want to ignore transparent pixels. If you’re done click OK.

You can drag a point to fit the shape of your object. Double click point to delete it, or double click in empty edge to add point. Keep in mind that the less number of vertex, the happier your CPU.

Once you’re done fitting your vertexes, you can change the physical properties of the body in the fixture parameter panel. Set the density, restitution, and friction as you want and then proceed to the next image with the same step. When everything is ready, we can now export the physics data. You must first change the exporter to Box2D ActionScript.

Publish to your project’s classpath folder.

Coding for Your Happiness

I’m assuming that you have setup your Starling correctly. Let’s first put aour assets in an accessible folder. I put them inside sprites folder in the project’s bin directory. By the way, I’m using FlashDevelop.

So, let’s start coding!

In your Game class, (or whatever you call the starling sprite class), set the Box2D world ready.

// Import all neccesary libs
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2World;
import starling.display.DisplayObject;
import starling.display.Image;
import starling.display.Sprite;
import starling.events.EnterFrameEvent;
import starling.textures.Texture;

/**
* ...
* @author Imran
*/

public class Game extends Sprite
{
// Embed sources, in this case I embed napoleon and floor image
[Embed(source = "../bin/sprites/napoleon.png")]
public static const Napoleon:Class;

[Embed(source = "../bin/sprites/floor.png")]
public static const Floor:Class;

private var world:b2World;
private var data:PhysicsData;

public function Game()
{
  super();

  // Create new box2d physics world and its gravity.
  world = new b2World(new b2Vec2(0, 9.8), true);

  // Create Starling image from embedded resources
  var napoleonImage:Image = new Image(Texture.fromBitmap(new Napoleon()));
  var floorImage:Image = new Image(Texture.fromBitmap(new Floor()));
}
}

Now Lets update the physics world for each timestep. Change your constructor function like this:

public function Game()
{
  super();

  // Create new box2d physics world and its gravity.
  world = new b2World(new b2Vec2(0, 9.8), true);

  // Create Starling image from embedded resources
  var napoleonImage:Image = new Image(Texture.fromBitmap(new Napoleon()));
  var floorImage:Image = new Image(Texture.fromBitmap(new Floor()));

  // Add enterframe listener to make the world update at each enterframe event.
  addEventListener(EnterFrameEvent.ENTER_FRAME, updateWorld);
}

And now create the event handler function.

private function updateWorld(e:EnterFrameEvent):void
{
  // Make the world update itself for each timestep, say 1/30 of a second
  world.Step(1 / 30, 10, 10);
  world.ClearForces();
}

And here comes the magick (intentional typo for FF fan reader)!! Update your constructor like this:

public function Game()
{
  super();

  world = new b2World(new b2Vec2(0, 9.8), true);

  var napoleonImage:Image = new Image(Texture.fromBitmap(new Napoleon()));
  var floorImage:Image = new Image(Texture.fromBitmap(new Floor()));

  // create an instance of physics data
  data = new PhysicsData();

  // Create napoleon body and attach napoleonImage as it's visual representation
  var napoleonBody:b2Body = data.createBody("napoleon", world, b2Body.b2_dynamicBody, napoleonImage);
  // Set the position and angle of our napoleon.
  napoleonBody.SetPositionAndAngle(new b2Vec2(100 / data.ptm_ratio, 50 / data.ptm_ratio), 0);
  // Add napoleon userData, napoleonImage, to display list
  addChild(napoleonBody.GetUserData());

  // Do the same for our floor guy
  var floorBody:b2Body = data.createBody("floor", world, b2Body.b2_staticBody, floorImage);
  floorBody.SetPositionAndAngle(new b2Vec2(0 / data.ptm_ratio, 550 / data.ptm_ratio), 0);
  addChild(napoleonBody.GetUserData());

  addEventListener(EnterFrameEvent.ENTER_FRAME, updateWorld);
}

The createBody method of PhysicsData class is the key here. It automatically creates body for you without bothering about shapes, fixtures, and all defs. It has been done for you. Wow!

createBody accepts four parameters:

  • name:String,
  • world:b2World,
  • bodyType:uint,
  • userData:*

The name parameters is a string that must be the same as your picture name. I used napoleon.png, so the name is “napoleon”. For the floor.png, the name is “floor”. If you use different name, you’ll get an error.

The world and bodyType parameter speaks for themselves.
As for userData, you must pass your starling image (or other starlingdisplay object) here.

For final touch, update your updateWorld handler like this:

private function updateWorld(e:EnterFrameEvent):void
{
  world.Step(1 / 30, 10, 10);
  world.ClearForces();

  // Code for making starlingImage follows your box2d body.
  for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
    // I Include it in a try catch function because for reason I don't really know, a static body automatically created at (0,0). Let's call him mr B -_-
    try
    {
      var sprite:DisplayObject = b.GetUserData() as DisplayObject;
      sprite.x = b.GetPosition().x * data.ptm_ratio;
      sprite.y = b.GetPosition().y * data.ptm_ratio;
      sprite.rotation = b.GetAngle();
    } catch (err:Error)
    {
      // I kill mr B here
      world.DestroyBody(b);
    }
  } // end of for
} // end of function

Test the movie and here’s what I got:

That’s all! You can optimize the code to fit your need. Good Luck ^_^


Imran
A compassionate AS3 developer for simulation and games.
I love comments, leave one ^_^


Advertisements
This entry was posted in Simulation and Games. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s