#region File Description //----------------------------------------------------------------------------- // ChaseCamera.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; #endregion namespace ChaseCameraSample { public class ChaseCamera { #region Chased object properties (set externally each frame) /// /// Position of object being chased. /// public Vector3 ChasePosition { get { return chasePosition; } set { chasePosition = value; } } private Vector3 chasePosition; /// /// Direction the chased object is facing. /// public Vector3 ChaseDirection { get { return chaseDirection; } set { chaseDirection = value; } } private Vector3 chaseDirection; /// /// Chased object's Up vector. /// public Vector3 Up { get { return up; } set { up = value; } } private Vector3 up = Vector3.Up; #endregion #region Desired camera positioning (set when creating camera or changing view) /// /// Desired camera position in the chased object's coordinate system. /// public Vector3 DesiredPositionOffset { get { return desiredPositionOffset; } set { desiredPositionOffset = value; } } private Vector3 desiredPositionOffset = new Vector3(0, 2.0f, 2.0f); /// /// Desired camera position in world space. /// public Vector3 DesiredPosition { get { // Ensure correct value even if update has not been called this frame UpdateWorldPositions(); return desiredPosition; } } private Vector3 desiredPosition; /// /// Look at point in the chased object's coordinate system. /// public Vector3 LookAtOffset { get { return lookAtOffset; } set { lookAtOffset = value; } } private Vector3 lookAtOffset = new Vector3(0, 2.8f, 0); /// /// Look at point in world space. /// public Vector3 LookAt { get { // Ensure correct value even if update has not been called this frame UpdateWorldPositions(); return lookAt; } } private Vector3 lookAt; #endregion #region Camera physics (typically set when creating camera) /// /// Physics coefficient which controls the influence of the camera's position /// over the spring force. The stiffer the spring, the closer it will stay to /// the chased object. /// public float Stiffness { get { return stiffness; } set { stiffness = value; } } private float stiffness = 1800.0f; /// /// Physics coefficient which approximates internal friction of the spring. /// Sufficient damping will prevent the spring from oscillating infinitely. /// public float Damping { get { return damping; } set { damping = value; } } private float damping = 600.0f; /// /// Mass of the camera body. Heaver objects require stiffer springs with less /// damping to move at the same rate as lighter objects. /// public float Mass { get { return mass; } set { mass = value; } } private float mass = 50.0f; #endregion #region Current camera properties (updated by camera physics) /// /// Position of camera in world space. /// public Vector3 Position { get { return position; } } private Vector3 position; /// /// Velocity of camera. /// public Vector3 Velocity { get { return velocity; } } private Vector3 velocity; #endregion #region Perspective properties /// /// Perspective aspect ratio. Default value should be overriden by application. /// public float AspectRatio { get { return aspectRatio; } set { aspectRatio = value; } } private float aspectRatio = 4.0f / 3.0f; /// /// Perspective field of view. /// public float FieldOfView { get { return fieldOfView; } set { fieldOfView = value; } } private float fieldOfView = MathHelper.ToRadians(45.0f); /// /// Distance to the near clipping plane. /// public float NearPlaneDistance { get { return nearPlaneDistance; } set { nearPlaneDistance = value; } } private float nearPlaneDistance = 1.0f; /// /// Distance to the far clipping plane. /// public float FarPlaneDistance { get { return farPlaneDistance; } set { farPlaneDistance = value; } } private float farPlaneDistance = 100000.0f; #endregion #region Matrix properties /// /// View transform matrix. /// public Matrix View { get { return view; } } private Matrix view; /// /// Projecton transform matrix. /// public Matrix Projection { get { return projection; } } private Matrix projection; #endregion #region Methods /// /// Rebuilds object space values in world space. Invoke before publicly /// returning or privately accessing world space values. /// private void UpdateWorldPositions() { // Construct a matrix to transform from object space to worldspace Matrix transform = Matrix.Identity; transform.Forward = ChaseDirection; transform.Up = Up; transform.Right = Vector3.Cross(Up, ChaseDirection); // Calculate desired camera properties in world space desiredPosition = ChasePosition + Vector3.TransformNormal(DesiredPositionOffset, transform); lookAt = ChasePosition + Vector3.TransformNormal(LookAtOffset, transform); } /// /// Rebuilds camera's view and projection matricies. /// private void UpdateMatrices() { view = Matrix.CreateLookAt(this.Position, this.LookAt, this.Up); projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlaneDistance, FarPlaneDistance); } /// /// Forces camera to be at desired position and to stop moving. The is useful /// when the chased object is first created or after it has been teleported. /// Failing to call this after a large change to the chased object's position /// will result in the camera quickly flying across the world. /// public void Reset() { UpdateWorldPositions(); // Stop motion velocity = Vector3.Zero; // Force desired position position = desiredPosition; UpdateMatrices(); } /// /// Animates the camera from its current position towards the desired offset /// behind the chased object. The camera's animation is controlled by a simple /// physical spring attached to the camera and anchored to the desired position. /// public void Update(GameTime gameTime) { if (gameTime == null) throw new ArgumentNullException("gameTime"); UpdateWorldPositions(); float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds; // Calculate spring force Vector3 stretch = position - desiredPosition; Vector3 force = -stiffness * stretch - damping * velocity; // Apply acceleration Vector3 acceleration = force / mass; velocity += acceleration * elapsed; // Apply velocity position += velocity * elapsed; UpdateMatrices(); } #endregion } }