Search code examples
c#visual-studio-20153dxnacollision-detection

Can't seem to get smooth 3D collision XNA


I am making a 3D game. The player is the "camera". I want it not to go through walls which is achieved. But now I want it to be able to "glide" along the wall as in any other fps. Here is the code: and thanks in advance:

protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();
        float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;

        keyState = Keyboard.GetState();

        camera.Update(gameTime);

        if (keyState.IsKeyDown(Keys.W)) camera.moveVector.Z = 1;
        if (keyState.IsKeyDown(Keys.S)) camera.moveVector.Z = -1;
        if (keyState.IsKeyDown(Keys.A)) camera.moveVector.X = 1;
        if (keyState.IsKeyDown(Keys.D)) camera.moveVector.X = -1;
        if (keyState.IsKeyDown(Keys.Space)&&camera.Position.Y>=0.5f) camera.moveVector.Y = 0.5f;

        if (camera.moveVector != Vector3.Zero)
        {
            //We don't want to make the player move faster when it is going diagonally.
            camera.moveVector.Normalize();
            //Now we add the smoothing factor and speed factor
            camera.moveVector *= (dt * camera.cameraSpeed);


            Vector3 newPosition = camera.PreviewMove(camera.moveVector);

            bool moveTrue = true;


            if (newPosition.X < 0 || newPosition.X > Map.mazeWidth) moveTrue = false;
            if (newPosition.Z < 0 || newPosition.Z > Map.mazeHeight) moveTrue = false;
            foreach (BoundingBox boxes in map.GetBoundsForCell((int)newPosition.X, (int)newPosition.Z))
            {
                if (boxes.Contains(newPosition) == ContainmentType.Contains)
                {
                    moveTrue = false;
                }
            }

            if (moveTrue) camera.Move(camera.moveVector);


            base.Update(gameTime);

And here is the code for excecuting the movement:

        //Updating the look at vector
        public void UpdateLookAt()
        {
            //Built a rotation matrix to rotate the direction we are looking
            Matrix rotationMatrix = Matrix.CreateRotationX(cameraRotation.X) * Matrix.CreateRotationY(cameraRotation.Y);

        // Build a look at offset vector 
        Vector3 lookAtOffset = Vector3.Transform(Vector3.UnitZ, rotationMatrix);

        //Update our camera's look at the vector
        cameraLookAt = (cameraPosition + lookAtOffset);

    }


    //Method to create movement and to check if it can move:)
    public Vector3 PreviewMove(Vector3 amount)
    {
        //Create a rotation matrix to move the camera
        Matrix rotate = Matrix.CreateRotationY(cameraRotation.Y);

        //Create the vector for movement
        Vector3 movement = new Vector3(amount.X, amount.Y, amount.Z);
        movement = Vector3.Transform(movement, rotate);

        // Give the value of the camera position +ze movement
        return (cameraPosition+movement);
    }

    //Method that moves the camera when it hasnt'collided with anything
    public void Move(Vector3 scale)
    {
        //Moveto the location
        MoveTo(PreviewMove(scale), Rotation);
    }

Already thought of using the invert method given by xna. But I can't seem to find the normal. And I have tried to move the camera parallel to the wall. But I was unable to achieve that. Any help is appreicated.


Solution

  • If you have found that a point intersects a bounding box, you have to check which of the six faces the entry point lies in. This can be done as follows: Construct a line segment between the old camera position and the new one:

    p = (1 - t) * oldPos + t * newPos
    

    , where you use only the dimension of oldPos and newPos, which is interesting for the face (e.g. for the left/right face, take the x-coordinate). p is the according coordinate for the face. Calculate t for every face and find the face for which t is maximal, ignoring faces behind which the point already lies (i.e. the dot product of the face normal and the direction from the face to the point is negative). This will be the face, in which your entry point lies. All you then need to do is adapt the relevant coordinate (again the x-coordinate for left/right face etc.), such that it does not lie within the bounds (e.g. set newPos.x = boundingBox.MaxX for the right face). This is equal to a projection of the point onto the bounding box surface and equivalent to using only the component of the movement vector that is parallel to the box if an intersection would occur).

    Btw, the solution of the above formula is:

    t = (p - oldPos) / (newPos - oldPos)