views:

78

answers:

3

I'm making a game using JigLib and GLGE. The camera follows the player the way I want it to, but when the player is close to a wall, the camera moves to the other side of the wall, which is annoying when there is something that needs quick reflexes.

I tried using picking to move the camera when it can't see the player, but it only moves until half of the wall is in front of it and half is behind it, making an annoying split screen effect where one half of the screen is blank.

camera.setLocY(Math.max(playerMesh.getLocY() - 10, 3));
camera.setRotY(playerHeading);
camera.setLocX(playerMesh.getLocX() + Math.sin(playerHeading) * Math.max(camera.getLocY() - playerMesh.getLocY() + 5, 0));
camera.setLocZ(playerMesh.getLocZ() + Math.cos(playerHeading) * Math.max(camera.getLocY() - playerMesh.getLocY() + 5, 0));
camera.setRotX(Math.atan2(playerMesh.getLocY() - camera.getLocY(), Math.max(camera.getLocY() - playerMesh.getLocY() + 5, 0)));

var playerVec = [playerMesh.getLocX(), playerMesh.getLocY(), playerMesh.getLocZ()],
    cameraVec = [camera.getLocX(), camera.getLocY(), camera.getLocZ()];
scene.removeChild(playerMesh);
scene.removeChild(floor);
do {
    var ray = scene.pick(canvas.width / 2, canvas.height / 2);
    if (ray.distance && ray.distance < GLGE.distanceVec3(playerVec, cameraVec)) {
        camera.setLocX(ray.coord[0]);
        camera.setLocY(ray.coord[1]);
        camera.setLocZ(ray.coord[2]);
        cameraVec = ray.coord;
        camera.setRotX(Math.atan2(playerMesh.getLocY() - camera.getLocY(), Math.max(camera.getLocY() - playerMesh.getLocY(), 0)));
    }
} while (ray.distance && ray.distance < GLGE.distanceVec3(playerVec, cameraVec));
scene.addChild(playerMesh);
scene.addChild(floor);

Is there a resource where I could find information about how to move the camera to avoid walls? Is my code wrong?

+3  A: 

You are just using the camera location in your tests.

You need to take into account the camera's field of view. At it's simplest you need to check more points at the extremes of the field of view cone as well as the actual location.

Another technique would be to make a camera sphere or cone and perform the collision detection with that. Obviously for most cases this would be too expensive, so only do this when the camera is within a certain distance to the walls.

ChrisF
Arriu
A: 

You not only need to prevent the camera from hitting the wall, but you must take account the clipping distance when deciding how far from the wall the camera should be.

When the camera is hugging the wall, and the clipping causes that wall to be clipped INSIDE the camera FOV, the result is that you will see that wall, AND something behind it. This bug was clearly visible on Tomb Raider 2 (in several places you could make the camera "see" the clipping of a wall, thus you would see about 3 or 4 pixels of whatever was behind the wall)

The game Sauerbraten (or Cube 2) is known for having a pretty good way to handling camera, and it is open source (so you can take a peek and know how they made it)

speeder
A: 

I found an easier solution - I placed several cameras and made some code to automatically switch to the closest one to the player.

function selectCamera(point) {
    var pointVec3 = convertToVec3(point);
    cameras.sort(function(a, b){
        return GLGE.distanceVec3(pointVec3, convertToVec3(a.getPosition())) - GLGE.distanceVec3(pointVec3, convertToVec3(b.getPosition()));
    });
    scene.setCamera(cameras[0]);

    // Now, point it in the right direction!
    var pos = scene.camera.getPosition(),
        coord = [pos.x-point.x, pos.y-point.y, pos.z-point.z];
    scene.camera.setRotOrder(GLGE.ROT_YZX);
    scene.camera.setRotX(Math.atan2(Math.sqrt(coord[2] * coord[2] + coord[0] * coord[0]), coord[1]) - Math.PI / 2);
    scene.camera.setRotY(Math.atan2(coord[0], coord[2]));
}
Ben L.