using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace GooCursor
{
public class Cursor : DrawableGameComponent
{
#region Private Structures
private struct TrailNode
{
public Vector2 Position;
public Vector2 Velocity;
}
#endregion
#region Fields and properties
// this is the sprite that is drawn at the current cursor position.
// textureCenter is used to center the sprite when drawing.
private Texture2D cursorTexture;
private Vector2 textureCenter;
private SpriteBatch spriteBatch;
private Vector2 position;
private int trailNodeCount;
private TrailNode[] trailNodes;
///
/// Gets of Sets the screen position of the cursor
///
public Vector2 Position
{
get { return position; }
set { position = value; }
}
///
/// Gets of Sets the stiffness of the trail
/// A lower number means the trail will be longer
///
public float TrailStiffness { get; set; }
///
/// Controls the damping of the velocity of trail nodes
///
public float TrailDamping { get; set; }
///
/// Mass of a trails node
///
public float TrailNodeMass { get; set; }
///
/// Controls how fast the gamepad moves the cursor.
/// Measured in pixels per second.
///
public float CursorSpeed { get; set; }
///
/// The scaling applied at the tip of the cursor
///
public float StartScale { get; set; }
///
/// The scaling applied at the end of the cursor
///
public float EndScale { get; set; }
///
/// use this to control the rate of change between the
/// StartScale and the EndScale
///
public float LerpExponent { get; set; }
///
/// Color used to fill the cursor
///
public Color FillColor { get; set; }
///
/// color used for the cursor border
///
public Color BorderColor { get; set; }
///
/// Size of the border (in pixels)
///
public float BorderSize { get; set; }
#endregion
#region Creation and initialization
public Cursor(Game game, int trailNodesNo, float stiffness, float damping)
:base(game)
{
trailNodeCount = trailNodesNo;
TrailStiffness = stiffness;
TrailDamping = damping;
trailNodes = new TrailNode[trailNodeCount];
CursorSpeed = 600;
StartScale = 1.0f;
EndScale = 0.3f;
LerpExponent = 0.5f;
TrailNodeMass = 11.2f;
FillColor = Color.Black;
BorderColor = Color.White;
BorderSize = 10;
}
public Cursor(Game game, int trailNodesNo)
: this(game, trailNodesNo, 30000, 600)
{
}
public Cursor(Game game)
: this(game,50,30000,600)
{
}
protected override void LoadContent()
{
cursorTexture = Game.Content.Load("cursor");
textureCenter = new Vector2(cursorTexture.Width / 2, cursorTexture.Height / 2);
spriteBatch = new SpriteBatch(GraphicsDevice);
base.LoadContent();
}
//we can center the cursor once we
//know how big the viewport will be
//this only really effects the 360 code
//where no mouse is available
public override void Initialize()
{
base.Initialize();
Viewport vp = GraphicsDevice.Viewport;
position.X = vp.X + (vp.Width / 2);
position.Y = vp.Y + (vp.Height / 2);
}
#endregion
#region Draw
public override void Draw(GameTime gameTime)
{
//spriteBatch.Begin SpriteBlendMode.AlphaBlend);
//spriteBatch.Begin(0, BlendState.AlphaBlend);
spriteBatch.Begin ();
//First we draw all the trail nodes using the border color
//we need to draw them slightly larger, so the border is left visible
//later, when we draw the actual nodes
//adjust the StartScale and EndScale to take into consideration the border
float borderStartScale = StartScale + BorderSize / cursorTexture.Width;
float borderEndScale = EndScale + BorderSize / cursorTexture.Width;
//draw all nodes with the new scales
for (int i = 0; i < trailNodeCount; i++)
{
TrailNode node = trailNodes[i];
float lerpFactor = (float)i / (float)(trailNodeCount - 1);
lerpFactor = (float)Math.Pow(lerpFactor, LerpExponent);
float scale = MathHelper.Lerp(borderStartScale, borderEndScale, lerpFactor);
//draw using the Border Color
spriteBatch.Draw(cursorTexture, node.Position, null, BorderColor, 0.0f,
textureCenter, scale, SpriteEffects.None, 0.0f);
}
//Next, we draw all the nodes normally, using the Fill Color
//Because before we drew them larger, after we draw them at
//their normal size, a border will remain visible.
for (int i = 0; i < trailNodeCount; i++)
{
TrailNode node = trailNodes[i];
float lerpFactor = (float)i / (float)(trailNodeCount - 1);
lerpFactor = (float)Math.Pow(lerpFactor, LerpExponent);
float scale = MathHelper.Lerp(StartScale, EndScale, lerpFactor);
//draw using the fill color
spriteBatch.Draw(cursorTexture, node.Position, null, FillColor, 0.0f,
textureCenter, scale, SpriteEffects.None, 0.0f);
}
spriteBatch.End();
}
#endregion
#region Update
Vector2 deltaMovement;
private void UpdateTrailNodes(float elapsed)
{
for (int i = 1; i < trailNodeCount; i++)
{
TrailNode tn = trailNodes[i];
// Calculate spring force
Vector2 stretch = tn.Position - trailNodes[i - 1].Position;
Vector2 force = -TrailStiffness * stretch - TrailDamping * tn.Velocity;
// Apply acceleration
Vector2 acceleration = force / TrailNodeMass;
tn.Velocity += acceleration * elapsed;
// Apply velocity
tn.Position += tn.Velocity * elapsed;
trailNodes[i] = tn;
}
}
public override void Update(GameTime gameTime)
{
// first, use the GamePad to update the cursor position
// 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.
deltaMovement = GamePad.GetState(PlayerIndex.One).ThumbSticks.Left;
deltaMovement.Y *= -1;
#if !XBOX360
//use the mouse position as the cursor position
MouseState mouseState = Mouse.GetState();
position.X = mouseState.X;
position.Y = mouseState.Y;
#endif
Console.WriteLine(position);
// modify position using delta, the CursorSpeed, and
// the elapsed game time.
position += deltaMovement * CursorSpeed *
(float)gameTime.ElapsedGameTime.TotalSeconds;
#if XBOX360
// 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
// set the new mouse position using the combination of mouse and gamepad data.
Mouse.SetPosition((int)position.X, (int)position.Y);
#endif
//set position of first trail node;
trailNodes[0].Position = position;
//update the trails
UpdateTrailNodes((float)gameTime.ElapsedGameTime.TotalSeconds);
}
#endregion
}
}