#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 }