#region File Description
//-----------------------------------------------------------------------------
// AnimatingSprite.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
#endregion
namespace RolePlayingGameData
{
///
/// A sprite sheet with flipbook-style animations.
///
public class AnimatingSprite : ContentObject
#if WINDOWS
, ICloneable
#endif
{
#region Graphics Data
///
/// The content path and name of the texture for this spell animation.
///
private string textureName;
///
/// The content path and name of the texture for this spell animation.
///
public string TextureName
{
get { return textureName; }
set { textureName = value; }
}
///
/// The texture for this spell animation.
///
private Texture2D texture;
///
/// The texture for this spell animation.
///
[ContentSerializerIgnore]
public Texture2D Texture
{
get { return texture; }
set { texture = value; }
}
#endregion
#region Frame Data
///
/// The dimensions of a single frame of animation.
///
private Point frameDimensions;
///
/// The width of a single frame of animation.
///
public Point FrameDimensions
{
get { return frameDimensions; }
set
{
frameDimensions = value;
frameOrigin.X = frameDimensions.X / 2;
frameOrigin.Y = frameDimensions.Y / 2;
}
}
///
/// The origin of the sprite, within a frame.
///
private Point frameOrigin;
///
/// The number of frames in a row in this sprite.
///
private int framesPerRow;
///
/// The number of frames in a row in this sprite.
///
public int FramesPerRow
{
get { return framesPerRow; }
set { framesPerRow = value; }
}
///
/// The offset of this sprite from the position it's drawn at.
///
private Vector2 sourceOffset;
///
/// The offset of this sprite from the position it's drawn at.
///
[ContentSerializer(Optional=true)]
public Vector2 SourceOffset
{
get { return sourceOffset; }
set { sourceOffset = value; }
}
#endregion
#region Animation Data
///
/// The animations defined for this sprite.
///
private List animations = new List();
///
/// The animations defined for this sprite.
///
public List Animations
{
get { return animations; }
set { animations = value; }
}
///
/// Enumerate the animations on this animated sprite.
///
/// The name of the animation.
/// The animation if found; null otherwise.
public Animation this[string animationName]
{
get
{
if (String.IsNullOrEmpty(animationName))
{
return null;
}
foreach (Animation animation in animations)
{
if (String.Compare(animation.Name, animationName, StringComparison.OrdinalIgnoreCase) == 0)
{
return animation;
}
}
return null;
}
}
///
/// Add the animation to the list, checking for name collisions.
///
/// True if the animation was added to the list.
public bool AddAnimation(Animation animation)
{
if ((animation != null) && (this[animation.Name] == null))
{
animations.Add(animation);
return true;
}
return false;
}
#endregion
#region Playback
///
/// The animation currently playing back on this sprite.
///
private Animation currentAnimation = null;
///
/// The current frame in the current animation.
///
private int currentFrame;
///
/// The elapsed time since the last frame switch.
///
private float elapsedTime;
///
/// The source rectangle of the current frame of animation.
///
private Rectangle sourceRectangle;
///
/// The source rectangle of the current frame of animation.
///
public Rectangle SourceRectangle
{
get { return sourceRectangle; }
}
///
/// Play the given animation on the sprite.
///
/// The given animation may be null, to clear any animation.
public void PlayAnimation(Animation animation)
{
// start the new animation, ignoring redundant Plays
if (animation != currentAnimation)
{
currentAnimation = animation;
ResetAnimation();
}
}
///
/// Play an animation given by index.
///
public void PlayAnimation(int index)
{
// check the parameter
if ((index < 0) || (index >= animations.Count))
{
throw new ArgumentOutOfRangeException("index");
}
PlayAnimation(this.animations[index]);
}
///
/// Play an animation given by name.
///
public void PlayAnimation(string name)
{
// check the parameter
if (String.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
PlayAnimation(this[name]);
}
///
/// Play a given animation name, with the given direction suffix.
///
///
/// For example, passing "Walk" and Direction.South will play the animation
/// named "WalkSouth".
///
public void PlayAnimation(string name, Direction direction)
{
// check the parameter
if (String.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
PlayAnimation(name + direction.ToString());
}
///
/// Reset the animation back to its starting position.
///
public void ResetAnimation()
{
elapsedTime = 0f;
if (currentAnimation != null)
{
currentFrame = currentAnimation.StartingFrame;
// calculate the source rectangle by updating the animation
UpdateAnimation(0f);
}
}
///
/// Advance the current animation to the final sprite.
///
public void AdvanceToEnd()
{
if (currentAnimation != null)
{
currentFrame = currentAnimation.EndingFrame;
// calculate the source rectangle by updating the animation
UpdateAnimation(0f);
}
}
///
/// Stop any animation playing on the sprite.
///
public void StopAnimation()
{
currentAnimation = null;
}
///
/// Returns true if playback on the current animation is complete, or if
/// there is no animation at all.
///
public bool IsPlaybackComplete
{
get
{
return ((currentAnimation == null) ||
(!currentAnimation.IsLoop &&
(currentFrame > currentAnimation.EndingFrame)));
}
}
#endregion
#region Updating
///
/// Update the current animation.
///
public void UpdateAnimation(float elapsedSeconds)
{
if (IsPlaybackComplete)
{
return;
}
// loop the animation if needed
if (currentAnimation.IsLoop && (currentFrame > currentAnimation.EndingFrame))
{
currentFrame = currentAnimation.StartingFrame;
}
// update the source rectangle
int column = (currentFrame - 1) / framesPerRow;
sourceRectangle = new Rectangle(
(currentFrame - 1 - (column * framesPerRow)) * frameDimensions.X,
column * frameDimensions.Y,
frameDimensions.X, frameDimensions.Y);
// update the elapsed time
elapsedTime += elapsedSeconds;
// advance to the next frame if ready
while (elapsedTime * 1000f > (float)currentAnimation.Interval)
{
currentFrame++;
elapsedTime -= (float)currentAnimation.Interval / 1000f;
}
}
#endregion
#region Drawing
///
/// Draw the sprite at the given position.
///
/// The SpriteBatch object used to draw.
/// The position of the sprite on-screen.
/// The depth at which the sprite is drawn.
public void Draw(SpriteBatch spriteBatch, Vector2 position, float layerDepth)
{
Draw(spriteBatch, position, layerDepth, SpriteEffects.None);
}
///
/// Draw the sprite at the given position.
///
/// The SpriteBatch object used to draw.
/// The position of the sprite on-screen.
/// The depth at which the sprite is drawn.
/// The sprite-effect applied.
public void Draw(SpriteBatch spriteBatch, Vector2 position, float layerDepth,
SpriteEffects spriteEffect)
{
// check the parameters
if (spriteBatch == null)
{
throw new ArgumentNullException("spriteBatch");
}
if (texture != null)
{
spriteBatch.Draw(texture, position, sourceRectangle, Color.White, 0f,
sourceOffset, 1f, spriteEffect,
MathHelper.Clamp(layerDepth, 0f, 1f));
}
}
#endregion
#region Content Type Reader
///
/// Read an AnimatingSprite object from the content pipeline.
///
public class AnimatingSpriteReader : ContentTypeReader
{
///
/// Read an AnimatingSprite object from the content pipeline.
///
protected override AnimatingSprite Read(ContentReader input,
AnimatingSprite existingInstance)
{
AnimatingSprite animatingSprite = existingInstance;
if (animatingSprite == null)
{
animatingSprite = new AnimatingSprite();
}
animatingSprite.AssetName = input.AssetName;
animatingSprite.TextureName = input.ReadString();
animatingSprite.Texture =
input.ContentManager.Load(
System.IO.Path.Combine(@"Textures",
animatingSprite.TextureName));
animatingSprite.FrameDimensions = input.ReadObject();
animatingSprite.FramesPerRow = input.ReadInt32();
animatingSprite.SourceOffset = input.ReadObject();
animatingSprite.Animations.AddRange(
input.ReadObject>());
return animatingSprite;
}
}
#endregion
#region ICloneable Members
///
/// Creates a clone of this object.
///
public object Clone()
{
AnimatingSprite animatingSprite = new AnimatingSprite();
animatingSprite.animations.AddRange(animations);
animatingSprite.currentAnimation = currentAnimation;
animatingSprite.currentFrame = currentFrame;
animatingSprite.elapsedTime = elapsedTime;
animatingSprite.frameDimensions = frameDimensions;
animatingSprite.frameOrigin = frameOrigin;
animatingSprite.framesPerRow = framesPerRow;
animatingSprite.sourceOffset = sourceOffset;
animatingSprite.sourceRectangle = sourceRectangle;
animatingSprite.texture = texture;
animatingSprite.textureName = textureName;
return animatingSprite;
}
#endregion
}
}