In the render loop of my first-person game, I call a function to detect collisions and move the player/camera. This function uses a CubeGeometry called pCube
, which is constantly updated to match the camera's position:
// Function for player movements
function pMovements() {
mPlayer.colBottom = false;
pCube.position.x = mPlayer.yawObject.position.x + 50; // The cube is offset by +50 for visibility/debugging.
pCube.position.y = mPlayer.yawObject.position.y - 10;
pCube.position.z = mPlayer.yawObject.position.z;
// -- COLLISION DETECTION START --
var originPoint = pCube.position.clone();
for (var vertexIndex = 0; vertexIndex < pCube.geometry.vertices.length; vertexIndex++)
{
var localVertex = pCube.geometry.vertices[vertexIndex].clone();
var globalVertex = localVertex.applyMatrix4( pCube.matrix );
var directionVector = globalVertex.sub( pCube.position );
var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() );
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ) {
// Detect collisions with bottom vertices
if (vertexIndex == 2 || vertexIndex == 3 || vertexIndex == 6 || vertexIndex == 7) {
mPlayer.colBottom = true;
mPlayer.velocity.y = Math.max( 0, mPlayer.velocity.y ); // Stop falling
}
}
}
// -- COLLISION DETECTION END --
var delta = (Date.now() - time) * 0.1;
mPlayer.velocity.x += (-mPlayer.velocity.x) * 0.08 * delta; // Movement in x-axis
mPlayer.velocity.z += (-mPlayer.velocity.z) * 0.08 * delta; // Movement in z-axis
if (mPlayer.colBottom == false) {
mPlayer.velocity.y -= 0.1 * delta; // Falling
}
if (mPlayer.moveForward) mPlayer.velocity.z -= mPlayer.speed * delta;
if (mPlayer.moveBack) mPlayer.velocity.z += mPlayer.speed * delta;
if (mPlayer.moveLeft) mPlayer.velocity.x -= mPlayer.speed * delta;
if (mPlayer.moveRight) mPlayer.velocity.x += mPlayer.speed * delta;
mPlayer.yawObject.translateX(mPlayer.velocity.x);
mPlayer.yawObject.translateY(mPlayer.velocity.y);
mPlayer.yawObject.translateZ(mPlayer.velocity.z);
if (mPlayer.yawObject.position.y < -2000) {
// Reset player's position if fallen out of bounds
mPlayer.velocity.y = 0;
mPlayer.yawObject.position.y = 100;
mPlayer.yawObject.position.x = 0;
mPlayer.yawObject.position.z = 0;
mPlayer.yawObject.rotation.y = 0;
mPlayer.pitchObject.rotation.x = 0;
}
if (mPlayer.moveDown) {
mPlayer.yawObject.position.y -= 1;
}
if (mPlayer.moveUp) {
mPlayer.yawObject.position.y += 1;
}
}
Check out the demo here.
Use WASD keys to move and Space bar to jump. Discover that the black cube/rectangle mirrors the camera's position with an offset of +50 on the x-axis. Collisions are detected based on this cube geometry.
Two questions arise from this setup. Should collision detection be based on the vertices or faces of the cube? If an object is smaller than the cube, collisions may not be detected as it won't hit any vertices. Would it be better to rewrite it to detect collisions based on faces instead?
Secondly, how can the issue of the cube falling too far down when a collision is detected be addressed? In the demo, the cube continues to fall for some time after a collision, possibly due to mPlayer.velocity.y
. Despite attempts to fix it, even jumping causes the cube to sink too deeply into the floor. Any suggestions on how to prevent this behavior?