#region File Description
//-----------------------------------------------------------------------------
// Game.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
#endregion
namespace ChaseCameraSample
{
///
/// Sample showing how to implement a simple chase camera.
///
public class ChaseCameraGame : Microsoft.Xna.Framework.Game
{
#region Fields
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont spriteFont;
KeyboardState lastKeyboardState = new KeyboardState();
GamePadState lastGamePadState = new GamePadState();
MouseState lastMousState = new MouseState();
KeyboardState currentKeyboardState = new KeyboardState();
GamePadState currentGamePadState = new GamePadState();
MouseState currentMouseState = new MouseState();
Ship ship;
ChaseCamera camera;
Model shipModel;
Model groundModel;
bool cameraSpringEnabled = true;
#endregion
#region Initialization
public ChaseCameraGame()
{
graphics = new GraphicsDeviceManager(this);
graphics.SupportedOrientations = DisplayOrientation.Portrait;
Content.RootDirectory = "Content";
IsMouseVisible = true;
#if WINDOWS_PHONE
graphics.PreferredBackBufferWidth = 480;
graphics.PreferredBackBufferHeight = 800;
TargetElapsedTime = TimeSpan.FromTicks(333333);
graphics.IsFullScreen = true;
#else
graphics.PreferredBackBufferWidth = 853;
graphics.PreferredBackBufferHeight = 480;
#endif
// Create the chase camera
camera = new ChaseCamera();
// Set the camera offsets
camera.DesiredPositionOffset = new Vector3(0.0f, 2000.0f, 3500.0f);
camera.LookAtOffset = new Vector3(0.0f, 150.0f, 0.0f);
// Set camera perspective
camera.NearPlaneDistance = 10.0f;
camera.FarPlaneDistance = 100000.0f;
//TODO: Set any other camera invariants here such as field of view
}
///
/// Initalize the game
///
protected override void Initialize()
{
base.Initialize();
ship = new Ship(GraphicsDevice);
// 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();
}
///
/// Load graphics content.
///
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
spriteFont = Content.Load("gameFont");
shipModel = Content.Load("Ship");
groundModel = Content.Load("Ground");
}
#endregion
#region Update and Draw
///
/// Allows the game to run logic.
///
protected override void Update(GameTime gameTime)
{
lastKeyboardState = currentKeyboardState;
lastGamePadState = currentGamePadState;
lastMousState = currentMouseState;
#if WINDOWS_PHONE
currentKeyboardState = new KeyboardState();
#else
currentKeyboardState = Keyboard.GetState();
#endif
currentGamePadState = GamePad.GetState(PlayerIndex.One);
currentMouseState = Mouse.GetState();
// Exit when the Escape key or Back button is pressed
if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
currentGamePadState.Buttons.Back == ButtonState.Pressed)
{
Exit();
}
bool touchTopLeft = currentMouseState.LeftButton == ButtonState.Pressed &&
lastMousState.LeftButton != ButtonState.Pressed &&
currentMouseState.X < GraphicsDevice.Viewport.Width / 10 &&
currentMouseState.Y < GraphicsDevice.Viewport.Height / 10;
// Pressing the A button or key toggles the spring behavior on and off
if (lastKeyboardState.IsKeyUp(Keys.A) &&
(currentKeyboardState.IsKeyDown(Keys.A)) ||
(lastGamePadState.Buttons.A == ButtonState.Released &&
currentGamePadState.Buttons.A == ButtonState.Pressed) ||
touchTopLeft)
{
cameraSpringEnabled = !cameraSpringEnabled;
}
// Reset the ship on R key or right thumb stick clicked
if (currentKeyboardState.IsKeyDown(Keys.R) ||
currentGamePadState.Buttons.RightStick == ButtonState.Pressed)
{
ship.Reset();
camera.Reset();
}
// Update the ship
ship.Update(gameTime);
// Update the camera to chase the new target
UpdateCameraChaseTarget();
// 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)
camera.Update(gameTime);
else
camera.Reset();
base.Update(gameTime);
}
///
/// Update the values to be chased by the camera
///
private void UpdateCameraChaseTarget()
{
camera.ChasePosition = ship.Position;
camera.ChaseDirection = ship.Direction;
camera.Up = ship.Up;
}
///
/// Draws the ship and ground.
///
protected override void Draw(GameTime gameTime)
{
GraphicsDevice device = graphics.GraphicsDevice;
device.Clear(Color.CornflowerBlue);
GraphicsDevice.BlendState = BlendState.Opaque;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
DrawModel(shipModel, ship.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 the camera 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.World = transforms[mesh.ParentBone.Index] * world;
// Use the matrices provided by the chase camera
effect.View = camera.View;
effect.Projection = camera.Projection;
}
mesh.Draw();
}
}
///
/// Displays an overlay showing what the controls are,
/// and which settings are currently selected.
///
private void DrawOverlayText()
{
spriteBatch.Begin();
string text = "-Touch, Right Trigger, or Spacebar = thrust\n" +
"-Screen edges, Left Thumb Stick,\n or Arrow keys = steer\n" +
"-Press A or touch the top left corner\n to toggle camera spring (" + (cameraSpringEnabled ?
"on" : "off") + ")";
// 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
}
#region Entry Point
///
/// The main entry point for the application.
///
static class Program
{
static void Main()
{
using (ChaseCameraGame game = new ChaseCameraGame())
{
game.Run();
}
}
}
#endregion
}