Tuesday, September 4, 2012

Box2dWeb Collisions, Three.js, and Debugging

‹prev | My Chain | next›

Tonight I continue my exploration of hooking Box2dWeb into Three.js. So far, I have Box2dWeb physics affecting movement in my Three.js visualization and am able to move Box2dWeb objects with key presses. Tonight, I would like to see about getting collisions working.

In my Three.js representation, I add some fruit (because what kid doesn't want to play a game with health foods?):
  fruit = new THREE.Mesh(
    new THREE.CylinderGeometry(20, 20, 1, 24),
    new THREE.MeshNormalMaterial({color: 0xB22222})
  );
  fruit.rotation.x = Math.PI/2;
  scene.add(fruit);
Then, I add a corresponding circle in the Box2dWeb initialization:
  // fruit
  bodyDef.type = b2Body.b2_dynamicBody;
  fixDef.shape = new b2CircleShape(20);
  bodyDef.position.x = 75;
  bodyDef.position.y = 10;
  fruit_fixture = world.CreateBody(bodyDef).CreateFixture(fixDef);
And, lastly, in my gameStep(), I link the position of the fruit in the Three.js animation with it in the Box2dWeb world:
function gameStep() {
  world.Step(
    1 / 60,   //frame-rate
    10,       //velocity iterations
    10       //position iterations
  );
  world.ClearForces();

  var ppos = player_fixture.GetBody().GetDefinition().position;
  player.position.x = ppos.x;
  player.position.y = ppos.y;

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

  setTimeout(gameStep, 1000 / 60); // process the game logic
                                   // at a target 60 FPS.
}
And it works. Kind of.

I can see both the "player" (the rectangle) and the fruit (the circle):


I can even bump into the fruit. Only weird stuff happens. Like the player suddenly falls below ground level:


I cannot figure out what is going on. Maybe Box2dWeb and Three.js disagree over the placement or shape of one or both objects.

To figure this out, I make use of the debug draw that comes with Box2dWeb. In the Box2dWeb setup, I add the following (more or less copied from the Box2dWeb samples):
  var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;

  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);
And tell gameStep() to update the debugging drawing:
function gameStep() {
  world.Step(
    1 / 60,   //frame-rate
    10,       //velocity iterations
    10       //position iterations
  );
  world.DrawDebugData();
  world.ClearForces();
  // ...
}
The result is that... my player has fallen down:


Sigh. That seems obvious in retrospect. I had it in the back of my mind that I ought to be replicating the rotation as well as the position between the two worlds. Somehow I managed to forget this. It was not until I could visualize the two side-by-side that I was able to work out the problem.

So I set the Three.js rotation to the Box2dWeb rotation:
function gameStep() {
  // ...
  var ppos = player_fixture.GetBody().GetDefinition().position;
  player.position.x = ppos.x;
  player.position.y = ppos.y;
  player.rotation.z = player_fixture.GetBody().GetDefinition().angle;
  // ...

  setTimeout(gameStep, 1000 / 60); // process the game logic
                                   // at a target 60 FPS.
}
Which seems to resolve the issue:


Well, except that I do not want my player to fall. Still, knowing what the problem is gets me started on a solution. Tomorrow.


Day #499

No comments:

Post a Comment