#region File Description
//-----------------------------------------------------------------------------
// Game.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Media;
#endregion
namespace Aiming
{
///
/// this sample showing how to aim one object towards another. In this sample, a
/// spotlight turns to aim towards a cat that the player controls.
///
public class AimingGame : Game
{
#region Constants
// how fast can the cat move? this is in terms of pixels per frame.
const float CatSpeed = 10.0f;
// how fast can the spot light turn? this is in terms of radians per frame.
const float SpotlightTurnSpeed = 0.025f;
#endregion
#region Fields
GraphicsDeviceManager graphics;
// we'll need a spriteBatch to draw the spotlight and cat.
SpriteBatch spriteBatch;
// these four values control the spotlight and how it draws.
// first is the actual sprite that we'll draw to represent the spotlight.
Texture2D spotlightTexture;
// next is the position of the spotlight on the screen.
Vector2 spotlightPosition = new Vector2();
// the origin of the spotlightTexture. The spotlight will rotate around this
// point.
Vector2 spotlightOrigin = new Vector2();
// the angle that the spotlight is currently facing. this is in radians. a value
// of 0 points to the right.
float spotlightAngle = 0.0f;
// these next three variables control the cat. catTexture is the sprite that
// represents the cat...
Texture2D catTexture;
// ...catPosition is the cat's position on the screen...
Vector2 catPosition = new Vector2();
// ...and catOrigin is the origin of catTexture. the sprite will be drawn
// centered around this value.
Vector2 catOrigin = new Vector2();
#endregion
#region Initialization
public AimingGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
#if WINDOWS_PHONE
graphics.SupportedOrientations = DisplayOrientation.Portrait;
graphics.PreferredBackBufferWidth = 480;
graphics.PreferredBackBufferHeight = 800;
TargetElapsedTime = TimeSpan.FromTicks(333333);
#else
graphics.PreferredBackBufferWidth = 320;
graphics.PreferredBackBufferHeight = 480;
#endif
graphics.IsFullScreen = true;
}
protected override void Initialize()
{
base.Initialize();
// once base.Initialize has finished, the GraphicsDevice will have been
// created, and we'll know how big the Viewport is. We want the spotlight
// to be centered in the middle of the screen, so we'll use the viewport
// to calculate where that is.
Viewport vp = graphics.GraphicsDevice.Viewport;
spotlightPosition.X = vp.X + vp.Width / 2;
spotlightPosition.Y = vp.Y + vp.Height / 2;
// we'll use the viewport size again, this time to put the cat on the
// screen. He goes 1/4 of the way across and halfway down.
catPosition.X = vp.X + vp.Width / 4;
catPosition.Y = vp.Y + vp.Height / 2;
}
///
/// Load your graphics content.
///
protected override void LoadContent()
{
// load our textures, and create a sprite batch...
spotlightTexture = Content.Load("spotlight");
catTexture = Content.Load("cat");
spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
// now that we've loaded our textures, we can use them to calculate some
// values that we'll use when drawing them. When we draw the spotlight,
// it needs to rotate around the "source" of the light. since
// spriteBatch.Draw will rotate sprites around the "origin" parameter,
// we need spotlightOrigin to be the "source" of the light. Since I drew
// spotlight.png myself, I happen to know that the source is halfway
// down the left hand side of the texture.
spotlightOrigin.X = 0;
spotlightOrigin.Y = spotlightTexture.Height / 2;
// Next, we want spriteBatch to draw the cat texture centered on the
// "catPosition" vector. SpriteBatch.Draw will center the sprite on the
// "origin" parameter, so we'll just calculate that to be the middle of
// the texture.
catOrigin.X = catTexture.Width / 2;
catOrigin.Y = catTexture.Height / 2;
}
#endregion
#region Update and Draw
///
/// Allows the game to run logic.
///
protected override void Update(GameTime gameTime)
{
HandleInput();
// clamp the cat's position so that it stays on the screen.
Viewport vp = graphics.GraphicsDevice.Viewport;
catPosition.X = MathHelper.Clamp(catPosition.X, vp.X, vp.X + vp.Width);
catPosition.Y = MathHelper.Clamp(catPosition.Y, vp.Y, vp.Y + vp.Height);
// use the TurnToFace function to update the spotlightAngle to face
// towards the cat.
spotlightAngle = TurnToFace(spotlightPosition, catPosition, spotlightAngle,
SpotlightTurnSpeed);
base.Update(gameTime);
}
///
/// Calculates the angle that an object should face, given its position, its
/// target's position, its current angle, and its maximum turning speed.
///
private static float TurnToFace(Vector2 position, Vector2 faceThis,
float currentAngle, float turnSpeed)
{
// consider this diagram:
// C
// /|
// / |
// / | y
// / o |
// S--------
// x
//
// where S is the position of the spot light, C is the position of the cat,
// and "o" is the angle that the spot light should be facing in order to
// point at the cat. we need to know what o is. using trig, we know that
// tan(theta) = opposite / adjacent
// tan(o) = y / x
// if we take the arctan of both sides of this equation...
// arctan( tan(o) ) = arctan( y / x )
// o = arctan( y / x )
// so, we can use x and y to find o, our "desiredAngle."
// x and y are just the differences in position between the two objects.
float x = faceThis.X - position.X;
float y = faceThis.Y - position.Y;
// we'll use the Atan2 function. Atan will calculates the arc tangent of
// y / x for us, and has the added benefit that it will use the signs of x
// and y to determine what cartesian quadrant to put the result in.
// http://msdn2.microsoft.com/en-us/library/system.math.atan2.aspx
float desiredAngle = (float)Math.Atan2(y, x);
// so now we know where we WANT to be facing, and where we ARE facing...
// if we weren't constrained by turnSpeed, this would be easy: we'd just
// return desiredAngle.
// instead, we have to calculate how much we WANT to turn, and then make
// sure that's not more than turnSpeed.
// first, figure out how much we want to turn, using WrapAngle to get our
// result from -Pi to Pi ( -180 degrees to 180 degrees )
float difference = WrapAngle(desiredAngle - currentAngle);
// clamp that between -turnSpeed and turnSpeed.
difference = MathHelper.Clamp(difference, -turnSpeed, turnSpeed);
// so, the closest we can get to our target is currentAngle + difference.
// return that, using WrapAngle again.
return WrapAngle(currentAngle + difference);
}
///
/// Returns the angle expressed in radians between -Pi and Pi.
///
private static float WrapAngle(float radians)
{
while (radians < -MathHelper.Pi)
{
radians += MathHelper.TwoPi;
}
while (radians > MathHelper.Pi)
{
radians -= MathHelper.TwoPi;
}
return radians;
}
///
/// This is called when the game should draw itself.
///
protected override void Draw(GameTime gameTime)
{
GraphicsDevice device = graphics.GraphicsDevice;
device.Clear(Color.Black);
// draw the cat.
spriteBatch.Begin();
spriteBatch.Draw(catTexture, catPosition, null, Color.White,
0.0f, catOrigin, 1.0f, SpriteEffects.None, 0.0f);
spriteBatch.End();
// Start sprite batch with additive blending, and draw the spotlight.
// Additive blending works very well for effects like lights and fire.
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive);
spriteBatch.Draw(spotlightTexture, spotlightPosition, null, Color.White,
spotlightAngle, spotlightOrigin, 1.0f, SpriteEffects.None, 0.0f);
spriteBatch.End();
base.Draw(gameTime);
}
#endregion
#region Handle Input
///
/// Handles input for quitting the game.
///
void HandleInput()
{
#if WINDOWS_PHONE
KeyboardState currentKeyboardState = new KeyboardState();
#else
KeyboardState currentKeyboardState = Keyboard.GetState();
#endif
GamePadState currentGamePadState = GamePad.GetState(PlayerIndex.One);
MouseState currentMouseState = Mouse.GetState();
TouchCollection currentTouchState = TouchPanel.GetState();
// Check for exit.
if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
currentGamePadState.Buttons.Back == ButtonState.Pressed)
{
Exit();
}
// check to see if the user wants to move the cat. we'll create a vector
// called catMovement, which will store the sum of all the user's inputs.
Vector2 catMovement = currentGamePadState.ThumbSticks.Left;
// flip y: on the thumbsticks, down is -1, but on the screen, down is bigger
// numbers.
catMovement.Y *= -1;
if (currentKeyboardState.IsKeyDown(Keys.Left) ||
currentGamePadState.DPad.Left == ButtonState.Pressed)
{
catMovement.X -= 1.0f;
}
if (currentKeyboardState.IsKeyDown(Keys.Right) ||
currentGamePadState.DPad.Right == ButtonState.Pressed)
{
catMovement.X += 1.0f;
}
if (currentKeyboardState.IsKeyDown(Keys.Up) ||
currentGamePadState.DPad.Up == ButtonState.Pressed)
{
catMovement.Y -= 1.0f;
}
if (currentKeyboardState.IsKeyDown(Keys.Down) ||
currentGamePadState.DPad.Down == ButtonState.Pressed)
{
catMovement.Y += 1.0f;
}
//Move toward the touch point. We slow down the cat when it gets within a distance of CatSpeed to the touch point.
float smoothStop = 1;
if (currentTouchState != null )
{
if (currentTouchState.Count > 0)
{
Vector2 touchPosition = currentTouchState[0].Position;
if (touchPosition != catPosition)
{
catMovement = touchPosition - catPosition;
float delta = CatSpeed - MathHelper.Clamp(catMovement.Length(), 0, CatSpeed);
smoothStop = 1 - delta / CatSpeed;
}
}
}
Vector2 mousePosition = new Vector2(currentMouseState.X, currentMouseState.Y);
if (currentMouseState.LeftButton == ButtonState.Pressed && mousePosition != catPosition)
{
catMovement = mousePosition - catPosition;
float delta = CatSpeed - MathHelper.Clamp(catMovement.Length(), 0, CatSpeed);
smoothStop = 1 - delta / CatSpeed;
}
// normalize the user's input, so the cat can never be going faster than
// CatSpeed.
if (catMovement != Vector2.Zero)
{
catMovement.Normalize();
}
catPosition += catMovement * CatSpeed * smoothStop;
}
#endregion
}
}