Friday, August 24, 2012

Collision Events in Physijs

‹prev | My Chain | next›

Up tonight, I would like to apply a constant force on the raft in my simple Three.js / Physijs game. My first thought is to use Physijs events to detect when the raft hits individual segments of the river. I can then push the raft in the same direction that the river segment is rotated.

So I start with a heavily logged addCurrent() function:
function addCurrent(river_segment, c, s) {
  river_segment.addEventListener('collision', function(object) {
    if (object.id != raft.id) return;
    console.log(this.id);
    console.log(object.id);
    console.log(c + " ::: " + s);
    raft.applyCentralForce(new THREE.Vector3(1e8 * c, 0, 1e8 * s));
  });
}
The c and s variables are the sine and cosine of the river's current angle. Unfortunately, when the animation starts, I find that I am colliding immediately with all three plane segments that I have added:
39
42
0.9238795325112867 ::: -0.3826834323650898 
36 
42 
1 ::: 0 
33 
42 
0.9238795325112867 ::: 0.3826834323650898
The same raft object (as evidenced byt the ID 42) is colliding with each plane simultaneously. As the raft moves down the river from one segment to another, no subsequent collision events are registered.

If I invert the collision detect to find what the raft is colliding with:
  raft = buildRaft();
  scene.add(raft);
  raft.setDamping(0.0, 1.0);
  raft.addEventListener('collision', function(object) {
    console.log('raft');
    console.log(object);
  });
Then I see similar results. The raft is colliding with multiple PlaneMesh objects even though it seems to only overlap one:


Bah! I do not really need to use collisions to implement this. Three.js rays might be a more appropriate solution. Still, I can't let this pass without investigation.

So I create a new, simpler scene with no gravity:
var renderer, scene, camera, cube1, cube2;

Physijs.scripts.worker = 'scripts/physijs_worker.js';
Physijs.scripts.ammo = 'ammo.js';

document.addEventListener( "DOMContentLoaded", function() {
  init();
  animate();
});

function init() {
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearColorHex(0x87CEEB);
  document.body.appendChild(renderer.domElement);

  scene = new Physijs.Scene;
  scene.setGravity(
    new THREE.Vector3(0,0,0)
  );

  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = 100;
  scene.add(camera);
}

function animate() {
  requestAnimationFrame(animate);
  scene.simulate(); // run physics
  render();
}

function render() {
  renderer.render(scene, camera);
}
In the init() function, I add two cubes, one of which gets an event listener:
  cube1 = new Physijs.BoxMesh(
    new THREE.CubeGeometry(10, 10, 10),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 0.9
    )
  );
  cube1.position.x = -50;
  scene.add(cube1);
  console.log("cube 1: " + cube1.id);

  cube2 = new Physijs.BoxMesh(
    new THREE.CubeGeometry(10, 10, 10),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 0.9
    )
  );
  cube2.position.x = 50;
  scene.add(cube2);
  console.log("cube 2: " + cube2.id);

  cube2.addEventListener('collision', function(object) {
    console.log("Object " + this.id + " collided with " + object.id);
  });
In the JavaScript console, I give one cube a shove, wait, and then see the expected collision:


Brilliant. Collision events work in Physijs. But what am I doing wrong in my raft game? Perhaps planes are the culprit. To test that theory, I add a plane:
var plane = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(15, 1000),
    new THREE.MeshBasicMaterial({color: 0x7CFC00})
  );
  plane.position.y = -5;
  scene.add(plane);
This goes right down the middle of the two cubes so it should collide with nothing. However, after reloading, I find:


Ah ha! Cube #2 is registers a collision with the plane without even touching it. That certainly explains the behavior that I see in my raft game.

I am unsure if this is a bug. On the one hand the two objects do not touch so how can they collide? On the other hand, the face of a cube touching a plane does not feel like a "collision".

As I said, I think that I can likely get river segments to push the raft through judicious use of Three.js rays. I will give that a try tomorrow.


Day #488

2 comments:

  1. Could you use a cube with a really shallow depth, like 1px?

    ReplyDelete
    Replies
    1. That's a good thought, but it doesn't seem to work. If the cube and cube-plane share a plane, then the collision is registered, but sends the cube tumbling off into space.

      If they don't share a plane, then the cube passes right over top of the cube-plane without registering a collision.

      Really, what I'm trying is probably not possible -- at least not with collisions. I want the cube to glide on top of the plane (or cube-plane), have it register as a collision, but not bump the cube into a tumble. A collision without possibility of a tumble does not sound physical.

      Thanks for the suggestion -- I think it clears up my thinking some. In other words, I think a ray is probably the way to go.

      Delete