Collision I - Rays

As of version 0.4, only ray-based collision is supported by the library. The next version is planned to support box and sphere collision testing.

The basis of all collision detection with a BSP level is in the function Q3BSPLevel.Trace():

public Q3BSPCollisionData Trace(Vector3 startPosition, Vector3 endPosition);
When called, this function will look through the BSP tree and see if the line between startPosition and endPosition passes through any solid geometry. Regardless of the result of a collision test, the function returns a filled out Q3BSPCollisionData structure:

public struct Q3BSPCollisionData
    public float ratio; // Normalized between 0 and 1, measure of where in between the points collision occured.
    public Vector3 collisionPoint; // The point of collision between the two input points. If there is no collision this is the same as the endpoint.
    public bool startOutside; // True if the start position of the test is outside of any brush.
    public bool inSolid; // True if the entire line segment is contained within level geometry. 
    public Vector3 startPosition; // The input startPosition of the line segment
    public Vector3 endPosition; // The input endPosition of the line segment
The simplest way to implement this collision detection is to apply it to our game's camera. To do so, we will have to buffer it's movement. In the Update() method of the completed Basic Tutorial, change the camera control code to this:

Vector3 cameraPositionChangeThisFrame = Vector3.Zero;

if (keyboardState.IsKeyDown(Keys.W))
    cameraPositionChangeThisFrame -= speed * (float)gameTime.ElapsedGameTime.TotalSeconds * cameraForward;
if (keyboardState.IsKeyDown(Keys.S))
    cameraPositionChangeThisFrame += speed * (float)gameTime.ElapsedGameTime.TotalSeconds * cameraForward;

if (keyboardState.IsKeyDown(Keys.A))
    cameraPositionChangeThisFrame -= speed * (float)gameTime.ElapsedGameTime.TotalSeconds * cameraLeft;
if (keyboardState.IsKeyDown(Keys.D))
    cameraPositionChangeThisFrame += speed * (float)gameTime.ElapsedGameTime.TotalSeconds * cameraLeft;

if (keyboardState.IsKeyDown(Keys.LeftShift))
    cameraPositionChangeThisFrame.Y += speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
if (keyboardState.IsKeyDown(Keys.LeftControl))
    cameraPositionChangeThisFrame.Y -= speed * (float)gameTime.ElapsedGameTime.TotalSeconds;

After doing this we will then make a call to the Trace function, and using the collision point, set the new position of our camera.

Q3BSPCollisionData collision = level.Trace(cameraPosition, cameraPosition + cameraPositionChangeThisFrame);
cameraPosition = collision.collisionPoint;
Because the Q3BSPCollisionData's collision point is either the endpoint of the trace or, if there is actually collision, the position of the collision, it should be safe to set our camera to that position.

A caveat of this ray-based collision testing is that our camera, when as close to a wall as possible, is now nearly co-planar with the wall, allowing the eye to see past it. Sphere or box collision would be a better fit for this situation.

Last edited Feb 8, 2009 at 7:34 AM by MasterSlowPoke, version 2


No comments yet.