Search code examples
three.jscollision-detectionraycasting

Three.js raycasting collision not working


I am working on an arcade style Everest Flight Simulator.

In my debugger where I am building this, I have a terrain and helicopter class which generate the BufferGeometry terrain mesh, the Groups for the helipad Geometries, and the group for the helicopter Camera and Geometry.

My issue is that currently I can't seem to get any collision to detect. I imagine it may not support BufferGeometries so that is an issue for me because I need the terrain to be a Buffer since it's far too expansive... as a standard geometry it causes a memory crash in the browser.

However, testing the helipad geometries alone it still does not trigger. They are in a group so I add the groups to a global window array and set the collision check to be recursive but to no avail.

Ultimately, I am open to other forms of collision detection and may need two types as I have to use buffer geometries. Any ideas on how to fix this or a better solution?

The Helicopter Object Itself

// Rect to Simulate Helicopter
const geometry = new THREE.BoxGeometry( 2, 1, 4 ),
      material = new THREE.MeshBasicMaterial(),
      rect = new THREE.Mesh( geometry, material );

rect.position.x = 0;
rect.position.y = terrain.returnCameraStartPosY();
rect.position.z = 0;
rect.rotation.order = "YXZ";
rect.name = "heli";

// Link Camera and Helicopter
const heliCam = new THREE.Group(),
      player = new Helicopter(heliCam, "OH-58 Kiowa", 14000);

heliCam.add(camera);
heliCam.add(rect);
heliCam.position.set( 0, 2040, -2000 );
heliCam.name = "heliCam";
scene.add(heliCam);

Adding Objects to Global Collision Array

// Add Terrain
const terrain = new Terrain.ProceduralTerrain(),
      terrainObj = terrain.returnTerrainObj(),
      helipadEnd = new Terrain.Helipad( 0, 1200, -3600, "Finish", true ),
      helipadStart = new Terrain.Helipad( 0, 2000, -2000, "Start", false ),
      helipadObjStart = helipadStart.returnHelipadObj(),
      helipadObjEnd = helipadEnd.returnHelipadObj();

window.collidableMeshList.push(terrainObj);
window.collidableMeshList.push(helipadObjStart);
window.collidableMeshList.push(helipadObjEnd);

Collision Detection Function Run Every Frame

collisionDetection(){
    const playerOrigin = this.heli.children[1].clone(); // Get Box Mesh from Player Group

    for (let i = playerOrigin.geometry.vertices.length - 1; i >= 0; i--) {
        const localVertex      = playerOrigin.geometry.vertices[i].clone(),
              globalVertex     = localVertex.applyMatrix4( playerOrigin.matrix ),
              directionVector  = globalVertex.sub( playerOrigin.position ),
              ray              = new THREE.Raycaster( playerOrigin, directionVector.clone().normalize() ),
              collisionResults = ray.intersectObjects( window.collidableMeshList, true ); // Recursive Boolean for children

        if ( collisionResults.length > 0 ){
            this.landed = true;
            console.log("Collision");
        }

        // if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ){
        //  this.landed = true;
        //  console.log("Collision with vectorLength")
        // }    
    }
}

Solution

  • It's hard to tell what's going on inside your custom classes, but it looks like you're using an Object3D as the first argument of the raycaster, instead of a Vector3 when you use this.heli.children[1].clone(). Why don't you try something like:

    var raycaster = new THREE.Raycaster();
    var origin = this.heli.children[1].position;
    
    raycaster.set(origin, direction);
    

    Also, are you sure you're using a BufferGeometry? Because when you access a vertex value like this: playerOrigin.geometry.vertices[i], it should give you an error. There is no vertices attribute in a BufferGeometry so I don't know how you're determining the direction vector.