XNA Creators Club Online
Page 1 of 1 (6 items)
Sort Posts: Previous Next

Upgraded the Chase Camera with rotation.

Last post 12/12/2009 8:14 AM by Neith. 5 replies.
  • 1/8/2009 6:01 PM

    Upgraded the Chase Camera with rotation.

    Greetings.
    I used the Chase Camera demo for a demo project at work, and wanted to improve it a little with a rotation around the model for a more variable 3rd person view. Kind of like EVE Online.

    It also differs a little in that it is made a part of the components and the update method is called normally from the game loop. Forgive my nomenclature as I am not well versed in XNA program structures. I found the tutorial quite useful and wanted to share this version for those who might find it as useful as I found the original version. I got the Quaternion code from another demo, http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2/Quaternions.php 


    /**
     * Chase Camera.
     *
     * Ref: http://creators.xna.com/en-US/sample/chasecamera
     *
     * To use (in main class):
      ChaseCamera camera;
      bool cameraSpringEnabled = true;
     
      camera = new ChaseCamera(this, content);
      // Set the camera offsets
      camera.DesiredPositionOffset = new Vector3(100.0f, 45.0f, -100.0f);
      camera.LookAtOffset = new Vector3(0.0f, 0.0f, 0.0f);
      // Set camera perspective
      camera.NearPlaneDistance = 10.0f;
      camera.FarPlaneDistance = 100000.0f; 
     
      this.Components.Add(camera);  // in initialize() function.
      // Set the camera aspect ratio
      // This must be done after the class to base.Initalize() which will
      // initialize the graphics device.
      camera.AspectRatio = (float)graphics.GraphicsDevice.Viewport.Width / graphics.GraphicsDevice.Viewport.Height;

      // Perform an inital reset on the camera so that it starts at the resting
      // position. If we don't do this, the camera will start at the origin and
      // race across the world to get behind the chased object.
      // This is performed here because the aspect ratio is needed by Reset.
      UpdateCameraChaseTarget();
      camera.Reset();    

      Function in main class:
       protected override void Update(GameTime gameTime)
        {
          UpdateCameraChaseTarget();
          base.Update(gameTime);
        }
      UpdateCameraChaseTarget(); // Called in main class Update function.
     
     *
     */

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.Input;

    namespace GraphicsDemoDOTNET
    {
      public class ChaseCamera : DrawableGameComponent
      {
        private static ChaseCamera activeCamera = null;
        private ContentManager _content;

       
        private Quaternion rotation;
        private float turnSpeed = 30f;
        int centerX;
        int centerY;

        int mscroll;
       

        #region Chased object properties (set externally each frame)

        /// <summary>
        /// Position of object being chased.
        /// </summary>
        public Vector3 ChasePosition
        {
          get { return chasePosition; }
          set { chasePosition = value; }
        }
        private Vector3 chasePosition;

        /// <summary>
        /// Direction the chased object is facing.
        /// </summary>
        public Vector3 ChaseDirection
        {
          get { return chaseDirection; }
          set { chaseDirection = value; }
        }
        private Vector3 chaseDirection;

        /// <summary>
        /// Chased object's Up vector.
        /// </summary>
        public Vector3 Up
        {
          get { return up; }
          set { up = value; }
        }
        private Vector3 up = Vector3.Up;

        #endregion

        public static ChaseCamera ActiveCamera
        {
          get { return activeCamera; }
          set { activeCamera = value; }
        }

        public ChaseCamera(Game game, ContentManager content)
          : base(game)
        {
          _content = content;
          Reset();
          if (ActiveCamera == null)
            ActiveCamera = this;
        }

        #region Desired camera positioning (set when creating camera or changing view)

        /// <summary>
        /// Desired camera position in the chased object's coordinate system.
        /// </summary>
        public Vector3 DesiredPositionOffset
        {
          get { return desiredPositionOffset; }
          set { desiredPositionOffset = value; }
        }
        private Vector3 desiredPositionOffset = new Vector3(0, 0.0f, 0.0f);

        /// <summary>
        /// Desired camera position in world space.
        /// </summary>
        public Vector3 DesiredPosition
        {
          get
          {
            // Ensure correct value even if update has not been called this frame
            UpdateWorldPositions();

            return desiredPosition;
          }
        }
        private Vector3 desiredPosition;

        /// <summary>
        /// Look at point in the chased object's coordinate system.
        /// </summary>
        public Vector3 LookAtOffset
        {
          get { return lookAtOffset; }
          set { lookAtOffset = value; }
        }
       
        private Vector3 lookAtOffset = new Vector3(0, 0, 0);

        /// <summary>
        /// Look at point in world space.
        /// </summary>
        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)

        /// <summary>
        /// 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.
        /// </summary>
        public float Stiffness
        {
          get { return stiffness; }
          set { stiffness = value; }
        }
        private float stiffness = 1800.0f;

        /// <summary>
        /// Physics coefficient which approximates internal friction of the spring.
        /// Sufficient damping will prevent the spring from oscillating infinitely.
        /// </summary>
        public float Damping
        {
          get { return damping; }
          set { damping = value; }
        }
        private float damping = 600.0f;

        /// <summary>
        /// Mass of the camera body. Heaver objects require stiffer springs with less
        /// damping to move at the same rate as lighter objects.
        /// </summary>
        public float Mass
        {
          get { return mass; }
          set { mass = value; }
        }
        private float mass = 50.0f;

        #endregion

        #region Current camera properties (updated by camera physics)

        /// <summary>
        /// Position of camera in world space.
        /// </summary>
        public Vector3 Position
        {
          get { return position; }
        }
        private Vector3 position;

        /// <summary>
        /// Velocity of camera.
        /// </summary>
        public Vector3 Velocity
        {
          get { return velocity; }
        }
        private Vector3 velocity;

        #endregion

        #region Perspective properties

        /// <summary>
        /// Perspective aspect ratio. Default value should be overriden by application.
        /// </summary>
        public float AspectRatio
        {
          get { return aspectRatio; }
          set { aspectRatio = value; }
        }
        private float aspectRatio = 4.0f / 3.0f;

        /// <summary>
        /// Perspective field of view.
        /// </summary>
        public float FieldOfView
        {
          get { return fieldOfView; }
          set { fieldOfView = value; }
        }
        private float fieldOfView = MathHelper.ToRadians(45.0f); // Width of the view

        /// <summary>
        /// Distance to the near clipping plane.
        /// </summary>
        public float NearPlaneDistance
        {
          get { return nearPlaneDistance; }
          set { nearPlaneDistance = value; }
        }
        private float nearPlaneDistance = 1.0f;

        /// <summary>
        /// Distance to the far clipping plane.
        /// </summary>
        public float FarPlaneDistance
        {
          get { return farPlaneDistance; }
          set { farPlaneDistance = value; }
        }
        private float farPlaneDistance = 10000.0f;

        #endregion

        #region Matrix properties

        /// <summary>
        /// View transform matrix.
        /// </summary>
        public Matrix View
        {
          get { return view; }
        }
        private Matrix view;

        /// <summary>
        /// Projecton transform matrix.
        /// </summary>
        public Matrix Projection
        {
          get { return projection; }
        }
        private Matrix projection;

        #endregion

        #region Methods

        /// <summary>
        /// Rebuilds object space values in world space. Invoke before publicly
        /// returning or privately accessing world space values.
        /// </summary>
        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);
        }
        /// <summary>
        /// Rebuilds camera's view and projection matricies.
        /// </summary>
        private void UpdateMatrices()
        {
          view = Matrix.CreateLookAt(this.Position, this.LookAt, this.Up);   
          projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView,
              AspectRatio, NearPlaneDistance, FarPlaneDistance);
        }
        private void UpdateMatrices(int i)
        {
          Vector3 campos = new Vector3(0, 0.1f, 0.6f);
          campos = Vector3.Transform(campos, Matrix.CreateFromQuaternion(rotation));     
          Vector3 camup = new Vector3(0, 0, 0);
          camup = Vector3.Transform(camup, Matrix.CreateFromQuaternion(rotation));

          view = Matrix.CreateLookAt(this.Position+campos, this.LookAt, this.Up + camup);

          projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView,
                                   AspectRatio, NearPlaneDistance, FarPlaneDistance);

        }
        /// <summary>
        /// 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.
        /// </summary>
        public void Reset()
        {
          UpdateWorldPositions();
          // Stop motion
          velocity = Vector3.Zero;
          // Force desired position
          position = desiredPosition;
          rotation = new Quaternion(0, 0, 0, 1);     
          UpdateMatrices();
        }
        /// <summary>
        /// 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.
        /// </summary>
        public override void Update(GameTime gameTime)
        {
          if (gameTime == null)
            throw new ArgumentNullException("gameTime");
          //KeyboardState keyboard = Keyboard.GetState();
          MouseState mouse = Mouse.GetState();
          UpdateWorldPositions();
              
          if (mouse.LeftButton == ButtonState.Pressed)
          {
            mscroll = 0;
            centerX = Game.Window.ClientBounds.Width / 2;
            centerY = Game.Window.ClientBounds.Height / 2;      
            Mouse.SetPosition(centerX, centerY);
            //RevolveGlobal(new Vector3(1, 0, 0), (MathHelper.ToRadians((mouse.Y - centerY) * turnSpeed * 0.01f)));
            Revolve(new Vector3(1, 0, 0), (mouse.Y - centerY) * 0.01f);
            //RevolveGlobal(new Vector3(0, 1, 0), (MathHelper.ToRadians((mouse.X - centerX) * turnSpeed * 0.01f)));
            Revolve(new Vector3(0, 1, 0), (mouse.X - centerX) * 0.01f);
         //   TranslateGlobal(new Vector3(0, 0, 0));
            Translate(new Vector3(0, 0, 0));            
            UpdateMatrices(1);
          }
          else
          {
            mscroll = 1;
            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();     
          }
          //if (mouse.ScrollWheelValue <= mscroll)
          if (mscroll !=0 )
          {
            Translate(new Vector3(0, 0, mouse.ScrollWheelValue * 0.01f));       
          }
        }      
        #endregion

        public void Rotate(Vector3 axis, float angle)
        {
          axis = Vector3.Transform(axis, Matrix.CreateFromQuaternion(rotation));
          rotation = Quaternion.Normalize(Quaternion.CreateFromAxisAngle(axis, angle) * rotation);
        }
        public void RotateGlobal(Vector3 axis, float angle)
        {
          rotation = Quaternion.Normalize(Quaternion.CreateFromAxisAngle(axis, angle) * rotation);
        }
        public void Translate(Vector3 distance)
        {
          position += Vector3.Transform(distance, Matrix.CreateFromQuaternion(rotation));
        }
        public void TranslateGlobal(Vector3 distance)
        {
          position += distance;
        }
        public void Revolve(Vector3 axis, float angle)
        {
          Vector3 revolveAxis = Vector3.Transform(axis, Matrix.CreateFromQuaternion(rotation));
          Quaternion rotate = Quaternion.CreateFromAxisAngle(revolveAxis, angle);
          position = Vector3.Transform(position - ChasePosition, Matrix.CreateFromQuaternion(rotate)) + ChasePosition;
          Rotate(axis, angle);
        }
        public void RevolveGlobal(Vector3 axis, float angle)
        {
          Quaternion rotate = Quaternion.CreateFromAxisAngle(axis, angle);
          position = Vector3.Transform(position - ChasePosition, Matrix.CreateFromQuaternion(rotate)) + ChasePosition;
          RotateGlobal(axis, angle);
        }
      }
    }

  • 1/10/2009 7:11 AM In reply to

    Re: Upgraded the Chase Camera with rotation.

    awsome work!  ima try this sometime.
  • 5/16/2009 7:12 PM In reply to

    Re: Upgraded the Chase Camera with rotation.

    Thanks man, this has been really helpful
  • 5/23/2009 2:17 AM In reply to

    Re: Upgraded the Chase Camera with rotation.

    Sounds like you just did exactly what I was planning for my next step. I want to use the Right Thumbstick on the 360 controller to rotate my camera around the player character. Thanks, I'll check this out today.

    Tim
  • 5/24/2009 3:08 AM In reply to

    Re: Upgraded the Chase Camera with rotation.

    Hey,

    I had the chance to play around with your updated ChaseCamera yesterday. I had already done some considerable modifications to the ChaseCamera sample game, so I didn't have much luck with it. Whenever I would click the mouse button and drag, the view would rotate, but it was as if the camera was no longer updating it's position - ie. the target that I was following would begin to move around freely (running away from the camera) while the camera appeared to rotate around a fixed point in space.

    This could be caused by something else I had done (I made the ship much faster and only allowed for movement along X and Z - more like a car).

    Also, the call to Mouse.Center() created some interesting bugs. If I tab out of the game while it's running, then anywhere that I click on my screen does not register the mouse click for windows, but instead re-centers my mouse! At that point, the only thing I could do to end the sample program was ctrl+alt+del.

    As I said in my original post, I wanted the camera to rotate based on the right thumbstick of the controller. For this particular application, I simply needed the camera to rotate to specific locations at either left, right, or in front (looking back). My solution was to simply set the ChaseCamera.desiredOffsetPosition Vector based on user input.

    So far it's working splendidly.

    Just my input.
    Thanks,
    Tim
  • 12/12/2009 8:14 AM In reply to

    Re: Upgraded the Chase Camera with rotation.

    Awesome.  Thanks!
Page 1 of 1 (6 items) Previous Next
var gDomain='m.webtrends.com'; var gDcsId='dcschd84w10000w4lw9hcqmsz_8n3x'; var gTrackEvents=1; var gFpc='WT_FPC'; /*<\/scr"+"ipt>");} /*]]>*/
DCSIMG