How to detect collision in three.js?

前端 未结 4 1931
小鲜肉
小鲜肉 2020-11-30 23:48

I am using three.js.

I have two mesh geometries in my scene.

If these geometries are intersected (or would intersect if translated) I want to detect

相关标签:
4条回答
  • 2020-11-30 23:52

    since my other answer is limited I made something else that is more accurate and only returns true when there is a collision and false when there isn't (but sometimes when There still is) anyway, First make The Following Function:

    function rt(a,b) {
      let d = [b]; 
      let e = a.position.clone();
      let f = a.geometry.vertices.length;
      let g = a.position;
      let h = a.matrix;
      let i = a.geometry.vertices;
        for (var vertexIndex = f-1; vertexIndex >= 0; vertexIndex--) {      
            let localVertex = i[vertexIndex].clone();
            let globalVertex = localVertex.applyMatrix4(h);
            let directionVector = globalVertex.sub(g);
            
            let ray = new THREE.Raycaster(e,directionVector.clone().normalize());
            let collisionResults = ray.intersectObjects(d);
            if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ) { 
                return true;
        }
        }
     return false;
    }
    

    that above Function is the same as an answer in this question by Lee Stemkoski (who I am giving credit for by typing that) but I made changes so it runs faster and you don't need to create an array of meshes. Ok step 2: create this function:

    function ft(a,b) {
      return rt(a,b)||rt(b,a)||(a.position.z==b.position.z&&a.position.x==b.position.x&&a.position.y==b.position.y)
    }
    

    it returns true if the center of mesh A isn't in mesh B AND the center of mesh B isn't in A OR There positions are equal AND they are actually touching. This DOES still work if you scale one (or both) of the meshes. I have an example at: https://3d-collsion-test-r.glitch.me/

    0 讨论(0)
  • 2020-11-30 23:55

    This really is far too broad of a topic to cover in a SO question, but for the sake of greasing the SEO of the site a bit, here's a couple of simple starting points:

    If you want really simple collision detection and not a full-on physics engine then check out (link removed due to no more existing website)

    If, on the other hand you DO want some collision response, not just "did A and B bump?", take a look at Physijs, which is a super easy to use Ammo.js wrapper built around Three.js

    0 讨论(0)
  • In Three.js, the utilities CollisionUtils.js and Collisions.js no longer seem to be supported, and mrdoob (creator of three.js) himself recommends updating to the most recent version of three.js and use the Ray class for this purpose instead. What follows is one way to go about it.

    The idea is this: let's say that we want to check if a given mesh, called "Player", intersects any meshes contained in an array called "collidableMeshList". What we can do is create a set of rays which start at the coordinates of the Player mesh (Player.position), and extend towards each vertex in the geometry of the Player mesh. Each Ray has a method called "intersectObjects" which returns an array of objects that the Ray intersected with, and the distance to each of these objects (as measured from the origin of the Ray). If the distance to an intersection is less than the distance between the Player's position and the geometry's vertex, then the collision occurred on the interior of the player's mesh -- what we would probably call an "actual" collision.

    I have posted a working example at:

    http://stemkoski.github.io/Three.js/Collision-Detection.html

    You can move the red wireframe cube with the arrow keys and rotate it with W/A/S/D. When it intersects one of the blue cubes, the word "Hit" will appear at the top of the screen once for every intersection as described above. The important part of the code is below.

    for (var vertexIndex = 0; vertexIndex < Player.geometry.vertices.length; vertexIndex++)
    {       
        var localVertex = Player.geometry.vertices[vertexIndex].clone();
        var globalVertex = Player.matrix.multiplyVector3(localVertex);
        var directionVector = globalVertex.subSelf( Player.position );
    
        var ray = new THREE.Ray( Player.position, directionVector.clone().normalize() );
        var collisionResults = ray.intersectObjects( collidableMeshList );
        if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ) 
        {
            // a collision occurred... do something...
        }
    }
    

    There are two potential problems with this particular approach.

    (1) When the origin of the ray is within a mesh M, no collision results between the ray and M will be returned.

    (2) It is possible for an object that is small (in relation to the Player mesh) to "slip" between the various rays and thus no collision will be registered. Two possible approaches to reduce the chances of this problem are to write code so that the small objects create the rays and do the collision detection effort from their perspective, or include more vertices on the mesh (e.g. using CubeGeometry(100, 100, 100, 20, 20, 20) rather than CubeGeometry(100, 100, 100, 1, 1, 1).) The latter approach will probably cause a performance hit, so I recommend using it sparingly.

    I hope that others will contribute to this question with their solutions to this question. I struggled with it for quite a while myself before developing the solution described here.

    0 讨论(0)
  • 2020-12-01 00:17

    only works on BoxGeometry and BoxBufferGeometry

    create the following function:

    function checkTouching(a, d) {
      let b1 = a.position.y - a.geometry.parameters.height / 2;
      let t1 = a.position.y + a.geometry.parameters.height / 2;
      let r1 = a.position.x + a.geometry.parameters.width / 2;
      let l1 = a.position.x - a.geometry.parameters.width / 2;
      let f1 = a.position.z - a.geometry.parameters.depth / 2;
      let B1 = a.position.z + a.geometry.parameters.depth / 2;
      let b2 = d.position.y - d.geometry.parameters.height / 2;
      let t2 = d.position.y + d.geometry.parameters.height / 2;
      let r2 = d.position.x + d.geometry.parameters.width / 2;
      let l2 = d.position.x - d.geometry.parameters.width / 2;
      let f2 = d.position.z - d.geometry.parameters.depth / 2;
      let B2 = d.position.z + d.geometry.parameters.depth / 2;
      if (t1 < b2 || r1 < l2 || b1 > t2 || l1 > r2 || f1 > B2 || B1 < f2) {
        return false;
      }
      return true;
    }
    

    use it in conditional statements like this:

    if (checkTouching(cube1,cube2)) {
    alert("collision!")
    }
    

    I have an example using this at https://3d-collion-test.glitch.me/

    Note: if you rotate(or scale) one (or both) of the cubes/prisims, it will detect as though they haven't been turned(or scaled)

    0 讨论(0)
提交回复
热议问题