2
\$\begingroup\$

I'm making a game using THREE.JS, and I want my character to walk on the terrain, and collide with static 3D objects (=AABB boxes) that are on that terrain.

The problem is: THREE.JS has only a raycasting class which can be used for collisions.

I figured it would be possible if I casted 8 rays from my character to the points of an imaginary cube around the character (simulating an AABB box). I realize a sphere would be much better, but I wouldn't know how to do that using rays.

var rayDirections = [];
rayDirections.push(new THREE.Vector3(1, 1, 1));
rayDirections.push(new THREE.Vector3(-1, 1, 1));
rayDirections.push(new THREE.Vector3(1, 1, -1));
rayDirections.push(new THREE.Vector3(-1, 1, -1));            
rayDirections.push(new THREE.Vector3(1, -1, 1));
rayDirections.push(new THREE.Vector3(-1, -1, 1));
rayDirections.push(new THREE.Vector3(1, -1, -1));
rayDirections.push(new THREE.Vector3(-1, -1, -1));   

Then, I simply test the rays against the terrain and possible 3D meshes on the terrain, and check if the ray distance from one of the raycasts is smaller than the AABB box size. This would mean a collision occured.

Rays around the character http://www.ironbane.com/images/colbox.png.

So far so good! I cast a ray from the character's position with every rayDirection and check if one collides with the meshes array (which contains the terrain and other 3D objects on the terrain)

Once I have a collision, what to do now? Should I calculate a reflection vector? If for instance I simply walk on a flat terrain, the four rays at the bottom should trigger a collision. How do I use this reflection vector in my situation?

for(var r in rayDirections) {
    // this.position refers to the character's current position
    var ray = new THREE.Ray( this.position, rayDirections[r] );


    for (var m in meshes) {
        var intersects = ray.intersectObject( meshes[m] );

        if ( intersects.length > 0 ) {
            // Normal vector of the meshes' face we hit
            var nV = intersects[0].face.normal;

            // Velocity vector (of the character)
            var vV = this.velocity.clone();

            // And calculate the reflection vector
            var rV = n.multiplyScalar(-2 * vV.dot(nV)).addSelf(vV);;

            // ... Now what?
        }
    }
}
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

What shape are you modelling your character as? An axis-aligned cylinder? (which is typical)

Personally, I'd just move the character without worrying about collisions, then check if the character is "inside" any obstacle, and move him to the nearest non-intersecting location. This is pretty trivial in most cases.

In the case of an AABB and a cylindrical character, it's only necessary to check a collision with certain points (depending on how accurate you want it to be and what kind of obstacles you'll have). Maybe you just need to check:

  • If any corner of an AABB obstacle is inside the player (if so, move player to nearest non-intersecting point using linear interpolation)
  • If any axis-aligned edge of the cylinder is inside any AABB obstacle

In the case of the player standing on a platform, he is constantly falling slightly, and constantly being moved back to the top of the platform (the player doesn't notice this, as no rendering happens between these two events)

Just write down your use-cases. Example:

  1. Player does not fall through a platform he's standing on
  2. Player cannot walk through a wall orthogonally (at right-angles)
  3. Player approaching wall non-orthogonally, slides along it as they collide (e.g. if at 45 deg to wall, you'll still move along the wall at a lower speed than you're trying to go)
  4. Player cannot pass under a suspended obstacle which is too low for his head
  5. Player cannot become partially embedded in the corner of an obstacle (e.g. if approaching the corner off-centre)

I think you don't need to worry about raycasting. Just moving the player out of overlapping objects is sufficient.

This technique also works quite well for moving platforms (provided they don't move too fast), especially if you use Verlet integration.

\$\endgroup\$
1
\$\begingroup\$

Collision detection is just the very first part of a pretty complicated problem. You might find it's much easier to integrate functionality from an actual physics engine. I haven't used this one, but anything based on Box2D has a good chance of being pretty good: http://box2d-js.sourceforge.net/

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .