AnimatingSprite.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. //-----------------------------------------------------------------------------
  2. // AnimatingSprite.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using Microsoft.Xna.Framework;
  10. using Microsoft.Xna.Framework.Content;
  11. using Microsoft.Xna.Framework.Graphics;
  12. namespace RolePlaying.Data
  13. {
  14. /// <summary>
  15. /// A sprite sheet with flipbook-style animations.
  16. /// </summary>
  17. public class AnimatingSprite : ContentObject
  18. #if WINDOWS
  19. , ICloneable
  20. #endif
  21. {
  22. /// <summary>
  23. /// The content path and name of the texture for this spell animation.
  24. /// </summary>
  25. private string textureName;
  26. /// <summary>
  27. /// The content path and name of the texture for this spell animation.
  28. /// </summary>
  29. public string TextureName
  30. {
  31. get { return textureName; }
  32. set { textureName = value; }
  33. }
  34. /// <summary>
  35. /// The texture for this spell animation.
  36. /// </summary>
  37. private Texture2D texture;
  38. /// <summary>
  39. /// The texture for this spell animation.
  40. /// </summary>
  41. [ContentSerializerIgnore]
  42. public Texture2D Texture
  43. {
  44. get { return texture; }
  45. set { texture = value; }
  46. }
  47. /// <summary>
  48. /// The dimensions of a single frame of animation.
  49. /// </summary>
  50. private Point frameDimensions;
  51. /// <summary>
  52. /// The width of a single frame of animation.
  53. /// </summary>
  54. public Point FrameDimensions
  55. {
  56. get { return frameDimensions; }
  57. set
  58. {
  59. frameDimensions = value;
  60. frameOrigin.X = frameDimensions.X / 2;
  61. frameOrigin.Y = frameDimensions.Y / 2;
  62. }
  63. }
  64. /// <summary>
  65. /// The origin of the sprite, within a frame.
  66. /// </summary>
  67. private Point frameOrigin;
  68. /// <summary>
  69. /// The number of frames in a row in this sprite.
  70. /// </summary>
  71. private int framesPerRow;
  72. /// <summary>
  73. /// The number of frames in a row in this sprite.
  74. /// </summary>
  75. public int FramesPerRow
  76. {
  77. get { return framesPerRow; }
  78. set { framesPerRow = value; }
  79. }
  80. /// <summary>
  81. /// The offset of this sprite from the position it's drawn at.
  82. /// </summary>
  83. private Vector2 sourceOffset;
  84. /// <summary>
  85. /// The offset of this sprite from the position it's drawn at.
  86. /// </summary>
  87. [ContentSerializer(Optional=true)]
  88. public Vector2 SourceOffset
  89. {
  90. get { return sourceOffset; }
  91. set { sourceOffset = value; }
  92. }
  93. /// <summary>
  94. /// The animations defined for this sprite.
  95. /// </summary>
  96. private List<Animation> animations = new List<Animation>();
  97. /// <summary>
  98. /// The animations defined for this sprite.
  99. /// </summary>
  100. public List<Animation> Animations
  101. {
  102. get { return animations; }
  103. set { animations = value; }
  104. }
  105. /// <summary>
  106. /// Enumerate the animations on this animated sprite.
  107. /// </summary>
  108. /// <param name="animationName">The name of the animation.</param>
  109. /// <returns>The animation if found; null otherwise.</returns>
  110. public Animation this[string animationName]
  111. {
  112. get
  113. {
  114. if (String.IsNullOrEmpty(animationName))
  115. {
  116. return null;
  117. }
  118. foreach (Animation animation in animations)
  119. {
  120. if (String.Compare(animation.Name, animationName, StringComparison.OrdinalIgnoreCase) == 0)
  121. {
  122. return animation;
  123. }
  124. }
  125. return null;
  126. }
  127. }
  128. /// <summary>
  129. /// Add the animation to the list, checking for name collisions.
  130. /// </summary>
  131. /// <returns>True if the animation was added to the list.</returns>
  132. public bool AddAnimation(Animation animation)
  133. {
  134. if ((animation != null) && (this[animation.Name] == null))
  135. {
  136. animations.Add(animation);
  137. return true;
  138. }
  139. return false;
  140. }
  141. /// <summary>
  142. /// The animation currently playing back on this sprite.
  143. /// </summary>
  144. private Animation currentAnimation = null;
  145. /// <summary>
  146. /// The current frame in the current animation.
  147. /// </summary>
  148. private int currentFrame;
  149. /// <summary>
  150. /// The elapsed time since the last frame switch.
  151. /// </summary>
  152. private float elapsedTime;
  153. /// <summary>
  154. /// The source rectangle of the current frame of animation.
  155. /// </summary>
  156. private Rectangle sourceRectangle;
  157. /// <summary>
  158. /// The source rectangle of the current frame of animation.
  159. /// </summary>
  160. public Rectangle SourceRectangle
  161. {
  162. get { return sourceRectangle; }
  163. }
  164. /// <summary>
  165. /// Play the given animation on the sprite.
  166. /// </summary>
  167. /// <remarks>The given animation may be null, to clear any animation.</remarks>
  168. public void PlayAnimation(Animation animation)
  169. {
  170. // start the new animation, ignoring redundant Plays
  171. if (animation != currentAnimation)
  172. {
  173. currentAnimation = animation;
  174. ResetAnimation();
  175. }
  176. }
  177. /// <summary>
  178. /// Play an animation given by index.
  179. /// </summary>
  180. public void PlayAnimation(int index)
  181. {
  182. // check the parameter
  183. if ((index < 0) || (index >= animations.Count))
  184. {
  185. throw new ArgumentOutOfRangeException("index");
  186. }
  187. PlayAnimation(this.animations[index]);
  188. }
  189. /// <summary>
  190. /// Play an animation given by name.
  191. /// </summary>
  192. public void PlayAnimation(string name)
  193. {
  194. // check the parameter
  195. if (String.IsNullOrEmpty(name))
  196. {
  197. throw new ArgumentNullException("name");
  198. }
  199. PlayAnimation(this[name]);
  200. }
  201. /// <summary>
  202. /// Play a given animation name, with the given direction suffix.
  203. /// </summary>
  204. /// <example>
  205. /// For example, passing "Walk" and Direction.South will play the animation
  206. /// named "WalkSouth".
  207. /// </example>
  208. public void PlayAnimation(string name, Direction direction)
  209. {
  210. // check the parameter
  211. if (String.IsNullOrEmpty(name))
  212. {
  213. throw new ArgumentNullException("name");
  214. }
  215. PlayAnimation(name + direction.ToString());
  216. }
  217. /// <summary>
  218. /// Reset the animation back to its starting position.
  219. /// </summary>
  220. public void ResetAnimation()
  221. {
  222. elapsedTime = 0f;
  223. if (currentAnimation != null)
  224. {
  225. currentFrame = currentAnimation.StartingFrame;
  226. // calculate the source rectangle by updating the animation
  227. UpdateAnimation(0f);
  228. }
  229. }
  230. /// <summary>
  231. /// Advance the current animation to the final sprite.
  232. /// </summary>
  233. public void AdvanceToEnd()
  234. {
  235. if (currentAnimation != null)
  236. {
  237. currentFrame = currentAnimation.EndingFrame;
  238. // calculate the source rectangle by updating the animation
  239. UpdateAnimation(0f);
  240. }
  241. }
  242. /// <summary>
  243. /// Stop any animation playing on the sprite.
  244. /// </summary>
  245. public void StopAnimation()
  246. {
  247. currentAnimation = null;
  248. }
  249. /// <summary>
  250. /// Returns true if playback on the current animation is complete, or if
  251. /// there is no animation at all.
  252. /// </summary>
  253. public bool IsPlaybackComplete
  254. {
  255. get
  256. {
  257. return ((currentAnimation == null) ||
  258. (!currentAnimation.IsLoop &&
  259. (currentFrame > currentAnimation.EndingFrame)));
  260. }
  261. }
  262. /// <summary>
  263. /// Update the current animation.
  264. /// </summary>
  265. public void UpdateAnimation(float elapsedSeconds)
  266. {
  267. if (IsPlaybackComplete)
  268. {
  269. return;
  270. }
  271. // loop the animation if needed
  272. if (currentAnimation.IsLoop && (currentFrame > currentAnimation.EndingFrame))
  273. {
  274. currentFrame = currentAnimation.StartingFrame;
  275. }
  276. // update the source rectangle
  277. int column = (currentFrame - 1) / framesPerRow;
  278. sourceRectangle = new Rectangle(
  279. (currentFrame - 1 - (column * framesPerRow)) * frameDimensions.X,
  280. column * frameDimensions.Y,
  281. frameDimensions.X, frameDimensions.Y);
  282. // update the elapsed time
  283. elapsedTime += elapsedSeconds;
  284. // advance to the next frame if ready
  285. while (elapsedTime * 1000f > (float)currentAnimation.Interval)
  286. {
  287. currentFrame++;
  288. elapsedTime -= (float)currentAnimation.Interval / 1000f;
  289. }
  290. }
  291. /// <summary>
  292. /// Draw the sprite at the given position.
  293. /// </summary>
  294. /// <param name="spriteBatch">The SpriteBatch object used to draw.</param>
  295. /// <param name="position">The position of the sprite on-screen.</param>
  296. /// <param name="layerDepth">The depth at which the sprite is drawn.</param>
  297. public void Draw(SpriteBatch spriteBatch, Vector2 position, float layerDepth)
  298. {
  299. Draw(spriteBatch, position, layerDepth, SpriteEffects.None);
  300. }
  301. /// <summary>
  302. /// Draw the sprite at the given position.
  303. /// </summary>
  304. /// <param name="spriteBatch">The SpriteBatch object used to draw.</param>
  305. /// <param name="position">The position of the sprite on-screen.</param>
  306. /// <param name="layerDepth">The depth at which the sprite is drawn.</param>
  307. /// <param name="spriteEffect">The sprite-effect applied.</param>
  308. public void Draw(SpriteBatch spriteBatch, Vector2 position, float layerDepth,
  309. SpriteEffects spriteEffect)
  310. {
  311. // check the parameters
  312. if (spriteBatch == null)
  313. {
  314. throw new ArgumentNullException("spriteBatch");
  315. }
  316. if (texture != null)
  317. {
  318. spriteBatch.Draw(texture, position, sourceRectangle, Color.White, 0f,
  319. sourceOffset, 1f, spriteEffect,
  320. MathHelper.Clamp(layerDepth, 0f, 1f));
  321. }
  322. }
  323. /// <summary>
  324. /// Read an AnimatingSprite object from the content pipeline.
  325. /// </summary>
  326. public class AnimatingSpriteReader : ContentTypeReader<AnimatingSprite>
  327. {
  328. /// <summary>
  329. /// Read an AnimatingSprite object from the content pipeline.
  330. /// </summary>
  331. protected override AnimatingSprite Read(ContentReader input,
  332. AnimatingSprite existingInstance)
  333. {
  334. AnimatingSprite animatingSprite = existingInstance;
  335. if (animatingSprite == null)
  336. {
  337. animatingSprite = new AnimatingSprite();
  338. }
  339. animatingSprite.AssetName = input.AssetName;
  340. animatingSprite.TextureName = input.ReadString();
  341. animatingSprite.Texture =
  342. input.ContentManager.Load<Texture2D>(
  343. System.IO.Path.Combine(@"Textures",
  344. animatingSprite.TextureName));
  345. animatingSprite.FrameDimensions = input.ReadObject<Point>();
  346. animatingSprite.FramesPerRow = input.ReadInt32();
  347. animatingSprite.SourceOffset = input.ReadObject<Vector2>();
  348. animatingSprite.Animations.AddRange(
  349. input.ReadObject<List<Animation>>());
  350. return animatingSprite;
  351. }
  352. }
  353. /// <summary>
  354. /// Creates a clone of this object.
  355. /// </summary>
  356. public object Clone()
  357. {
  358. AnimatingSprite animatingSprite = new AnimatingSprite();
  359. animatingSprite.animations.AddRange(animations);
  360. animatingSprite.currentAnimation = currentAnimation;
  361. animatingSprite.currentFrame = currentFrame;
  362. animatingSprite.elapsedTime = elapsedTime;
  363. animatingSprite.frameDimensions = frameDimensions;
  364. animatingSprite.frameOrigin = frameOrigin;
  365. animatingSprite.framesPerRow = framesPerRow;
  366. animatingSprite.sourceOffset = sourceOffset;
  367. animatingSprite.sourceRectangle = sourceRectangle;
  368. animatingSprite.texture = texture;
  369. animatingSprite.textureName = textureName;
  370. return animatingSprite;
  371. }
  372. }
  373. }