Sunday, January 15, 2012

Simple Physics with DartBox2D

‹prev | My Chain | next›

Last night, I got started with DartBox2D, the Dart version of Box2D. I was able to run the demos and take a stab at drawing some things on the page, but without really understanding what I was doing. Tonight, I am going to follow along with the "Hello World!" in the Box2D Manual and see if I can get it working in Dart.

From the manual, the first thing to do is create the world:
b2Vec2 gravity(0.0f, -10.0f);
bool doSleep = true;
b2World world(gravity, doSleep);
The stuff that I already copied from the DartBox2D demo into my Simple2D constructor covers just this:
  Simple2d() {
    // Setup the World.
    final gravity = new Vector(0, GRAVITY);
    bool doSleep = true;
    world = new World(gravity, doSleep, new DefaultWorldPool());
  }
The only significant difference between the two is the third argument to the world constructor, DefaultWorldPool. From the DartBox2D documentation the DefaultWorldPool:
Provides object pooling for some objects used in the engine. Objects retrieved from here should only be used temporarily.
That does not provide much illumination for me, but, looking that the World code, it definitely requires a third argument.

Next up in the tutorial is to create a "ground box". As far as I can tell this is not a first order concept in Box2D. Rather, it is a box that is meant to serve as the ground. I am probably just being dense, but I am happy to be a newbie.

From the tutorial, the ground box looks like:
// Create the body & add it to the world
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f, -10.0f);
b2Body* groundBody = world.CreateBody(&groundBodyDef);

// Fixture with width 100 and height 10
b2PolygonShape groundBox;
groundBox.SetAsBox(50.0f, 10.0f)

// Bind shape to body
groundBody->CreateFixture(&groundBox, 0.0f);
The equivalent DartBox2D takes place (adapted from the demo code) in the initialize() method:
void initialize() {
    Body ground;
    {
      BodyDef bd = new BodyDef();
      bd.position.setCoords(0.0, 0.0);
      assert(world != null);
      ground = world.createBody(bd);

      PolygonShape sd = new PolygonShape();
      sd.setAsBox(50.0, 0.4);

      ground.createFixtureFromShape(sd);
    }
  }
Next up in the tutorial is adding a box. Just as with the static ground object, the tutorial creates a dynamic box by defining a body, shape and "fixture" (which connects the two):
// Body definition
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(0.0f, 4.0f);
b2Body* body = world.CreateBody(&bodyDef);

// Define the shape of the body
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(1.0f, 1.0f)

// Bind shape to the body (e.g. Fixture)
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3
body->CreateFixture(&fixtureDef);
I add the equivalent Dart code to the initialize() block:
  void initialize() {
    Body ground;
    { /* ... */ }


    Body body;
    {
      BodyDef bodyDef = new BodyDef();
      bodyDef.type = BodyType.DYNAMIC;
      bodyDef.position.setCoords(20.0, 20.0);
      body = world.createBody(bodyDef);

      PolygonShape dynamicBox = new PolygonShape();
      dynamicBox.setAsBox(1.0, 1.0);

      FixtureDef fixtureDef = new FixtureDef();
      fixtureDef.shape = dynamicBox;
      fixtureDef.density = 1.0;
      fixtureDef.friction = 0.3;

      body.createFixture(fixtureDef);
    }
  }
The complete set of objects needed to do something in Box2D is somewhat overwhelming: body (for position), shape (for, er.. shape), fixture definition (shape plus real-world attributes), and fixture (the fixture definition realized in the world). Still, I much prefer it in Dart to C++ (though I may be biased).

The remainder of the tutorial specifies steps for printing out the position of the square as it falls. That is rather dull and, thankfully, the Box2D folks have provided methods for GUI representations of DartBox2D things via an initializeAnimation() method:
  /**
   * Creates the canvas and readies the demo for animation. Must be called
   * before calling runAnimation.
   */
  void initializeAnimation() {
    // Setup the canvas.
    canvas = document.createElement('canvas');
    canvas.width = CANVAS_WIDTH;
    canvas.height = CANVAS_HEIGHT;
    document.body.appendChild(canvas);
    ctx = canvas.getContext("2d");

    // Create the viewport transform with the center at extents.
    final extents = new Vector(CANVAS_WIDTH / 2, CANVAS_HEIGHT / 2);
    viewport = new CanvasViewportTransform(extents, extents);
    viewport.scale = _VIEWPORT_SCALE;

    // Create our canvas drawing tool to give to the world.
    debugDraw = new CanvasDraw(viewport, ctx);

    // Have the world draw itself for debugging purposes.
    world.debugDraw = debugDraw;

    frameCount = 0;
    window.setInterval(() { fps = frameCount; frameCount = 0; }, 1000);
  }

And a runAnimation() method:
  /**
   * Starts running the demo as an animation using an animation scheduler.
   */
  void runAnimation() {
    window.webkitRequestAnimationFrame((num time) {
      step(time);
    }, canvas);
  }

  void step(num timestamp) {
    world.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS);

    // Clear the animation panel and draw new frame.
    ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
    world.drawDebugData();
    ctx.setFillColor('black');

    window.webkitRequestAnimationFrame((num time) {
      step(time);
    }, canvas);
  }
I find the box a little odd, so I switch it to a ball:
    Body body;
    {
      // ...

      CircleShape circle = new CircleShape();
      circle.radius = 2.0;

      FixtureDef fixtureDef = new FixtureDef();
      fixtureDef.shape = circle;
      fixtureDef.density = 0.8;
      fixtureDef.friction = 0.0;
      fixtureDef.restitution = 0.5;

      body.createFixture(fixtureDef);
    }
I also give the body an initial velocity:
    Body body;
    {
      BodyDef bodyDef = new BodyDef();
      bodyDef.type = BodyType.DYNAMIC;
      bodyDef.position.setCoords(-25.0, 30.0);
      body = world.createBody(bodyDef);

      body.linearVelocity = new Vector(20.0, 0.0);
      //...
    }
With some bounding box shapes, I have me a nice animation (actually starts ~6 second mark):


That's a fun little library. Wish I knew about this sooner.


Day #266

No comments:

Post a Comment