Tuesday, July 24, 2012

Animating Three.js Rotation

‹prev | My Chain | next›


My Three.js avatar controls are working fairly well. There are a couple of problems still lingering. The first that I hope to fix tonight is that the avatar is facing the wrong direction when moving left or right. I need for it to face left or right, but instead it continues to point forward.

This is not as simple as rotating the avatar about the y-axis (up and down):
function render() {
    // ...
    if (controls.moveLeft) avatar.rotation.y = -Math.PI/2;
    if (controls.moveRight) avatar.rotation.y = Math.PI/2;
    // ...
}
The problem with that approach is that the avatar is added to the entire scene:
  avatar = buildAvatar();
  scene.add(avatar);
By adding to the entire scene, the y-axis for the avatar is at the origin. In other words, if I rotate about the y-axis, I rotate about the origin and end up on the other side of the world.

To make rotation work around the center of the avatar, I need to add a frame of reference for the avatar:
  avatar = buildAvatar();
  var a_frame = new THREE.Object3D();
  a_frame.add(avatar);
  scene.add(a_frame);
Instead of tying the FirstPersonControls to the avatar, I now need to tie them to this frame of reference:
  avatar = buildAvatar();
  var a_frame = new THREE.Object3D();
  a_frame.add(avatar);
  scene.add(a_frame);

  controls = new THREE.FirstPersonControls(a_frame);
  controls.movementSpeed = 10000;
  controls.activeLook = false;
Now, rotating the avatar about the y-axis will rotate around the new a_frame y-axis, so this will work:
function render() {
    // ...
    if (controls.moveLeft) avatar.rotation.y = -Math.PI/2;
    if (controls.moveRight) avatar.rotation.y = Math.PI/2;
    // ...
}
And, indeed it does work as I walk from left to right, I am facing the direction in which I am walking:


Last up tonight, I would like the transition from walking forward to walking to the side to be smoother. Currently, moving to the side immediately renders the avatar facing to the side.

For that, I am going to use Tween, which comes along with Three.js in examples/js/Tween.js. After copying that into my /scripts directory, I update the HTML to source this library in addition to Three.js:
<!DOCTYPE html>
<html>
  <head>
    <script src="/scripts/Three.js"></script>
    <script src="/scripts/Tween.js"></script>
    <script src="/scripts/avatar.js"></script>
    <title>Avatar</title>
  </head>
  <body>
  </body>
</html>
Next, I update the usual Three.js animate() function to update any active "Tweens":
function animate() {
  // note: three.js includes requestAnimationFrame shim
  requestAnimationFrame(animate);
  TWEEN.update();
  render();
}
Without a running "tween", this has no effect. So I add one. The animation that I desire is for the avatar to rotate from its current y-axis frame of reference rotation to some end-angle (e.g. +90° or -90°). In tween-ese, this means starting an object's y property at the avatar's current rotation, describing the end state for the y rotation and the number of milliseconds that the animation should take. Lastly, I need an onUpdate callback that will effect slight change on the avatar:
function spinAvatar(angle) {
  new TWEEN.Tween( { y: avatar.rotation.y } )
      .to( { y: angle }, 100 )
      .onUpdate( function () {
          avatar.rotation.y = this.y;
      } )
      .start();
}
That is a nice, compact DSL for describing changes over time. I like it. It certainly beats trying to manually keep track of the rotation myself in the game's animate() function.

I swap out the immediate angle changes for this spinAvatar() tween:
function render() {
  // ...
  if (controls.moveForward || controls.moveBackward ||
      controls.moveRight || controls.moveLeft) {
    // ...
    if (controls.moveLeft) spinAvatar(-Math.PI/2);
    if (controls.moveRight) spinAvatar(Math.PI/2);
  }
  else {
    spinAvatar(0);
  }
  // ...
}
And it works! I have a nice, smooth rotation immediately when the avatar starts walking, giving the whole thing a nice feel.


Day #457

2 comments:

  1. Could you post a full sample code using the above..?

    ReplyDelete
    Replies
    1. I believe that the full version is located at: https://github.com/eee-c/threejs-my/blob/gh-pages/scripts/avatar.js. That version may have additional tweaks and may not correspond exactly to this post, but it looks close after skimming it.

      Delete