1
\$\begingroup\$

I'm trying to create a third person camera in my XNA game. I'm able to move the model I have and the camera at the same time, although the camera is not "following" the model. The model moves faster than the camera. Ultimately, I want the camera to rotate with the model with the left and right keys, and move the model with forward and back.

This is in my update method

    if (keyState.IsKeyDown(Keys.D))
        {
            camera.Rotation = MathHelper.WrapAngle(camera.Rotation - (rotateScale * elapsed));
        }

        if (keyState.IsKeyDown(Keys.A))
        {
            camera.Rotation = MathHelper.WrapAngle(camera.Rotation + (rotateScale * elapsed));
        }

        if (keyState.IsKeyDown(Keys.W))
        {
            camera.MoveForward(moveScale * elapsed);
            player.Position += new Vector3(0.1f, 0f, 0f);
        }

        if (keyState.IsKeyDown(Keys.S))
        {
            camera.MoveForward(-moveScale * elapsed);
            player.Position += new Vector3(-0.1f, 0f, 0f);
        }

player is the model I'm using. Currently the A and D keys don't do anything with the model.

    public class Camera
{
    private Vector3 position = Vector3.Zero;
    private Vector3 lookAt;
    private Vector3 baseCameraReference = new Vector3(0, 0, 1);

    private bool needViewResync = true;

    private float rotation;

    public Matrix Projection { get; protected set; }
    private Matrix cachedViewMatrix;

    public Vector3 Position
    {
        get
        {
            return position;
        }

        set
        {
            position = value;
            UpdateLookAt();
        }
    }

    public float Rotation
    {
        get
        {
            return rotation;
        }

        set
        {
            rotation = value;
            UpdateLookAt();
        }
    }

    public Matrix View
    {
        get
        {
            if (needViewResync)
                cachedViewMatrix = Matrix.CreateLookAt(Position, lookAt, Vector3.Up);

            return cachedViewMatrix;
        }
    }

    public Camera(Vector3 position, float rotation, float aspectRatio, float nearClip, float farClip)
    {
        Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio, nearClip, farClip);
        MoveTo(position, rotation);


    }

    private void UpdateLookAt()
    {
        Matrix rotationMatrix = Matrix.CreateRotationY(rotation);

        Vector3 lookAtOffset = Vector3.Transform(baseCameraReference, rotationMatrix);
        lookAt = position + lookAtOffset;
        needViewResync = true;
    }

    private void MoveTo(Vector3 position, float rotation)
    {
        this.position = position;
        this.rotation = rotation;
        UpdateLookAt();
    }

    public Vector3 PreviewMove(float scale)
    {
        Matrix rotate = Matrix.CreateRotationY(rotation);
        Vector3 forward = new Vector3(0, 0, scale);
        forward = Vector3.Transform(forward, rotate);
        return (position + forward);
    }

    public void MoveForward(float scale)
    {
        MoveTo(PreviewMove(scale), rotation);
    }
}

I also want to offset the camera for obvious reason, but changing the values in the variable baseCameraReference doesn't change what I want it to

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

If I understand your requirements, you want your camera to always be behind your player (but maybe offset slightly) and move and rotate with the player. The camera should stay in the same location and orientation (rotation) relative to the player.

Then all the rotation and movement code for updating your camera can be eliminated, it is just doing the same movement and rotation as you want the player to do anyway. Why do it twice? Spend all your coding effort moving and rotating the player only.

After that, simply send the updated players matrix to the camera update method after the player is moved and rotated and use the positional and rotational info from that player's matrix to build the camera.

Public void CameraUpdate(Matrix playerMatrix)
{
  camPosition = playerMatrix.Translation + (playerMatrix.Backward * trailingDistance);
  camTarget = playerMatrix.Translation;

  view = Matrix.CreateLookAt(camPosition, camTarget, Vector3.Up); 
}

That's all you need to update the camera. If you want to offset the camera's position (say, to position it over the player's shoulder or something), you can add more offsets when setting the position, like this:

camPosition = playerMatrix.Translation +
                (playerMatrix.Backward * trailingDistance) +
                (playerMatrix.Up  * verticalOffset) +
                (playerMatrix.Left * lateralOffset);

same with the target if you want it to seem as if it's looking past the player instead of at him:

camTarget = playerMatrix.Translation + (playerMatrix.Up * someOffset);
\$\endgroup\$
3
  • \$\begingroup\$ How would I implement this into my update method and by using the keys? \$\endgroup\$ Jun 8, 2013 at 22:46
  • \$\begingroup\$ I also don't have a player matrix, just a player.Position which is a Vector3 \$\endgroup\$ Jun 8, 2013 at 22:53
  • \$\begingroup\$ You must have a matrix for the player. When you draw the player, you set the effect.World to a matrix that represents the player's current position and rotation. that is the matrix I am referring to. Sequence of events: 1.) update player by responding to the input (gamepad or keyboard). 2.) set the player's matrix at this time (same as you would if drawing player). 3.)pass this matrix to the camera and use it to build view matrix in a manner similar to what I've suggested. 4.) pass this same matrix to the effect.World later when drawing the player (don't need to calculate it again). \$\endgroup\$
    – Steve H
    Jun 8, 2013 at 23:10
0
\$\begingroup\$

This is the part of my player class that I'm looking at

    public void Render(Matrix view, Matrix projection)
        {
            foreach (ModelMesh mesh in mymodel.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    //effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index]
                        * Matrix.CreateScale(Scale)
                        * Matrix.CreateRotationY(MathHelper.ToRadians(RotationY))
                        * Matrix.CreateRotationX(MathHelper.ToRadians(RotationX))
                        * Matrix.CreateTranslation(Position);

                    effect.View = view;
                    effect.Projection = projection;
                }
                mesh.Draw();
            }
        }

What you're saying is to use the matrix "transforms[]" while passing in the camera method?

    camera.CameraUpdate(player.transforms)

and then update that matrix based on input?

\$\endgroup\$
7
  • \$\begingroup\$ here is a basic working example of the 3 classes and how they interact: game class: pastebin.com/juZFmef9 player class: pastebin.com/avwSA2ec camera class: pastebin.com/NFduExyw \$\endgroup\$
    – Steve H
    Jun 9, 2013 at 1:16
  • \$\begingroup\$ the hard coded offset values were appropriate to the model I was using at the time, your values would most likely be different. set to taste. \$\endgroup\$
    – Steve H
    Jun 9, 2013 at 1:24
  • \$\begingroup\$ Everthing compiles fine but the input doesn't change anything \$\endgroup\$ Jun 9, 2013 at 1:59
  • \$\begingroup\$ it sounds like you may need 1 on 1 help. feel free to email me. see my profile for address. \$\endgroup\$
    – Steve H
    Jun 9, 2013 at 2:03
  • \$\begingroup\$ Alright I sent you my project \$\endgroup\$ Jun 9, 2013 at 2:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .