Skip to content
Advertisement

THREE.js collision detection and stop/move again

I have a rounded ground and there is a ball on it. trying to move the ball inside the ground with using smartphone orientation. no problem with that part. the ball is moving and make the ball stop at the edge by using three.js intersectsSphere. but couldn’t manage the move the ball again if the orientation points opposite direction. here is the working sample.

https://codesandbox.io/s/jovial-mirzakhani-f50p2l?file=/src/App.vue

sample code below;

export const LIGHT = () => {
  const light = new THREE.SpotLight(0xffffff, 1);
  light.position.set(100, 1, 0);
  light.castShadow = true;
  light.position.set(0, 0, 35);

  return light;
};

export const BALL = () => {
  const geometry = new THREE.SphereGeometry(3, 10, 10);
  const material = new THREE.MeshLambertMaterial({ color: "#f1c40f" });
  const mesh = new THREE.Mesh(geometry, material);

  mesh.castShadow = true;
  mesh.receiveShadow = false;
  return mesh;
};

export const BALL_BOUNDING = (sphere) => {
  return new THREE.Sphere(sphere.position, 3);
};

export const GROUND = () => {
  const geometry = new THREE.CircleGeometry(21, 21, 0);
  const material = new THREE.MeshPhongMaterial({
    color: "#bdc3c7"
    // transparent: true,
    // opacity: 0,
  });

  const mesh = new THREE.Mesh(geometry, material);

  mesh.castShadow = true;
  mesh.position.z = 1;
  return mesh;
};

export const GROUND_BOUNDING = (ground) => {
  const geometry = new THREE.SphereGeometry(13, 8, 8);
  const material = new THREE.MeshLambertMaterial({
    color: "#FFF"
  });

  const mesh = new THREE.Mesh(geometry, material);
  // expanding bounding XYZ
  return new THREE.Sphere(mesh.position, 18);
};

const ww = window.innerWidth;
const wh = window.innerHeight;
const renderer = new THREE.WebGLRenderer({ canvas: canvas.value, alpha: true });
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(35, ww / wh, 0.1, 1000);
const light = _light()
const ball = BALL()
const ball_bounding = BALL_BOUNDING(ball)
const ground = GROUND()
const ground_bounding = GROUND_BOUNDING(ground)

renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.position.z = 120;

scene.add(ball)
scene.add(ground)
scene.add(light)

function animate() {
  requestAnimationFrame(animate)
  render()
}

function render() {
  const x = posX.value / 20
  const y = posY.value / 20

  if (ball.geometry.boundingSphere !== null) {
    ball.geometry.computeBoundingSphere()
    ball_bounding.copy(ball.geometry.boundingSphere).applyMatrix4(ball.matrixWorld)
  }

  if (ground_bounding.intersectsSphere(ball_bounding)) {
    
    ball.position.x += x;
    ball.position.y -= y;
  }

  renderer.render(scene, camera)

}

animate()

Advertisement

Answer

Think about the very moment when the ball goes out of intersection, which is the moment when ground_bounding.intersectsSphere(ball_bounding) returns true for the last time, what happens exactly? Let’s take some time to ponder about it:

  1. ground_bounding.intersectsSphere(ball_bounding) returns true
  2. so you feel comfortable to move the ball ball.position.x += x
  3. next round animate(), ground_bounding.intersectsSphere(ball_bounding) returns false
  4. at this point, the ball is already moved (in the previous round) to a position where ground_bounding.intersectsSphere(ball_bounding) always returns false, we’re stuck in the state forever.

Mistake is made at step 2, the ball SHOULDN’T be moved even though step 1 says true. Because if moved, next round it will definitely say false, and got stuck there forever.

Solution is to make a dummy clone of ball_bounding, then move the dummy first, and do intersection test against the dummy. If dummy is still within intersection after moved, that means it’s also safe to move the ball.

const dummy = ball_bounding.clone();
dummy.center.x += x;
dummy.center.y -= y;
if (ground_bounding.intersectsSphere(dummy)) {
    ball.position.x += x;
    ball.position.y -= y;
}
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement