# 3D XNA Tracking Camera

I'm trying to implement a camera that tracks a 3D model in my game. I've taken a look at this MSDN article on creating a 3rd person camera, as well as experimenting on my own; but I don't get the correct result.

My camera follows behind my model correctly, until I rotate the model about the y-axis. In this case, the camera seems to fly off to one side or the other.

What I want, is when the model is rotated to the left the camera will remain behind it; so as too mimic the view that can been seen in the left image (minus the fact that the rest of the world will display the rotation). I have tried looking around for tracking camera implementations, but to no avail.

Here is the code I use to calculate the position and look-at vectors of the camera:

// Calculate the offset vector
Vector3 offset = new Vector3(0.0f, this.DistanceAboveTarget, -this.DistanceBehindTarget);

// Transform the offset by the target's rotation
float targetRotation = mTarget.Rotation.Y;
Matrix rotationMatrix = Matrix.CreateRotationY(targetRotation);

Vector3 transformedOffset = Vector3.Transform(offset, rotationMatrix);

// Calculate the new position and look at vectors
Vector3 targetPosition = mTarget.Position;
mLookAt = targetPosition;

mPosition = targetPosition + transformedOffset;


EDIT

All other matrices are calculate elsewhere in my code and are correct, so far as I understand. Full code samples can be found in the below links:

Attempt #1

I couldn't find anything wrong with your camera class, but I found a few anomalies in your rendering code.

As far as I could tell, your calculations inside the camera class are correct. Further more, even if you were calculating the camera position wrong, since the lookAtparameter is set to match the character's position, I think the model should still appear centered in view no matter what, which is not the case here.

So I looked into your GameModel class and noticed some strange things. In particular these lines:

effect.World = mCamera.World;
effect.View = boneTransforms[mesh.ParentBone.Index] * modelViewMatrix;


You should almost certainly replace this with:

effect.World = boneTransforms[mesh.ParentBone.Index] * this.Transform;
effect.View = mCamera.View;


I'm unsure if this will solve the problem, since matrix multiplication is associative and it seems the resulting multiplication order will end up being the same as before, but give it a try. It doesn't really make sense to have a world matrix on your camera class anyway.

Attempt #2

Wait a minute, you're calculating the player position like this:

public Vector3 Position
{
get { return mCenter + mTranslation; }
}


That extra mCenter might be the reason your camera is looking at the wrong spot. Your model should usually already be defined centered around local space, so there should be no need to add mCenter to the position.

And in case your model isn't centered, this still wouldn't be the correct way to do it. Let me know if that's the case and I'll guide you through the process. Basically you would need to calculate your world matrix a bit differently in order to account for an Origin, but that offset should happen before rotation, not together with translation. Something like:

Matrix transform = Matrix.CreateTranslation(-mCenter) *
Matrix.CreateScale(mScale) *
Matrix.CreateFromYawPitchRoll(mRotation.Y, mRotation.X, mRotation.Z) *
Matrix.CreateTranslation(mPosition);


And once again that's only if the model wasn't already centered when it was created.

• Unfortunately, that made no difference that I could see. But thanks for the tip about the World matrix usage, though. – Samuel Slade Mar 5 '12 at 17:33
• @SamuelSlade Check my edit. – David Gouveia Mar 5 '12 at 17:39
• Sorted! I actually thought the value of mCenter was coming back as zero, but that must have been a different model. Cheers! – Samuel Slade Mar 5 '12 at 19:42

The code you have gets the position of the camera but doesn't set the direction it's pointing. You didn't create the view or projection matrices yet (at least, not in your example code). Perhaps this is the problem. See the next steps in the MSDN article.

Here's the code (from the next two steps) that creates the view and projection matrices:

view = Matrix.CreateLookAt(cameraPosition, avatarPosition, new Vector3(0.0f, 1.0f, 0.0f));
Viewport viewport = graphics.GraphicsDevice.Viewport;
float aspectRatio = (float)viewport.Width / (float)viewport.Height;

proj = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio,
nearClip, farClip);


Then, on the final step, you'll see that these get passed into the BasicEffect instance.

• I should probably have mentioned that the other matrices are calculated elsewhere and are correct. I will update my question to highlight this. – Samuel Slade Mar 5 '12 at 15:46

I'll break it down for you quickly. What you want to do is take the current MouseState and pass MouseState and use them to calculate the values you want to rotate around the X and Y axis by. Then create a rotation matrix by doing:

Matrix rotationMatrix = Matrix.CreateRotationY(MathHelper.ToRadians(rotation)) * Matrix.CreateRotationY(MathHelper.ToRadians(rotation));


Then transform your third person reference (Vector3 of the distance the camera is from player) by the new rotationMatrix.

Then change the camera position to the new transformed third person reference + your player position.

Lastly, change the viewMatrix to: viewMatrix = Matrix.CreateLookAt(camera position, player position, Vector3.up);.

That was just off the top of my head, but that's the gist of it.

• ...which is pretty much what he was already doing. His matrix math is okay, the problem was with the player position having the wrong value. – David Gouveia Mar 5 '12 at 20:49