0
\$\begingroup\$

I'm building a fairly basic 3D first-person puzzle game. The only objects for which collisions matter are walls, and all walls are at convenient 90 degree angles aligned with the coordinate axes.

Unfortunately, I cannot consistently prevent the camera from moving through walls. There is no off-the-shelf game engine or framework involved, so I have to implement all this from scratch.

Currently, my algorithm (in GLSL-ish psuedo code) looks something like this:

void update_position(seconds){
    vec3 ray = Raycast(position, speed, map.grid);
    float xmax = max(ray.x-.01,0);
    float ymax = max(ray.y-.01,0);
    float zmax = max(ray.z-.01,0);

    float dx = speed.x * seconds;
    float dy = speed.y * seconds;
    float dz = speed.z * seconds;

    if(abs(dx) > xmax){
        dx = (dx > 0 ? xmax : -xmax);
        speed.x = 0;
    }
    if(abs(dy) > ymax){
        dy = (dy > 0 ? ymax : -ymax);
        speed.y = 0;
    }
    if(abs(dz) > zmax){
        dz = (dz > 0 ? zmax : -zmax);
        speed.z = 0;
    }

    position.x += dx;
    position.y += dy;
    position.z += dz;
}

In prose: I cast a ray from the player's/camera's current position along the speed vector, which extends until it hits wall. (The Raycast function is used elsewhere, so I already know it works correctly.) I then define the maximum distance the player is permitted to move in each dimension to be slightly less than the component of the ray in that dimension (so that we never end up exactly on a wall). I then compute a provisional set of deltas to add to the current position based on the speed and the time since the last frame (in fractional seconds), and check to see if any of them exceed the maximum allowed magnitude which would take the player through the wall. If they do, I clamp the magnitude of the offending component to its max value, and set the component of speed in that direction to 0; so, the player should end up just barely outside the wall, and sliding along it.

This usually works pretty well if you happen to run straight into a wall, but it fails frequently but unpredictably (as in, sometimes it doesn't fail) near corners, and especially near three-way corners (where two walls and a ceiling or floor come together).

So, what is wrong with this algorithm, and how do I fix it?

\$\endgroup\$
  • \$\begingroup\$ Use standard collision detection treating your camera as a small sphere \$\endgroup\$ – Allahjane Dec 20 '16 at 4:11
  • \$\begingroup\$ Can you be more specific than "standard collision detection"? I don't have an engine here that I can tell "my camera is a small sphere, please handle collisions". Is there a standard reference for this I should know about that will tel me how to roll it from scratch? \$\endgroup\$ – Logan R. Kearsley Dec 20 '16 at 4:48
  • \$\begingroup\$ For my turn based strategy game I let the players move around with a first person 3D camera. I don't try to prevent them from going through walls, and think of it more like an omnipotence feature. What I'm trying to say is, ensure your game actually needs a camera like what you're intending. If anything I will just be clamping the depth Y value to prevent them from going under the level. \$\endgroup\$ – Krythic Dec 21 '16 at 16:46
1
\$\begingroup\$

You could use planes for the walls and only update the camera if the camera sphere and potential new postion do not intersect the planes.

A basic sphere plane intersection would look something like:

bool intersecting_or_behind_plane = (dot(position, plane_normal) - plane_d) <= sphere_radius

Collsion detection is quite a large topic, but searching for things like geometry intersection should help.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.