Search code examples
three.jscollision-detection

Three.js: First person camera collision detection


I have a very simple scenario in Three.js, where the user can move the camera around in order to have a first person perspective.

The camera must not move inside an object, e.g. a cube. I try to prevent that with Raycaster.intersectObjects. Hovever, only one face of the cube is recognized when I walk into it. It seems as if the other faces of the cube are not "visible" for the ray, but of course they are perfectly rendered.

The relevant code looks like this:

cubeGeometry = new THREE.CubeGeometry(size, size, size, 20, 20, 20);
cubeMesh = new THREE.Mesh(cubeGeometry, new THREE.MeshBasicMaterial({color: 0x00FF00, wireframe: true, side: THREE.DoubleSide}));
geometries.push(cubeMesh); 

...

var oldCamera;
function moveAround(){
    oldCamera = camera.clone();
    if(movement=="forward") camera.translateZ(-30);
    if(movement=="left") camera.translateX(-30);
    if(movement=="backward") camera.translateZ(30);
    if(movement=="right") camera.translateX(30);
    ray = new THREE.Raycaster(camera.position, oldCamera.position.normalize());
    ray.far = 100;
    if(ray.intersectObjects(geometries).length>0){
        // only the case with one of the faces of a cube
        ...

[EDIT 2020.05.09]

I uploaded a working example on GitHub:

https://github.com/TovAqulic/threejs_collision_issue/blob/master/index.html

You can move (the camera i.e. yourself) around by using the arrow keys and can turn by using the mouse.

All the geometry objects, i.e. the surrounding walls and the block in the middle, are part of the geometries array that is checked with raycaster.intersectObjects(geometries).

In almost all cases the movement is blocked when moving onto a wall, by frontal, backward or sideways movement, as expected.

However, there are positions where you can move through the walls, particularly the walls to the right. E.g., just move to the right wall directly after loading the page.

I don't understand what I am doing wrong.


Solution

  • ray = new THREE.Raycaster(camera.position, oldCamera.position.normalize());

    This code is not ideal. Create your instance of THREE.Raycaster once and then reuse it. Also name it raycaster (since it is not an instance of THREE.Ray). Besides, before calling intersectObjects(), use this code to prepare the raycaster:

    raycaster.ray.origin.copy( camera.position );
    camera.getWorldDirection( raycaster.ray.direction );