John Perry final Writeup
Index:
    1.) Write Up
    2.) Screen Shots
    3.) Code

1.) Write Up:


    I was very happy with the progress I made towards getting a great start on my research project for next semester. I learned a lot about Visual Studio, the C# language and most importantly XNA. Ultimately I was able to get an object, for my demonstration the object happened to be a space ship, flying in three dimensions with the camera following it. It also had a “spring effect” which worked well in emphasizing the speed of the object. I started by doing some tutorials on XNA, then I did some research on moving in three dimensions and ultimately dug up some old linear algebra and physics equations to get the ship and the “spring effect” working. First I will describe the tutorials.
    I started with a little two dimensional tutorial on making a picture float around the screen and when it hit the side it would bounce off it in the opposite direction. This was a good introduction to XNA’s syntax however I didn’t use much of the knowledge I gained there in the three dimensional animation I ultimately demonstrated. After the two dimensional tutorial I did a three part three dimensional tutorial. It was here that I learned most of the knowledge that I applied for creating my final animation. The first step was simply to display a rocket ship on the screen. I didn’t actually create the rocket ship however it did give me experience in using .fbx files. The second step was making the ship move in two dimensions and the third step was adding sound. This taught me a lot of things necessary for the final project. First I learned how three dimensional models work in XNA, second I learned how to take input from both the key board and the xbox game pad. The third step was adding sound when the ship boosted and when I warped the ship back to the center. I was able to get this working in the tutorial, however, I started from scratch with my demonstration and as things came towards the deadline I was not able to re-implement the sound which was disappointing.
    The first thing I decided I needed to do was to be able to control the camera in three dimensional space, preferably with an object in front of it. This is commonly known as third person view for most games. I first tried to implement this based directly off the tutorial I finished. After making no progress I began to search to see if there was any information on where to begin with what I wanted to do. I was able to find information on exactly what I was looking for and with an added "spring effect" that I actually planed on implementing but not until later in the project. It was here I quickly learned that movement in three dimensions is much more difficult than two dimensions. For the object i needed 4 vectors, the object position, its direction, it up vector and its right vector. All of this is just to get the ship's orientation in 3D space. We also have a velocity and acceleration vector. Finally we also have a vector for force, and constant for drag force. For as long as the user hold down the space bar, we have a trust amount of 1. We then get   force = objectDirection * thrustAmount * MaxForce; where MaxForce is just an arbitrary constant. Then we have   acceleration = force / Mass; objectVelocity += acceleration * elapsed;  where Mass is just a random constant I set, and elapse is the time we have been at this acceleration. when the space bar is not down acceleration and force = 0 so object velocity should be unaffected, however i also have the following line of code.  objectVelocity *= DragFactor; While the spacebar is not down this is the acting force that slows the spaceship. Finally we apply the velocity on the position and then make sure that the y position never goes below the "floor". We also use a matrix to store the  for the rotations around the x and y axis.
    The camera was a bit more difficult as it's position, velocity etc. were all based on where the object was. We needed to know what direction the the camera was looking to, much like how we found the direction the object facing. For the camera there are also some special properties like aspect ratio, field of view, near clipping plane and far clipping plane. Aspect ratio is basically the dimensions of the screen, I used 4:3 which is the size of a standard TV. Field of view is is how narrow or wide the camera is looking. The clipping planes just show only things that are between them. The far clipping plane is more important because anything beyond that plane won't be shown to the user. The near clipping plane is basically where the camera is so if somehow I got the ship to crash into the camera the camera would just not show the ship. We have two main matrices one called view which holds the camera's position, what it is looking at and it's up vector. Then we have a matrix called project which holds those four camera properties I mentioned above. The spring physics while conceptually was quite hard, here are the 2 lines of code which essentially produced the effect.
            Vector3 stretch = position - desiredCameraPosition;
            Vector3 force = -stiffness * stretch - damping * velocity;
the stretch vector is basically the difference between where the camera is now and where we want it to be. Then we calculate the force vector as described above. The stiffness needs to be pretty high so, like i showed in my demonstration, the plane doesn't get too far away from the camera. The dampening also needs to be much greater than the stretch so the the second term will not oscillate from positive to negative, because if it did, the camera would bounce back and forth behind the object. Once we have the force we can derive the acceleration, velocity and position as we did with the object.
    Finally the beginning class is called Game1. Here we make the object the camera and the ground, and call the methods of theObject, and CameraFollow. First we set the screen size and the camera properties. There are a few other initializations done here which i will go over a bit later when describing XNA as a whole. Here we also set that if we press r we reset the ship, if we press escape we quit etc. Finally we draw the scene. The drawing is a bit difficult to explain and part of it i used from the tutorials.  Each model is connect to  a set of bones, and those bones have meshes  and the meshes move alone with its parent bone, and when we draw it we calculate all the transformations on the bones and therefore the meshes. Finally we have the text on screen as instructions. The way we did this is by creating a SpriteBatch. Then i create a string with the instructions. Final we begin the spritebatch, do a   spriteBatch.DrawString(spriteFont, text, new Vector2(65, 65), Color.Black); spriteFont is just a  font i picked earlier in the program, text is the string with the instructions, and Vector2(65,65) just says where on screen the text should be drawn. this is a common way of showing HUDs on video games.

    Finally all XNA video games have the same basic structure. The entry point with the main method and basically a game.run(); call. In the game one class we have the game 1 method with many initializations. Then we have the initialize method where it allows the game to perform any initialization it needs to before starting to run. This is where it can query for any required services and load any non-graphic related content.  Calling base.Initialize will enumerate through any components and initialize them as well. Then we have the load and unload graphics content methods where we load things like models and fonts. Then we have the update method. the update method is very important because this is where we describe how the scene changes from frame to frame. Then we have the draw method that draws the scene was we describe it in update.

    Ultimately i was very happy with the final outcome of my project and i can't wait to get working on it again next semester.

2.) Screen Shots
Here are some in game screen shots of of my final project:
First this is just the ship being idle a little bit above the ground
Here is the ship turning with normal spring settings:
Here is the ship thrusting with normal spring settings:
Finally this is a demonstration of one of the spring properties being altered. It would be easier to see in a video but i did demonstrate this in class. In the below picture the stiffness of the spring is greatly reduced therefore the ship gets very far away from the camera before the camera start to follow it. Like a normal spring it also only follows it very slowly, since in the physics of the game, the masses of the camera and the ship are large, and the spring force is not very large.

3.) Code
The object code:


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

namespace Final
{
    class theObject
    {
        #region ---- Variable Declerations ----
        //AudioEngine audioEngine;
        //WaveBank waveBlank;
        //SoundBank soundBlank;

        //This is basically so there is a floor that the object can't fall through
        private const float MinimumAltitude = 350.0f;

        // Location of the object in world space.
        public Vector3 objectPosition;

        // Direction the object is facing.
        public Vector3 objectDirection;

        // The object's up vector.
        public Vector3 Up;

        // The object's right vector.
        private Vector3 right;
        public Vector3 Right
        {
            get { return right; }
        }

        // The speed that the can rotate in any direction
        // this is measured in radians per second
        private const float RotationRate = 1.5f;

        // Mass of the object.
        private const float Mass = 1.0f;

        // Maximum force that can be applied along the object's direction.
        private const float MaxForce = 24000.0f;

        // Approximate wind resistance to slow and eventually stop the vehical when trust is not applied
        private const float DragFactor = 0.97f;

        // Current object velocity.
        public Vector3 objectVelocity;

        // The object's world transform matrix.
        public Matrix World
        {
            get { return world; }
        }
        private Matrix world;
        #endregion

        #region ---- Initializations ----
        public theObject()
        {
            Reset();
        }

        // Restore the ship to its original starting state
        public void Reset()
        {
            objectPosition = new Vector3(0, MinimumAltitude, 0);
            objectDirection = Vector3.Forward;
            Up = Vector3.Up;
            right = Vector3.Right;
            objectVelocity = Vector3.Zero;
        }
        #endregion

        #region ---- Update ----
        /// Applies a simple rotation to the ship and animates position based
        /// on simple linear motion physics.
        public void Update(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);

            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            // Determine rotation amount from input
            Vector2 rotationAmount = -gamePadState.ThumbSticks.Left;
            if (keyboardState.IsKeyDown(Keys.Left))
                rotationAmount.X = 1.0f;
            if (keyboardState.IsKeyDown(Keys.Right))
                rotationAmount.X = -1.0f;
            if (keyboardState.IsKeyDown(Keys.Up))
                rotationAmount.Y = -1.0f;
            if (keyboardState.IsKeyDown(Keys.Down))
                rotationAmount.Y = 1.0f;

            // Scale rotation amount to radians per second
            rotationAmount = rotationAmount * RotationRate * elapsed;

            // Correct the X axis steering when the ship is upside down
            if (Up.Y < 0)
                rotationAmount.X = -rotationAmount.X;

            // Create rotation matrix from rotation amount
            Matrix rotationMatrix = Matrix.CreateFromAxisAngle(Right, rotationAmount.Y) * Matrix.CreateRotationY(rotationAmount.X);

            // Rotate orientation vectors
            objectDirection = Vector3.TransformNormal(objectDirection, rotationMatrix);
            Up = Vector3.TransformNormal(Up, rotationMatrix);

            // Re-normalize orientation vectors
            // Without this, the matrix transformations may introduce small rounding
            // errors which add up over time and could destabilize the ship.
            objectDirection.Normalize();
            Up.Normalize();

            // Re-calculate Right
            right = Vector3.Cross(objectDirection, Up);

            // The same instability may cause the 3 orientation vectors may
            // also diverge. Either the Up or Direction vector needs to be
            // re-computed with a cross product to ensure orthagonality
            Up = Vector3.Cross(Right, objectDirection);


            //******************************************************************
            // Determine thrust amount from input.
            //float thrustAmount = gamePadState.Triggers.Right;
            float thrustAmount = 0;
            if (keyboardState.IsKeyDown(Keys.Space))
                thrustAmount = 1.0f;

            // Calculate force from thrust amount
            Vector3 force = objectDirection * thrustAmount * MaxForce;


            // Apply acceleration
            Vector3 acceleration = force / Mass;
            objectVelocity += acceleration * elapsed;

            // Apply psuedo drag
            objectVelocity *= DragFactor;

            // Apply velocity
            objectPosition += objectVelocity * elapsed;


            // Prevent ship from flying under the ground
            objectPosition.Y = Math.Max(objectPosition.Y, MinimumAltitude);


            // Reconstruct the ship's world matrix
            world = Matrix.Identity;
            world.Forward = objectDirection;
            world.Up = Up;
            world.Right = right;
            world.Translation = objectPosition;
        }
        #endregion
    }
}





The camera code
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace Final
{
    public class CameraFollow
    {
        #region ---- Declarations ----
        #region ---- Declare vairables and properties of the object we are following ----
        // Position of object we are following.
        public Vector3 ObjectPosition
        {
            get { return objectPosition; }
            set { objectPosition = value; }
        }
        private Vector3 objectPosition;


        // Direction the object is facing.
        public Vector3 ObjectDirection
        {
            get { return objectDirection; }
            set { objectDirection = value; }
        }
        private Vector3 objectDirection;


        // The object's Up vector.
        public Vector3 Up
        {
            get { return up; }
            set { up = value; }
        }
        private Vector3 up = Vector3.Up;
        #endregion

        #region ---- Set camera position realtive to object and in world space ----
        // Desired camera position relative to the object.
        public Vector3 DesiredCameraPositionOffset
        {
            get { return desiredCameraPositionOffset; }
            set { desiredCameraPositionOffset = value; }
        }       
        private Vector3 desiredCameraPositionOffset = new Vector3(0, 2.0f, 2.0f);


        // Desired camera position in world space.
        public Vector3 DesiredCameraPosition
        {
            get
            {
                // Ensure correct value even if update has not been called this frame
                // Defined below
                UpdateWorldPositions();
                return desiredCameraPosition;
            }
        }
        private Vector3 desiredCameraPosition;


        // Where the camera is looking at based on The Object's coordinates
        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 ---- Setting physics values for the 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 =1200.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 ---- Position and velocity of camera ----
        // 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 NearClippingPlaneDistance
        {
            get { return nearClippingPlaneDistance; }
            set { nearClippingPlaneDistance = value; }
        }
        private float nearClippingPlaneDistance = 1.0f;


        // Distance to the far clipping ClippingPlane.
        public float FarClippingPlaneDistance
        {
            get { return farClippingPlaneDistance; }
            set { farClippingPlaneDistance = value; }
        }
        private float farClippingPlaneDistance = 10000.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
        #endregion


        //Actual Methods below
        #region ---- UpdateWorldPositions ----
        // translates coordinates that are relative to the object into real world positions
        // this is used by the camera since it's position is realtive to the object's
        private void UpdateWorldPositions()
        {
            // Construct a matrix to transform from object space to worldspace
            // always need a forward, right and up vector.
            Matrix transform = Matrix.Identity;
            transform.Forward = ObjectDirection;
            transform.Up = Up;
            // this does the cross product
            transform.Right = Vector3.Cross(Up, ObjectDirection);


            // Calculate desired camera properties in world space
            desiredCameraPosition = ObjectPosition + Vector3.TransformNormal(DesiredCameraPositionOffset, transform);
            lookAt = ObjectPosition + Vector3.TransformNormal(LookAtOffset, transform);
        }
        #endregion

        #region ---- UpdateMatricies ----
        // Rebuilds camera's view and projection matricies.
        private void UpdateMatrices()
        {
            view = Matrix.CreateLookAt(this.Position, this.LookAt, this.Up);
            projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearClippingPlaneDistance, FarClippingPlaneDistance);
        }
        #endregion

        #region ---- Reset ----
        // Forces camera to be at desired position and to stop moving. The is useful
        // when the object is first created or after it has been teleported back to original position.
        // 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 = desiredCameraPosition;

            UpdateMatrices();
        }
        #endregion

        #region ---- Update ----
        // This animates the camera everytime theCamera.Update(gameTime) is called in the Game1 class
        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 - desiredCameraPosition;
            Vector3 force = -stiffness * stretch - damping * velocity;

            // Apply acceleration
            Vector3 acceleration = force / mass;
            velocity += acceleration * elapsed;

            // Apply velocity
            position += velocity * elapsed;

            UpdateMatrices();
        }
        #endregion
    }
}


The game1 code

#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace Final
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region ---- Variable Declarations ----
        GraphicsDeviceManager graphics;
        ContentManager content;

        // for 2d graphics typcially health bars etc.
        SpriteBatch spriteBatch;
        SpriteFont spriteFont;

        KeyboardState lastKeyboardState = new KeyboardState();
        KeyboardState currentKeyboardState = new KeyboardState();

        CameraFollow theCamera;

        theObject theObject = new theObject();

        Model objectModel;
        Model groundModel;

        bool cameraSpringEnabled = true;
        #endregion

        #region ---- Game1 and Initialize ----
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            content = new ContentManager(Services);

            graphics.PreferredBackBufferWidth = 1440;
            graphics.PreferredBackBufferHeight = 900;

            theCamera = new CameraFollow();

            // Set the theCamera offsets
            theCamera.DesiredCameraPositionOffset = new Vector3(0.0f, 2000.0f, 3500.0f);
            theCamera.LookAtOffset = new Vector3(0.0f, 150.0f, 0.0f);

            // Set theCamera perspective
            theCamera.NearClippingPlaneDistance = 10.0f;
            theCamera.FarClippingPlaneDistance = 100000.0f;
        }


        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();


            // Set the camera aspect ratio
            // This must be done after the class to base.Initalize() which will
            // initialize the graphics device.
            // this will set the Aspect ratio equal to the entire screen
            theCamera.AspectRatio = (float)graphics.GraphicsDevice.Viewport.Width / graphics.GraphicsDevice.Viewport.Height;


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

        #region ---- Load / Unload content ----
        /// <summary>
        /// Load your graphics content.  If loadAllContent is true, you should
        /// load content from both ResourceManagementMode pools.  Otherwise, just
        /// load ResourceManagementMode.Manual content.
        /// </summary>
        /// <param name="loadAllContent">Which type of content to load.</param>
        protected override void LoadGraphicsContent(bool loadAllContent)
        {
            if (loadAllContent)
            {
                spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
                spriteFont = content.Load<SpriteFont>("Content/Arial");

                objectModel = content.Load<Model>("Content/p1_wedge");
                groundModel = content.Load<Model>("Content/Ground");
            }
        }


        /// <summary>
        /// Unload your graphics content.  If unloadAllContent is true, you should
        /// unload content from both ResourceManagementMode pools.  Otherwise, just
        /// unload ResourceManagementMode.Manual content.  Manual content will get
        /// Disposed by the GraphicsDevice during a Reset.
        /// </summary>
        /// <param name="unloadAllContent">Which type of content to unload.</param>
        protected override void UnloadGraphicsContent(bool unloadAllContent)
        {
            if (unloadAllContent)
            {
                content.Unload();
            }
        }
        #endregion

        #region ---- Update ----
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            lastKeyboardState = currentKeyboardState;
            currentKeyboardState = Keyboard.GetState();

            // Allows the game to exit
            if (currentKeyboardState.IsKeyDown(Keys.Escape))
                this.Exit();

            // This switches whether or not spring physicis is on
            if (lastKeyboardState.IsKeyUp(Keys.A) && (currentKeyboardState.IsKeyDown(Keys.A)) )
            {
                cameraSpringEnabled = !cameraSpringEnabled;
            }

            // Reset to starting position if R is pressed
            if (currentKeyboardState.IsKeyDown(Keys.R))
            {
                theObject.Reset();
                theCamera.Reset();
            }

            // Update the object
            theObject.Update(gameTime);

            // Update the camera to chase the new target
            UpdateCameraObjectTarget();

            // The chase camera's update behavior is the springs, but we can
            // use the Reset method to have a locked, spring-less camera
            if (cameraSpringEnabled)
                theCamera.Update(gameTime);
            else
                theCamera.Reset();

            base.Update(gameTime);
        }

        // Update the values to be chased by the camera
        private void UpdateCameraObjectTarget()
        {
            theCamera.ObjectPosition = theObject.objectPosition;
            theCamera.ObjectDirection = theObject.objectDirection;
            theCamera.Up = theObject.Up;
        }
        #endregion

        #region ---- Draw ----
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            //graphics.GraphicsDevice.Clear(Color.DarkSeaGreen);
            GraphicsDevice theDevice = graphics.GraphicsDevice;

            theDevice.Clear(Color.DarkSeaGreen);

            DrawModel(objectModel, theObject.World);
            DrawModel(groundModel, Matrix.Identity);

            DrawOverlayText();

            base.Draw(gameTime);
        }

        // Simple model drawing method. The interesting part here is that
        // the view and projection matrices are taken from theCamera object.     
        private void DrawModel(Model model, Matrix world)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;
                    effect.World = transforms[mesh.ParentBone.Index] * world;

                    // Use the matrices provided by the chase camera
                    effect.View = theCamera.View;
                    effect.Projection = theCamera.Projection;
                }
                mesh.Draw();
            }
        }

        // Display the instructions in 2d on the screen
        private void DrawOverlayText()
        {
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);

            string text = "Spacebar = thrust\n" +
                          "Arrow keys = steer\n" +
                          "R = reset position of ship to middle\n" +
                          "A = toggle camera spring (" + (cameraSpringEnabled ?
                              "on" : "off") + ")\n" +
                          "Esc = exit";

            // Draw the string twice to create a drop shadow, first colored black
            // and offset one pixel to the bottom right, then again in white at the
            // intended position. This makes text easier to read over the background.
            spriteBatch.DrawString(spriteFont, text, new Vector2(65, 65), Color.Black);
            spriteBatch.DrawString(spriteFont, text, new Vector2(64, 64), Color.White);

            spriteBatch.End();
        }
        #endregion
    }
}