Monday, September 3, 2012

Movement with Box2dWeb and Three.js

‹prev | My Chain | next›

Last night, I got started on a new game for Gaming JavaScript. This one still uses Three.js to draw items, but relies on Box2dWeb for physics. In many ways, this is similar to Gladius with the Box2D plugin, though with more work.

The work involves maintaining a shadow version of the scene for Box2d physics. The terminology is also a bit foreign to me: bodies, body defs, and fixture defs. In the end, I was able to make my player (just a rectangluar cube for now) fall from a height. Today I would like to be able to move my player.

Since my gameStep() updates the player position based on the physics position already:
function gameStep() {
  world.Step(
    1 / 60,   //frame-rate
    10,       //velocity iterations
    10       //position iterations
  );
  world.ClearForces();

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

  setTimeout(gameStep, 1000 / 60); // process the game logic
                                   // at a target 60 FPS.
}
I ought to be able to have key-presses affect the Box2d position and see that reflected on the screen. That is the hope at least.

I establish a simple keydown event handler on the page:
document.addEventListener("keydown", function(event) {
  var code = event.which || event.keyCode;

  var events = {
    37: left,  // left arrow
    39: right, // right arrow
    38: up,    // up arrow
    32: up     // space bar
  };

  if (events[code]) events[code]();
  else console.log(code);
});
And I get started with the left() function to move the player left:
function left() {
  var b2Vec2 = Box2D.Common.Math.b2Vec2
    , player = player_fixture.GetBody().GetDefinition();

  player.linearVelocity = new b2Vec2(-10, 0);
}
Setting the linear velocity on the body definition does not, in fact work. I would have been surprised if it had—the rest of the physics engine would have to pay attention to an awful lot of properties that could change at any moment.

Anyhow, I dig though the documentation for a bit, eventually finding that this works:
function left() {
  var b2Vec2 = Box2D.Common.Math.b2Vec2
    , body = player_fixture.GetBody();

  body.ApplyForce(
    new b2Vec2(-1e4, 0),
    body.GetWorldCenter()
  );
}
I then generalize this to work with left, right and up:
function left()  { move(-1e4, 0); }
function right() { move(1e4, 0); }
function up()    { move(0, 1e5); }

function move(x, y) {
  var b2Vec2 = Box2D.Common.Math.b2Vec2
    , body = player_fixture.GetBody()
    , method = (y>0) ? 'ApplyForce' : 'ApplyImpulse';

  body[method](
    new b2Vec2(x, y),
    body.GetWorldCenter()
  );
}
The switch between ApplyForce and ApplyImpulse is a nod to normal game feel. When the direction of movement is up (y > 0), then the existing momentum should continue. Hence I apply a force in the up direction. In most games (kid games at least), a move to the left or right immediately affects the movement in that direction. Applying an impulse in Box2D does just that.

And all of this works fairly well. I can jump:




And I can even do crazy things like reverse direction in mid-air. Happily, tying the Three.js player position to the Box2dWeb position seems to work like a charm. At least with simple player movement.

I call it a day here so that I can get back to writing. Tomorrow I will pick back up with collisions.


Day #498

No comments:

Post a Comment