Moving a body to a specific position

2.1k views Asked by At

I understand that I can use body.position.set(x, y, z) to instantaneously move a body, but how can I move it smoothly in an animated manner where it's movement will adhere to the physics and collide with any other bodies on its journey? Using body.velocity.set(x, y, z) will set its velocity, and using body.linearDamping = v, will provide some friction/resistance... but it's still not good enough to allow me to specify exactly where I want the body to stop.

2

There are 2 answers

0
schteppe On BEST ANSWER

It sounds like you're looking for a kinematic body. With kinematic bodies, you have full control over the movement, and it will push away other bodies in its path. However, the body has infinite mass and is not affected by other bodies colliding with it.

Start off by defining the start and end positions of your body.

var startPosition = new CANNON.Vec3(5, 0, 2);
var endPosition = new CANNON.Vec3(-5, 0, 2);
var tweenTime = 3; // seconds

Then create your kinematic body. In this example we'll add a Box shape to it.

var body = new CANNON.Body({
  mass: 0,
  type: CANNON.Body.KINEMATIC,
  position: startPosition
});
body.addShape(new CANNON.Box(new CANNON.Vec3(1,1,1)));
world.add(body);

Compute the direction vector and get total length of the tween path.

var direction = new CANNON.Vec3();
endPosition.vsub(startPosition, direction);
var totalLength = direction.length();
direction.normalize();

The speed and velocity can be calculated using the formula v = s / t.

var speed = totalLength / tweenTime;
direction.scale(speed, body.velocity);

For each update, compute the tween progress: a number between 0 and 1 where 0 i start position and 1 is end position. Using this number you can calculate the current body position.

var progress = (world.time - startTime) / tweenTime;
if(progress < 1){
  // Calculate current position
  direction.scale(progress * totalLength, offset);
  startPosition.vadd(offset, body.position);
} else {
  // We passed the end position! Stop.
  body.velocity.set(0,0,0);
  body.position.copy(endPosition);
}

See full code below. You can duplicate one of the cannon.js demos and just paste this code.

var demo = new CANNON.Demo();

var postStepHandler;

demo.addScene("Tween box",function(){
  var world = demo.getWorld();

  // Inputs
  var startPosition = new CANNON.Vec3(5, 0, 2);
  var endPosition = new CANNON.Vec3(-5, 0, 2);
  var tweenTime = 3; // seconds

  var body = new CANNON.Body({
    mass: 0,
    type: CANNON.Body.KINEMATIC,
    position: startPosition
  });
  body.addShape(new CANNON.Box(new CANNON.Vec3(1,1,1)));
  world.add(body);
  demo.addVisual(body);

  if(postStepHandler){
    world.removeEventListener('postStep', postStepHandler);
  }

  // Compute direction vector and get total length of the path
  var direction = new CANNON.Vec3();
  endPosition.vsub(startPosition, direction);
  var totalLength = direction.length();
  direction.normalize();

  var speed = totalLength / tweenTime;
  direction.scale(speed, body.velocity);

  // Save the start time
  var startTime = world.time;

  var offset = new CANNON.Vec3();

  postStepHandler = function(){

    // Progress is a number where 0 is at start position and 1 is at end position
    var progress = (world.time - startTime) / tweenTime;

    if(progress < 1){
      direction.scale(progress * totalLength, offset);
      startPosition.vadd(offset, body.position);
    } else {
      body.velocity.set(0,0,0);
      body.position.copy(endPosition);
      world.removeEventListener('postStep', postStepHandler);
      postStepHandler = null;
    }
  }

  world.addEventListener('postStep', postStepHandler);
});

demo.start();
0
Bob Woodley On

You need to use a physics library for this, such as Physijs. It works easily with Three.js. Googling for "Physijs Three.js" will provide examples.