2
\$\begingroup\$

I am trying to learn about the Camera in a 3D space and wanted to 3rd person type of camera. Right now, I directly control my camera and it doesn't follow anything. It works like a first-person camera. I load my Model in my Game1.cs and draw it like this:

Model player;

protected override void LoadContent()
{
    // This is the default cube from Blender (when you first open up the program)
    player = this.Content.Load<Model>("Cube");
}

protected override void Draw(GameTime gameTime)
{
    foreach (ModelMesh mesh in player.Meshes)
    {
        foreach (BasicEffect effect in mesh.Effects)
        {
            effect.View = camera.View;
            effect.World = Matrix.CreateTranslation(5f, 1, 10f);
            effect.Projection = camera.Projection;

            mesh.Draw();
        }
    }

    base.Draw(gameTime);
}

I want my camera to behind the cube or player and follow it. I think I have to change PreviewMove method in my Camera class but I don't know what needs to be changed. My guess is that I have to make a world matrix for the camera and set it behind the cube and update it whenever I move the cube. From there I would move the movement (W,S,A,D) code to the player movement.

But like I said, I don't know how I would implement this. If someone can provide any help or link me to a tutorial, it would be greatly appreciated. Thanks for your time!

Camera Class:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Model3D
{
    class Camera
    {
        // Attributes
        private Vector3 camLookAt;
        private Vector3 camPosition;
        private Vector3 camRotation;
        private float cameraSpeed;
        private float mouseSensitivity;
        private Vector3 mouseRotationBuffer;
        private MouseState currentMouseState;
        private MouseState previousMouseState;
        private GraphicsDevice device;

        // Properties
        public Matrix Projection
        {
            get;
            protected set;
        }

        public Matrix View
        {
            get { return Matrix.CreateLookAt(camPosition, camLookAt, Vector3.Up); }
        }

        public Vector3 Position
        {
            get { return camPosition; }
            set
            {
                camPosition = value;
                UpdateLookAt();
            }
        }

        public Vector3 Rotation
        {
            get { return camRotation; }
            set
            {
                camRotation = value;
                UpdateLookAt();
            }
        }

        // Constructor
        public Camera(GraphicsDevice device, Vector3 position, Vector3 rotation, float fieldOfView = 45f, float speed = 10f, float sensitivity = 10f)
        {
            // Set the GraphicsDevice
            this.device = device;

            // Set camera speed & mouse sensitivity
            cameraSpeed = speed;
            mouseSensitivity = sensitivity;

            // Setup projection matrix
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(fieldOfView), device.DisplayMode.AspectRatio, 0.5f, 1000f);

            // Set camera position and rotation
            MoveTo(position, rotation);
        }

        // Set camera's position & rotation
        private void MoveTo(Vector3 pos, Vector3 rot)
        {
            Position = pos;
            Rotation = rot;
        }

        // Update LookAt Vector
        private void UpdateLookAt()
        {
            // Build a rotation matrix
            Matrix rotationMatrix = Matrix.CreateRotationX(camRotation.X) * Matrix.CreateRotationY(camRotation.Y);

            // Build LookAt offset
            Vector3 lookAtOffset = Vector3.Transform(Vector3.UnitZ, rotationMatrix);

            // Update camera's LookAt vector
            camLookAt = camPosition + lookAtOffset; 
        }

        // Simulate movement
        private Vector3 PreviewMove(Vector3 amount)
        {
            // Create a rotate matrix
            Matrix rotate = Matrix.CreateRotationY(camRotation.Y);

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

            // Return value of camera position + movement vector
            return camPosition + movement;
        }

        // Method that actually moves the camera
        private void MoveCamera(Vector3 scale)
        {
            MoveTo(PreviewMove(scale), Rotation);
        }

        // Keyboard and Mouse movement
        public void Update(GameTime gameTime)
        {
            // For smoothing (might not be beneficial for FPS)
            float deltaTime = (float) gameTime.ElapsedGameTime.TotalSeconds;

            // Mouse attributes
            float deltaX;
            float deltaY;

            // Mouse State & Keyboard State
            currentMouseState = Mouse.GetState();
            KeyboardState state = Keyboard.GetState();

            // Handle basic movement
            Vector3 moveVector = Vector3.Zero;

            if (state.IsKeyDown(Keys.W))
                moveVector.Z = 1;
            if (state.IsKeyDown(Keys.S))
                moveVector.Z = -1;
            if (state.IsKeyDown(Keys.A))
                moveVector.X = 1;
            if (state.IsKeyDown(Keys.D))
                moveVector.X = -1;
            if (state.IsKeyDown(Keys.Space))
                moveVector.Y = 1;
            if (state.IsKeyUp(Keys.Space) && Position.Y > 1.07)
                moveVector.Y = -1;

            if (moveVector != Vector3.Zero)
            {
                // Normalize vector so that we don't move faster diagonally
                // Turns it into a unit vector
                moveVector.Normalize();

                // Now we add in smooth factor and speed
                moveVector *= deltaTime * cameraSpeed;

                // Move camera
                MoveCamera(moveVector);
            }

            // Handle mouse movement
            if(currentMouseState != previousMouseState)
            {
                // Cache mouse location
                deltaX = currentMouseState.X - (device.Viewport.Width / 2);
                deltaY = currentMouseState.Y - (device.Viewport.Height / 2);

                // Create the rotation
                mouseRotationBuffer.X -= 0.01f * deltaX * mouseSensitivity;
                mouseRotationBuffer.Y -= 0.01f * deltaY * mouseSensitivity;

                // Clamp the rotation so it can't do a barrel roll
                if(mouseRotationBuffer.Y < MathHelper.ToRadians(-75.0f))
                    mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(-75.0f));
                if (mouseRotationBuffer.Y > MathHelper.ToRadians(75.0f))
                    mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(75.0f));

                Rotation = new Vector3(-MathHelper.Clamp(mouseRotationBuffer.Y, MathHelper.ToRadians(-75.0f), MathHelper.ToRadians(75.0f)), 
                    MathHelper.WrapAngle(mouseRotationBuffer.X), 0);
            }

            // Center the mouse
            Mouse.SetPosition(device.Viewport.Width / 2, device.Viewport.Height / 2);

            // Save previous mouse state
            previousMouseState = currentMouseState;
        }
    }
}
\$\endgroup\$
1
\$\begingroup\$

Conceptually, in a third person camera game, all your movement and rotation algorithms would be in your player class (Update method) as you use WASD and mouse to move and rotate your player around, not the camera. This new position and rotation of the player is stored in a matrix which has 2 jobs: 1. it will be used during drawing of the player as effect.World. and 2. sent to the camera's Update method to locate and orient the 3rd person camera.

Once the player's matrix is established, It gets passed to the Camera Update which uses it to create a new 1st person view matrix as follows:

void Update(Matrix playerWorldMatrix)
{


   Vector3 cameraPosition = playerWorldMatrix.Translation + (playerWorldMatrix.Backwards * cameraTrailingDistance);
   Vector3 cameraLookAt = playerWorldMatrix.Translation + (playerWorldMatrix.Forward * cameraLookAheadDistance);

   view = Matrix.CreateLookAt(cameraPosition, cameraLookAt, Vector3.Up);

}

The camera view matrix is that simple. But it can be adjusted to perfection also. It is easy to place vertical or horizontal offsets too so the camera is placed, say, over the right shoulder of the player looking forward:

Vector3 cameraPosition = playerWorldMatrix.Translation + (playerWorldMatrix.Backwards * cameraTrailingDistance) +
                                                         (playerWorldMatrix.Right * cameraRightLeftOffset) +
                                                         (playerWorldMatrix.Up * cameraVerticalOffset);
\$\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.