Running on Empty

The few things I know, I like to share.

XNA Framework GameEngine Development. (Part 10, CameraManager GameComponents)

Introduction

Welcome to Part 10 of the XNA Framework GameEngine Development series.  In this article I will implement the CameraManager and first person camera.  I will be expanding on the camera class in future atricles, for now I will frame out the class and get some simple input logic in place for the next article.

CameraManager GameComponent

The CameraManager class will manage all of the camera interaction and updates.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using System.Collections;
using RoeEngine2.GameComponents;

namespace RoeEngine2.Managers
{
    public class CameraManager : GameComponent
    {
        private static Hashtable _cameras = new Hashtable();

        public enum CameraNumber
        {
            _default = 1,
            _dolly = 2,
            _3 = 3,
            _4 = 4,
            _5 = 5,
            _6 = 6,
            _7 = 7,
            _8 = 8,
            _9 = 9,
            _10 = 10
        }

        private static bool _initialized = false;
        /// <summary>
        /// Is the CameraManagers Initialized, used for test cases and setup of Effects.
        /// </summary>
        public static bool Initialized
        {
            get { return _initialized; }
        }

        private static Camera _activeCamera;
        /// <summary>
        /// The camera where all the action takes place.
        /// </summary>
        public static Camera ActiveCamera
        {
            get { return _activeCamera; }
        }

        /// <summary>
        /// Create the camera Managers.
        /// </summary>
        /// <param name="game"></param>
        public CameraManager(Game game)
            : base(game)
        {
            Enabled = true;
        }
        
        /// <summary>
        /// Create the cameras.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            AddCamera(new Camera(Game), CameraNumber._default);
            SetActiveCamera(CameraNumber._default);
            AddCamera(new Camera(Game), CameraNumber._dolly);

            _initialized = true;
        }

        /// <summary>
        /// Update the active camera.
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
            _activeCamera.Update();
        }

        /// <summary>
        /// Adds a new camera to the CameraManagers
        /// </summary>
        /// <param name="newCamera"></param>
        /// <param name="cameraLabel"></param>
        public static void AddCamera(Camera newCamera, CameraNumber cameraNumber)
        {
            if (!_cameras.Contains(cameraNumber))
            {
                _cameras.Add(cameraNumber, newCamera);
            }
        }

        /// <summary>
        /// Change the projection matrix of all cameras.
        /// </summary>
        /// <param name="aspectRatio"></param>
        public static void SetAllCamerasProjectionMatrix(float aspectRatio)
        {
            foreach (Camera camera in _cameras.Values)
            {
                camera.Projection = Matrix.CreatePerspectiveFieldOfView(
                                        camera.FieldOfView, aspectRatio, camera.NearPlane, camera.FarPlane);
            }
        }

        /// <summary>
        /// Changes the active camera by label
        /// </summary>
        /// <param name="cameraLabel"></param>
        public static void SetActiveCamera(CameraNumber cameraNumber)
        {
            if (_cameras.ContainsKey(cameraNumber))
            {
                _activeCamera = _cameras[cameraNumber] as Camera;
            }
        }     
    }
}

Camera Class

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace RoeEngine2.GameComponents
{
    public class Camera
    {
        /// <summary>
        /// The type of the Camera.
        /// </summary>
        public enum CameraEnumType
        {
            Fixed = 0,
            FirstPerson = 1, 
            ThirdPerson = 2
        }

        private float _fieldOfView = MathHelper.Pi / 3.0f;
        /// <summary>
        /// The viewable angle.
        /// </summary>
        public float FieldOfView
        {
            get { return _fieldOfView; }
            set { _fieldOfView = value; }
        }

        private float _nearPlane = 0.1f;
        /// <summary>
        /// The near plane used to determine the viewable area.
        /// </summary>
        public float NearPlane
        {
            get { return _nearPlane; }
            set { _nearPlane = value; }
        }

        private float _farPlane = 3500.0f;
        /// <summary>
        /// The far plane used to determine the viewable area.
        /// </summary>
        public float FarPlane
        {
            get { return _farPlane; }
            set { _farPlane = value; }
        }

        /// <summary>
        /// Slightly smaller viewable field of view for culling.
        /// </summary>
        public float ViewableFieldOfView
        {
            get { return FieldOfView / 1.125f; }
        }

        private CameraEnumType _cameraType = CameraEnumType.Fixed;
        /// <summary>
        /// The type of the camera.
        /// </summary>
        public CameraEnumType CameraType
        {
            get { return _cameraType; }
            set { _cameraType = value; }
        }

        private Vector3 _position = Vector3.Zero;
        /// <summary>
        /// Postition of the camera.
        /// </summary>
        public Vector3 Position
        {
            get { return _position; }
        }

        private Vector3 _cameraReference = new Vector3(0, 0, 10);
        /// <summary>
        /// The spot in 3d space where the camera is looking.
        /// </summary>
        public Vector3 CameraReference
        {
            get { return _cameraReference; }
        }

        private float _yaw = 0.0f;
        /// <summary>
        /// Angle around the Y axis.
        /// </summary>
        public float Yaw
        {
            get { return _yaw; }
        }

        private float _pitch = 0.0f;
        /// <summary>
        /// Angle around the X axis.
        /// </summary>
        public float Pitch
        {
            get { return _pitch; }
        }

        private Matrix _world;
        /// <summary>
        /// The world matrix.
        /// </summary>
        public Matrix World
        {
            get { return _world; }
            set { _world = value; }
        }

        private Matrix _view;
        /// <summary>
        /// Matrix containing coordinates of the camera.
        /// </summary>
        public Matrix View
        {
            get { return _view; }
            set { _view = value; }
        }

        private Matrix _reflectedView;
        /// <summary>
        /// Reflected View matrix around an arbitrary plane.
        /// </summary>
        public Matrix ReflectedView
        {
            get { return _reflectedView; }
            set { _reflectedView = value; }
        }

        private Matrix _projection;
        /// <summary>
        /// The projection matrix, what can be seen.
        /// </summary>
        public Matrix Projection
        {
            get { return _projection; }
            set { _projection = value; }
        }

        private BoundingFrustum _frustum;
        /// <summary>
        /// The trapezoid that contains everything that the camera can see.
        /// </summary>
        public BoundingFrustum Frustum
        {
            get { return _frustum; }
        }

        private BoundingFrustum _reflectedFrustum;
        /// <summary>
        /// The trapezoid that contains everything that the camera can see if it was reflected.
        /// </summary>
        public BoundingFrustum ReflectedFrustum
        {
            get { return _reflectedFrustum; }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="game">The game.</param>
        public Camera(Game game)
        {
            // TODO: Construct any child components here
        }

        /// <summary>
        /// Set the position in 3d space.
        /// </summary>
        /// <param name="newPosition"></param>
        public void SetPosition(Vector3 newPosition)
        {
            _position = newPosition;
        }

        /// <summary>
        /// Set the point in 3d space where the camera is looking.
        /// </summary>
        /// <param name="newReference"></param>
        public void SetCameraReference(Vector3 newReference)
        {
            _cameraReference = newReference;
        }

        /// <summary>
        /// Move the camera in 3d space.
        /// </summary>
        /// <param name="move"></param>
        public void Translate(Vector3 move)
        {
            Matrix forwardMovement = Matrix.CreateRotationY(_yaw);
            Vector3 v = new Vector3(0, 0, 0);
            v = Vector3.Transform(move, forwardMovement);
            _position.Z += v.Z;
            _position.X += v.X;
            _position.Y += v.Y;
        }

        /// <summary>
        /// Rotate around the Y, Default Up axis.  Usually called Yaw.
        /// </summary>
        /// <param name="angle">Angle in degrees to rotate the camera.</param>
        public void RotateY(float angle)
        {
            angle = MathHelper.ToRadians(angle);
            if (_yaw >= MathHelper.Pi * 2)
                _yaw = MathHelper.ToRadians(0.0f);
            else if (_yaw <= -MathHelper.Pi * 2)
                _yaw = MathHelper.ToRadians(0.0f);
            _yaw += angle;
        }

        /// <summary>
        /// Rotate the camera around the X axis.  Usually called pitch.
        /// </summary>
        /// <param name="angle">Angle in degrees to rotate the camera.</param>
        public void RotateX(float angle)
        {
            angle = MathHelper.ToRadians(angle);
            _pitch += angle;
            if (_pitch >= MathHelper.ToRadians(75))
                _pitch = MathHelper.ToRadians(75);
            else if (_pitch <= MathHelper.ToRadians(-75))
                _pitch = MathHelper.ToRadians(-75);
        }

        private void UpdateFirstPerson()
        {
            Vector3 cameraPosition = _position;
            Matrix rotationMatrix = Matrix.CreateRotationY(_yaw);
            Matrix pitchMatrix = Matrix.Multiply(Matrix.CreateRotationX(_pitch), rotationMatrix);
            Vector3 transformedReference = Vector3.Transform(_cameraReference, pitchMatrix);
            Vector3 cameraLookat = cameraPosition + transformedReference;

            _view = Matrix.CreateLookAt(cameraPosition, cameraLookat, Vector3.Up);

            _frustum = new BoundingFrustum(Matrix.Multiply(_view, _projection));
            _reflectedFrustum = new BoundingFrustum(Matrix.Multiply(_reflectedView, _projection));
        }

        private void UpdateFixed()
        {
            _view = Matrix.CreateLookAt(_position, _cameraReference, Vector3.Up);

            _frustum = new BoundingFrustum(Matrix.Multiply(_view, _projection));
            _reflectedFrustum = new BoundingFrustum(Matrix.Multiply(_reflectedView, _projection));
        }

        /// <summary>
        /// Update the Camera.
        /// </summary>
        public void Update()
        {
            if (CameraType == CameraEnumType.Fixed)
            {
                UpdateFixed();
            }
            else if (CameraType == CameraEnumType.FirstPerson)
            {
                UpdateFirstPerson();
            }
            else if (CameraType == CameraEnumType.ThirdPerson)
            {

            }

        }
    }
}

Using the CameraManager GameComponent

Add the following code to the properties section of RoeEngine2.

private static CameraManager _cameraManagers = null;

And in the RoeEngine2 Constructor

// Init camera Managers
_cameraManagers = new CameraManager(this);
Components.Add(_cameraManagers);

Optional TDD:

[Test, Category("CameraManager")]
public void CameraManagerCreated()
{
     Assert.IsTrue(CameraManager.Initialized);
}

Conclusion

In this article I implemented a simple first person camera class and camera manager.  We can now move the camera around the world, the next article will build on everything we have done so far to draw some objects on the screen and move the camera around to actually view them.

About these ads

February 11, 2008 - Posted by | C#, GameComponent, TDD, XBOX360, XNA

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: