Friday, August 17, 2012

Finishing Up with Physijs Avatar

‹prev | My Chain | next›

Yesterday, I discovered the benefits of __dirtyRotation in Physijs, this physics engine for Three.js. I used it yesterday to prevent my player from spinning out of control after contact with other objects. Tonight, I plan to use it to restore the spin-to-walk ability.

I am not going to rewrite the Three.js first person controls, but I need some of that ability. I start with global variables that hold state (moving and state):
var moving = false;
var controlState = {};
Next, I need a keydown handler to set the state:
document.addEventListener("keydown", function(event) {
  // Last wins (for now) because we set the velocity, not apply force
  function setState(state) {
    moving = true;
    controlState = {};
    controlState[state] = true;
  }
  
  var speed = 500;
  var code = event.which || event.keyCode;
  if (code == 0x57) { // w
    setState("moveForward");
    a_frame.setLinearVelocity({z: -speed, y: 0, x: 0 });
  }
  // ...
});
I feel a little bad about overwriting controlState and the velocity like that, but it works for now—movement is only in one direction.

I also use a corresponding keyup handler to reset controlState and mark the avatar as no longer moving:
document.addEventListener("keyup", function(event) {
  function stopState(state) {
    if (controlState[state]) {
      moving = false;
      controlState = {};
    }
  }

  var code = event.which || event.keyCode;
  if (code == 0x57) { // w
    stopState("moveForward");
    // a_frame.setLinearVelocity({z: 0, y: 0, x: 0 });
  }
  // ...
});
This allows me to restore the motion of the legs when moving is true. I can also spin the avatar in the proper direction depending on the controls' state:
var w = 500;
function render() {
  var t_float = clock.getElapsedTime()
    , t = t_float * 1000
    , amplitude = (w/2 - Math.abs((t % (2*w)) - w))/w;

  if (moving) {
    avatar_left_leg.rotation.x = amplitude*(Math.PI/6);
    // ...
    if (controlState.moveLeft) spinAvatar(-Math.PI/2);
    // ...
  }
  else {
    spinAvatar(0);
  }

  scene.simulate(); // run physics

  renderer.render(scene, camera);
}
Even the spinAvatar() function still works. It works because it spins the avatar inside the Physijs frame so no changes are required:
function spinAvatar(angle) {
  new TWEEN.Tween( { y: avatar.rotation.y } )
      .to( { y: angle }, 100 )
      .onUpdate( function () {
         avatar.rotation.y = this.y;
      })
      .start();
}
So it turns out that I did not need any additional use of __dirtyRotation. As long as it keeps the frame of reference steady, everything just works. Nice.

This is all going so smoothly. Something is going to go horribly wrong soon. I just know it.

But failure does not arrive when I set up invisible fences around the island:
  scene = new Physijs.Scene;

  var fence = new Physijs.BoxMesh(
    new THREE.CubeGeometry(ISLAND_WIDTH, 1000, 10),
    new THREE.Material(),
    0
  );
  fence.position.z = -ISLAND_HALF;
  fence.position.y = 500;
  scene.add(fence);
The last argument of zero to the BoxMesh constructor specifies a mass of zero, which is interpreted as a static object in Physijs. In other words, my fence will not yield, keeping the avatar on the island:


I finally do run into a small problem with the avatar's interaction with the ball that I have left on the island for the avatar's amusement. My avatar can no longer budge it:


What fun is that?

Fortunately, it does not take too long to identify the problem. I specified a mass of 1000 for my avatar's frame of reference / Physijs box:
  a_frame = new Physijs.BoxMesh(
    new THREE.CubeGeometry(250, 250, 250),
    new THREE.Material(),
    1000
  );
It seems that, in Physijs, 1000 is very light. I suppose it is measured in grams.

Anyhow, with a slightly heavier mass:
  a_frame = new Physijs.BoxMesh(
    new THREE.CubeGeometry(250, 250, 250),
    new THREE.Material(),
    1000*1000*1000
  );
I can now send my ball rolling and bouncing around:


It is literally minutes of fun!

Actually, what this really means is that I seem to have gotten the hang of Physijs.


Day #481

No comments:

Post a Comment