#region File Description
//-----------------------------------------------------------------------------
// Cursor.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.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion
namespace TrianglePicking
{
///
/// Cursor is a DrawableGameComponent that draws a cursor on the screen. It works
/// differently on Xbox and Windows. On windows, this will be a cursor that is
/// controlled using both the mouse and the gamepad. On Xbox, the cursor will be
/// controlled using only the gamepad.
///
public class Cursor : DrawableGameComponent
{
#region Constants
// this constant controls how fast the gamepad moves the cursor. this constant
// is in pixels per second.
const float CursorSpeed = 250.0f;
#endregion
#region Fields and properties
// the content manager is passed in in Cursor's constructor, and will be used to
// load the texture for the cursor to draw with.
ContentManager content;
// this spritebatch is created internally, and is used to draw the cursor.
SpriteBatch spriteBatch;
// this is the sprite that is drawn at the current cursor position.
// textureCenter is used to center the sprite when drawing.
Texture2D cursorTexture;
Vector2 textureCenter;
// Position is the cursor position, and is in screen space.
private Vector2 position;
public Vector2 Position
{
get { return position;}
}
#endregion
#region Creation and initialization
// this constructor doesn't really do much of anything, just calls the base
// constructor, and saves the contentmanager so it can be used in
// LoadContent.
public Cursor(Game game, ContentManager content)
: base(game)
{
this.content = content;
}
// on Xbox360, initialize is overriden so that we can center the cursor once we
// know how big the viewport will be.
#if XBOX360
public override void Initialize()
{
base.Initialize();
Viewport vp = GraphicsDevice.Viewport;
position.X = vp.X + (vp.Width / 2);
position.Y = vp.Y + (vp.Height / 2);
}
#endif
// LoadContent needs to load the cursor texture and find its center.
// also, we need to create a SpriteBatch.
protected override void LoadContent()
{
cursorTexture = content.Load("cursor");
textureCenter = new Vector2(
cursorTexture.Width / 2, cursorTexture.Height / 2);
spriteBatch = new SpriteBatch(GraphicsDevice);
base.LoadContent();
}
#endregion
#region Draw
// Draw is pretty straightforward: we'll Begin the SpriteBatch, Draw the cursor,
// and then End.
public override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
// use textureCenter as the origin of the sprite, so that the cursor is
// drawn centered around Position.
spriteBatch.Draw(cursorTexture, Position, null, Color.White, 0.0f,
textureCenter, 1.0f, SpriteEffects.None, 0.0f);
spriteBatch.End();
base.Draw(gameTime);
}
#endregion
#region Update
// Update gets the current gamepad state and mouse state and uses that data to
// calculate where the cursor's position is on the screen. On xbox, the position
// is clamped to the viewport so that the cursor can't go off the screen. On
// windows, doing something like that would be rude :)
public override void Update(GameTime gameTime)
{
if (gameTime == null)
throw new ArgumentNullException("gameTime");
GamePadState currentState = GamePad.GetState(PlayerIndex.One);
// we'll create a vector2, called delta, which will store how much the
// cursor position should change.
Vector2 delta = currentState.ThumbSticks.Left;
// down on the thumbstick is -1. however, in screen coordinates, values
// increase as they go down the screen. so, we have to flip the sign of the
// y component of delta.
delta.Y *= -1;
// check the dpad: if any of its buttons are pressed, that will change delta
// as well.
if (currentState.DPad.Up == ButtonState.Pressed)
{
delta.Y = -1;
}
if (currentState.DPad.Down == ButtonState.Pressed)
{
delta.Y = 1;
}
if (currentState.DPad.Left == ButtonState.Pressed)
{
delta.X = -1;
}
if (currentState.DPad.Right == ButtonState.Pressed)
{
delta.X = 1;
}
// normalize delta so that we know the cursor can't move faster than
// CursorSpeed.
if (delta != Vector2.Zero)
{
delta.Normalize();
}
#if XBOX360
// modify position using delta, the CursorSpeed constant defined above, and
// the elapsed game time.
position += delta * CursorSpeed *
(float)gameTime.ElapsedGameTime.TotalSeconds;
// clamp the cursor position to the viewport, so that it can't move off the
// screen.
Viewport vp = GraphicsDevice.Viewport;
position.X = MathHelper.Clamp(position.X, vp.X, vp.X + vp.Width);
position.Y = MathHelper.Clamp(position.Y, vp.Y, vp.Y + vp.Height);
#else
MouseState mouseState = Mouse.GetState();
position.X = mouseState.X;
position.Y = mouseState.Y;
if (Game.IsActive)
{
// modify position using delta, the CursorSpeed constant defined above,
// and the elapsed game time, only if the cursor is on the screen
Viewport vp = GraphicsDevice.Viewport;
if ((vp.X <= position.X) && (position.X <= (vp.X + vp.Width)) &&
(vp.Y <= position.Y) && (position.Y <= (vp.Y + vp.Height)))
{
position += delta * CursorSpeed *
(float)gameTime.ElapsedGameTime.TotalSeconds;
position.X = MathHelper.Clamp(position.X, vp.X, vp.X + vp.Width);
position.Y = MathHelper.Clamp(position.Y, vp.Y, vp.Y + vp.Height);
}
else if (delta.LengthSquared() > 0f)
{
position.X = vp.X + vp.Width / 2;
position.Y = vp.Y + vp.Height / 2;
}
// set the new mouse position using the combination of mouse and gamepad
// data.
Mouse.SetPosition((int)position.X, (int)position.Y);
}
#endif
base.Update(gameTime);
}
#endregion
// CalculateCursorRay Calculates a world space ray starting at the camera's
// "eye" and pointing in the direction of the cursor. Viewport.Unproject is used
// to accomplish this. see the accompanying documentation for more explanation
// of the math behind this function.
public Ray CalculateCursorRay(Matrix projectionMatrix, Matrix viewMatrix)
{
// create 2 positions in screenspace using the cursor position. 0 is as
// close as possible to the camera, 1 is as far away as possible.
Vector3 nearSource = new Vector3(Position, 0f);
Vector3 farSource = new Vector3(Position, 1f);
// use Viewport.Unproject to tell what those two screen space positions
// would be in world space. we'll need the projection matrix and view
// matrix, which we have saved as member variables. We also need a world
// matrix, which can just be identity.
Vector3 nearPoint = GraphicsDevice.Viewport.Unproject(nearSource,
projectionMatrix, viewMatrix, Matrix.Identity);
Vector3 farPoint = GraphicsDevice.Viewport.Unproject(farSource,
projectionMatrix, viewMatrix, Matrix.Identity);
// find the direction vector that goes from the nearPoint to the farPoint
// and normalize it....
Vector3 direction = farPoint - nearPoint;
direction.Normalize();
// and then create a new ray using nearPoint as the source.
return new Ray(nearPoint, direction);
}
}
}