Wednesday, September 5, 2012

Box2dWeb Events in Three.js

‹prev | My Chain | next›

Yesterday, I realized that my initial difficulties with Three.js and Box2dWeb integration were caused by my failure to account for rotation. Specifically, objects in Box2dWeb were rotating / falling down after a collision, but I was only representing the position change in Three.js.

The solution was to copy the rotation from the Box2dWeb world into the Three.js scene:
function gameStep() {
  world.Step(
    1 / 60,   //frame-rate
    10,       //velocity iterations
    10       //position iterations
  );
  world.ClearForces();

  var pdef = player_fixture.GetBody().GetDefinition();
  player.position.x = pdef.position.x;
  player.position.y = pdef.position.y;
  player.rotation.z = pdef.angle;

  var fdef = fruit_fixture.GetBody().GetDefinition();
  fruit.position.x = fdef.position.x;
  fruit.position.y = fdef.position.y;

  setTimeout(gameStep, 1000 / 60); // process the game logic
                                   // at a target 60 FPS.
}
That works, but I do not really want my player to rotate or fall down in this jumping game. Happily, Box2dWeb provides a fixedRotation property to accomplish this:
  // ...
  // player
  bodyDef.type = b2Body.b2_dynamicBody;
  fixDef.shape = new b2PolygonShape;
  fixDef.shape.SetAsBox(10, 25);
  bodyDef.position.x = -75;
  bodyDef.position.y = 10;
  bodyDef.fixedRotation = true;
  player_fixture = world.CreateBody(bodyDef).CreateFixture(fixDef);
  // ...
With that, I can collide without rotating:


Next, I would like to detect the collision. This turns out to be fairly straight forward in Box2dWeb. Straight-forward, but a little different than typical JavaScript events.

Normally, I would add an event listener to the player or the game tokens. But in Box2dWeb, I need to create a b2ContactListener object and, on that object, I need to set a BeginContact property. Lastly, I have to add the listener to the Box2dWeb world object as a contact listener:
  // ...
  var myListener = new Box2D.Dynamics.b2ContactListener;

  myListener.BeginContact = function(contact) {
    if (contact.m_fixtureA === ground) return;
    if (contact.m_fixtureB === ground) return;
    console.log(contact);
    score += 10;
    writeScoreBoard("Score: " + score);
  };

  world.SetContactListener(myListener);
  // ...
In any collision, there are two objects. In Box2dWeb, these are properties of a contact object supplied to the listener's callback. The properties are named m_fixtureA and m_fixtureB. In my callback, I need guard clauses to ignore collisions with the ground. If it is a real collision, I update the game score.

And it works:


Nice. This listener object, call back property, set-on-world process feels a bit awkward, but it works. And for something compiled from ActionScript, I'll take it.

Day #500

No comments:

Post a Comment