1

I have a problem because - if I rotate the block collision is still a box. I want to make hit boxes rotated too but I don't know what to do.

Also I don't use import in libraries.

Here is my code to detect all collisions for a player with all meshes:

function checkCollisions() {
    const playerBox = new THREE.Box3().setFromObject(player);
    const nearbyObjects = scene.children.filter(obj => obj !== player && obj.isMesh && obj.name !== "deco");

    nearbyObjects.forEach(obj => {
        const objectBox = new THREE.Box3().setFromObject(obj);
        if (playerBox.intersectsBox(objectBox)) {

 
            const playerPos = player.position.clone();
            const objPos = obj.position.clone();
            const diff = playerPos.sub(objPos);

 
            const boundingBox = new THREE.Box3().setFromObject(obj);
            const playerHalfWidth = player.geometry.parameters.width / 2;
            const playerHalfHeight = player.geometry.parameters.height / 2;
            const playerHalfDepth = player.geometry.parameters.depth / 2;

            const obstacleHalfWidth = (boundingBox.max.x - boundingBox.min.x) / 2;
            const obstacleHalfHeight = (boundingBox.max.y - boundingBox.min.y) / 2;
            const obstacleHalfDepth = (boundingBox.max.z - boundingBox.min.z) / 2;

            const dx = (playerHalfWidth + obstacleHalfWidth) - Math.abs(diff.x);
            const dy = (playerHalfHeight + obstacleHalfHeight) - Math.abs(diff.y);
            const dz = (playerHalfDepth + obstacleHalfDepth) - Math.abs(diff.z);

 
            if (dx < dy && dx < dz) {
                // Left or Right side
                const normal = new THREE.Vector3(diff.x > 0 ? 1 : -1, 0, 0);
                player.position.x += dx * normal.x * 0.8 
                playerVelocity.x *= 0.2 
            } else if (dy < dx && dy < dz) {
                // Bottom or Top side
                if (diff.y > 0) { // If colliding from below
                    player.position.y += dy * 0.8 
                    playerVelocity.y = Math.max(playerVelocity.y, 0);  
                    isJumping = false;
                } else { // If colliding from above
                    player.position.y -= dy * 0.8;
                    playerVelocity.y *= -0.2 
                }
            } else {
                // Front or Back side
                const normal = new THREE.Vector3(0, 0, diff.z > 0 ? 1 : -1);
                player.position.z += dz * normal.z * 0.8 
                playerVelocity.z *= 0.2 
            }
        }
    });
}

1 Answer 1

0

Basic collision is just measuring distance in axis directions. It computes whether two axis-aligned boxes touch/intersect, by comparing their sizes along the axes (r167 code).

When you rotate a box, the math becomes harder, because you are now dealing with planes that are no longer axis aligned. I won't get into solving that problem here, but I'm sure you can find methods to achieve this if you really want to stick with bounding boxes.

One alternative is to use a geometry's .boundingSphere and Sphere.intersectsSphere(...). This is less accurate than a box because long/narrow shapes will have significant "padding" along the long axes. Picture a pencil inside a tight-fit bubble, and you can see there are areas a sphere would cover that are quite far from the pencil. That said, as a sphere it does not suffer the same rotational problems as a box. No matter what orientation your object is in, the sphere's shape remains constant.

Which brings us to another alternative, which is to simply store a "radius" value on the object. This can represent a sphere-like radius, but is defined by you, so it can be "tighter" and allow some wiggle-room for long/narrow objects. For example, your "radius" could be shorter than the ends of an object to reduce the "padding" in other areas (like the pencil example). Still, you would compute a collision the same way a sphere does.

myMesh1.userData.collisionRadius = 5
myMesh2.userData.collisionRadius = 7

// ...later...

function collision( mesh1, mesh2 ) {

  let collided = false

  const distanceBetween = mesh1.position.distanceTo( mesh2.position )

  const collisionThreshold = mesh1.userData.collisionRadius + mesh2.userData.collisionRadius

  if ( distanceBetween  <= collisionThreshold  ) {

    collided = true

  }

  return collided

}

A couple notes about the code above:

  • It assumes the meshes are in the same local space. If they aren't, you may need to use Object3D.localToWorld to put their respective position vectors both in world space. Do note that .localToWorld is destructive and changes the Vector3 passed to it, so it's better to pass copies into .localToWorld.
  • The code above isn't written to be efficient, it is written to debug each step. Once you understand what's going on, you can re-write it to work better.

Not the answer you're looking for? Browse other questions tagged or ask your own question.