title: Sprites, XNA, C# and elegance
body: <p>I'm back. Again. :3
  Right now, I'm working on my RPG project (just for fun, I don't have any illusions that it will be easy), and I've come to the point where I've written the underlying framework and I now want to write an elegant method of drawing sprites from a sprite map.</p>
  
  <p>At the moment I am using a sprite map from pokémon diamond (just to test, because it was easily available. I am not making the next "pokemon" game), which has the main hero walking in all three directions on one row, 37px x 37px sprites, 12 sprites in the image.</p>
  
  <p>The image is here: <a href="http://spriters-resource.com/ds/pkmndiamondpearl/lucas.png" rel="nofollow">http://spriters-resource.com/ds/pkmndiamondpearl/lucas.png</a>
  (I am working with the "Walking" subset, currently).</p>
  
  <p>I have created SpriteSheet class (along with a SpriteSheetData class, which is a representation of an XML file for a collection of spritesheets) and a SpriteManager class, all of which are listed below:</p>
  
  <p>SpriteSheet.cs</p>
  
  <pre><code>namespace RPG.Utils.Graphics
  {
  /// &lt;summary&gt;
  /// Represents a Sprite Sheet. A Sprite Sheet is a graphic that contains a number of frames for animations.
  /// These are laid out a set distance apart.
  /// &lt;/summary&gt;
  public struct SpriteSheet
  {
      /// &lt;summary&gt;
      /// The name for the texture. Used internally to reference the texture.
      /// &lt;/summary&gt;
      [ContentSerializer]
      public string TextureName
      {
          get;
          private set;
      }
      /// &lt;summary&gt;
      /// The file name of the texture.
      /// &lt;/summary&gt;
      [ContentSerializer]
      public string TextureFile
      {
          get;
          private set;
      }
      /// &lt;summary&gt;
      /// The width of each sprite in the sprite sheet.
      /// &lt;/summary&gt;
      [ContentSerializer]
      public int SpriteWidth
      {
          get;
          private set;
      }
      /// &lt;summary&gt;
      /// The height of each sprite in the sprite sheet.
      /// &lt;/summary&gt;
      [ContentSerializer]
      public int SpriteHeight
      {
          get;
          private set;
      }
      /// &lt;summary&gt;
      /// The interval between each frame of animation.
      /// This should be (by default) 100f or 100ms.
      /// &lt;/summary&gt;
      [ContentSerializer]
      public float AnimationInterval
      {
          get;
          set;
      }
  
      /// &lt;summary&gt;
      /// The number of frames per each individual animation.
      /// &lt;/summary&gt;
      [ContentSerializer]
      public int AnimationLength
      {
          get;
          set;
      }
  
      /// &lt;summary&gt;
      /// The texture for this sprite sheet.
      /// &lt;/summary&gt;
      [ContentSerializerIgnore]
      public Texture2D Texture
      {
          get;
          set;
      }
  }
  </code></pre>
  
  <p>SpriteManager.cs</p>
  
  <pre><code>/// &lt;summary&gt;
  /// A sprite manager. Just loads sprites from a file and then stores them.
  /// &lt;/summary&gt;
  public static class SpriteManager
  {
      private static Dictionary&lt;string, SpriteSheetData&gt; m_spriteSheets;
      public static Dictionary&lt;string, SpriteSheetData&gt; SpriteSheets
      {
          get
          {
              if (m_spriteSheets == null)
                  m_spriteSheets = new Dictionary&lt;string, SpriteSheetData&gt;();
              return m_spriteSheets;
          }
      }
      /// &lt;summary&gt;
      /// Loads all the sprites from the given directory using the content manager.
      /// Sprites are loaded by iterating SpriteSheetData (.xml) files inside the /Sprites/ directory.
      /// &lt;/summary&gt;
      /// &lt;param name="mgr"&gt;Content Manager.&lt;/param&gt;
      /// &lt;param name="subdir"&gt;Directory to load.&lt;/param&gt;
      public static void LoadAllSprites(ContentManager mgr, string subdir)
      {
          // Get the files in the subdirectory.
          IEnumerable&lt;string&gt; files = Directory.EnumerateFiles(mgr.RootDirectory+"/"+subdir);
          foreach (string f in files)
          {
              // Microsoft, why do you insist on not letting us load stuff with file extensions?
              string fname = f.Replace("Content/", "").Replace(".xnb", "");
              SpriteSheetData data = mgr.Load&lt;SpriteSheetData&gt;(fname);
  
              string spriteSheetDir = subdir +"/" + data.SpriteSheetName + "/";
  
              int loaded = 0;
              for (int i = 0; i &lt; data.SpriteSheets.Length; i++)
              {
                  loaded++;
                  SpriteSheet current = data.SpriteSheets[i];
                  current.Texture = mgr.Load&lt;Texture2D&gt;(spriteSheetDir + current.TextureFile);
                  data.SpriteSheetMap[current.TextureName] = current;
              }
  
              Console.WriteLine("Loaded SpriteSheetData file \"{0}\".xml ({1} sprite sheet(s) loaded).", data.SpriteSheetName, loaded);
              SpriteSheets[data.SpriteSheetName] = data;
          }
      }
  
      /// &lt;summary&gt;
      /// Query if a given Sprite definition file is loaded.
      /// &lt;/summary&gt;
      /// &lt;param name="spriteName"&gt;
      /// The sprite definition file name (ie, "Hero"). This should correspond with the XML file
      /// that contains the definition for the sprite sheets. It should NOT be the name OF a spritesheet.
      /// &lt;/param&gt;
      /// &lt;returns&gt;True if the sprite definition file is loaded.&lt;/returns&gt;
      public static bool IsLoaded(string spriteName)
      {
          return SpriteSheets.ContainsKey(spriteName);
      }
  }
  </code></pre>
  
  <p>SpriteSheetData.cs</p>
  
  <pre><code>/// &lt;summary&gt;
  /// Represents data for a SpriteSheet. These are stored in XML files.
  /// &lt;/summary&gt;
  public struct SpriteSheetData
  {
      /// &lt;summary&gt;
      /// The collective name for the sprite sheets.
      /// &lt;/summary&gt;
      [ContentSerializer]
      public string SpriteSheetName
      {
          get;
          set;
      }
      /// &lt;summary&gt;
      /// The SpriteSheets in this data file.
      /// &lt;/summary&gt;
      [ContentSerializer]
      internal SpriteSheet[] SpriteSheets
      {
          get;
          set;
      }
  
  
      [ContentSerializerIgnore]
      private Dictionary&lt;string, SpriteSheet&gt; m_map;
      /// &lt;summary&gt;
      /// The sprite sheet map.
      /// &lt;/summary&gt;
      [ContentSerializerIgnore]
      public Dictionary&lt;string, SpriteSheet&gt; SpriteSheetMap
      {
          get
          {
              if (m_map == null)
                  m_map = new Dictionary&lt;string, SpriteSheet&gt;();
              return m_map;
          }
      }
  }
  </code></pre>
  
  <p>And the file that I am using to "read in" the sprites is:
  Sprites/Hero.xml</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
  &lt;XnaContent&gt;
    &lt;Asset Type="RPG.Utils.Graphics.SpriteSheetData"&gt;
      &lt;SpriteSheetName&gt;Hero&lt;/SpriteSheetName&gt;
      &lt;SpriteSheets&gt;
        &lt;Item Type="RPG.Utils.Graphics.SpriteSheet"&gt;
          &lt;TextureName&gt;HeroWalking&lt;/TextureName&gt;
          &lt;TextureFile&gt;hero_walk&lt;/TextureFile&gt;
          &lt;SpriteWidth&gt;37&lt;/SpriteWidth&gt;
          &lt;SpriteHeight&gt;37&lt;/SpriteHeight&gt;
          &lt;AnimationInterval&gt;400&lt;/AnimationInterval&gt;
          &lt;AnimationLength&gt;3&lt;/AnimationLength&gt;
        &lt;/Item&gt;
      &lt;/SpriteSheets&gt;
    &lt;/Asset&gt;
  &lt;/XnaContent&gt;
  </code></pre>
  
  <p>The issue I am having is that I am unsure of a way to elegantly organize 1) the sprite loading and 2) the pairing of a sprite sheet (this is the "generic" part of the game, the bit I intend on re-using, so I don't know HOW many instances of an entity I may create) to an entity/ingame object - particularly the player (since that is what I am attempting to do now). I am unsure of how to continue so any help is GREATLY appreciated.</p>
  
  <p>If you need any more code (God forbid) ask and it shall be given unto you :3</p>