2012-05-24 3 views
1

Я вернулся. Еще раз. : 3 Прямо сейчас, я работаю над моим проектом RPG (просто для удовольствия, у меня нет иллюзий, что это будет легко), и я дошел до того, что написал основную структуру и Теперь я хочу написать элегантный метод рисования спрайтов со спрайта.Спрайты, XNA, C# и элегантность

На данный момент я использую карту спрайтов из алмаза pokémon (просто чтобы проверить, потому что она была легко доступна. Я не делаю следующую игру «покемон»), в которой главный герой, идущий во всех трех направлениях, одна строка, спрайты 37px x 37px, 12 спрайтов на изображении.

Изображение находится здесь: http://spriters-resource.com/ds/pkmndiamondpearl/lucas.png (Я работаю с подпрограммой «Ходьба», в настоящее время).

Я создал SpriteSheet класс (вместе с классом SpriteSheetData, который является представлением файла XML для коллекции spritesheets) и класса SpriteManager, все из которых перечислены ниже:

SpriteSheet.cs

namespace RPG.Utils.Graphics 
{ 
/// <summary> 
/// 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. 
/// </summary> 
public struct SpriteSheet 
{ 
    /// <summary> 
    /// The name for the texture. Used internally to reference the texture. 
    /// </summary> 
    [ContentSerializer] 
    public string TextureName 
    { 
     get; 
     private set; 
    } 
    /// <summary> 
    /// The file name of the texture. 
    /// </summary> 
    [ContentSerializer] 
    public string TextureFile 
    { 
     get; 
     private set; 
    } 
    /// <summary> 
    /// The width of each sprite in the sprite sheet. 
    /// </summary> 
    [ContentSerializer] 
    public int SpriteWidth 
    { 
     get; 
     private set; 
    } 
    /// <summary> 
    /// The height of each sprite in the sprite sheet. 
    /// </summary> 
    [ContentSerializer] 
    public int SpriteHeight 
    { 
     get; 
     private set; 
    } 
    /// <summary> 
    /// The interval between each frame of animation. 
    /// This should be (by default) 100f or 100ms. 
    /// </summary> 
    [ContentSerializer] 
    public float AnimationInterval 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// The number of frames per each individual animation. 
    /// </summary> 
    [ContentSerializer] 
    public int AnimationLength 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// The texture for this sprite sheet. 
    /// </summary> 
    [ContentSerializerIgnore] 
    public Texture2D Texture 
    { 
     get; 
     set; 
    } 
} 

SpriteManager.cs

/// <summary> 
/// A sprite manager. Just loads sprites from a file and then stores them. 
/// </summary> 
public static class SpriteManager 
{ 
    private static Dictionary<string, SpriteSheetData> m_spriteSheets; 
    public static Dictionary<string, SpriteSheetData> SpriteSheets 
    { 
     get 
     { 
      if (m_spriteSheets == null) 
       m_spriteSheets = new Dictionary<string, SpriteSheetData>(); 
      return m_spriteSheets; 
     } 
    } 
    /// <summary> 
    /// Loads all the sprites from the given directory using the content manager. 
    /// Sprites are loaded by iterating SpriteSheetData (.xml) files inside the /Sprites/ directory. 
    /// </summary> 
    /// <param name="mgr">Content Manager.</param> 
    /// <param name="subdir">Directory to load.</param> 
    public static void LoadAllSprites(ContentManager mgr, string subdir) 
    { 
     // Get the files in the subdirectory. 
     IEnumerable<string> 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<SpriteSheetData>(fname); 

      string spriteSheetDir = subdir +"/" + data.SpriteSheetName + "/"; 

      int loaded = 0; 
      for (int i = 0; i < data.SpriteSheets.Length; i++) 
      { 
       loaded++; 
       SpriteSheet current = data.SpriteSheets[i]; 
       current.Texture = mgr.Load<Texture2D>(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; 
     } 
    } 

    /// <summary> 
    /// Query if a given Sprite definition file is loaded. 
    /// </summary> 
    /// <param name="spriteName"> 
    /// 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. 
    /// </param> 
    /// <returns>True if the sprite definition file is loaded.</returns> 
    public static bool IsLoaded(string spriteName) 
    { 
     return SpriteSheets.ContainsKey(spriteName); 
    } 
} 

SpriteSheetData.cs

/// <summary> 
/// Represents data for a SpriteSheet. These are stored in XML files. 
/// </summary> 
public struct SpriteSheetData 
{ 
    /// <summary> 
    /// The collective name for the sprite sheets. 
    /// </summary> 
    [ContentSerializer] 
    public string SpriteSheetName 
    { 
     get; 
     set; 
    } 
    /// <summary> 
    /// The SpriteSheets in this data file. 
    /// </summary> 
    [ContentSerializer] 
    internal SpriteSheet[] SpriteSheets 
    { 
     get; 
     set; 
    } 


    [ContentSerializerIgnore] 
    private Dictionary<string, SpriteSheet> m_map; 
    /// <summary> 
    /// The sprite sheet map. 
    /// </summary> 
    [ContentSerializerIgnore] 
    public Dictionary<string, SpriteSheet> SpriteSheetMap 
    { 
     get 
     { 
      if (m_map == null) 
       m_map = new Dictionary<string, SpriteSheet>(); 
      return m_map; 
     } 
    } 
} 

И файл, который я использую, чтобы «читать» спрайтов является: спрайты/Hero.xml

<?xml version="1.0" encoding="utf-8" ?> 
<XnaContent> 
    <Asset Type="RPG.Utils.Graphics.SpriteSheetData"> 
    <SpriteSheetName>Hero</SpriteSheetName> 
    <SpriteSheets> 
     <Item Type="RPG.Utils.Graphics.SpriteSheet"> 
     <TextureName>HeroWalking</TextureName> 
     <TextureFile>hero_walk</TextureFile> 
     <SpriteWidth>37</SpriteWidth> 
     <SpriteHeight>37</SpriteHeight> 
     <AnimationInterval>400</AnimationInterval> 
     <AnimationLength>3</AnimationLength> 
     </Item> 
    </SpriteSheets> 
    </Asset> 
</XnaContent> 

Проблема, которую я имею что я неуверен способ элегантно организовать 1) загрузка спрайта и 2) спаривание листа спрайта (это «общая» часть игры, бит, который я намереваюсь повторно использовать, поэтому я не знаю КАК многие экземпляры объекта, который я могу создать) объекту/игровому объекту - особенно игроку (так как это то, что я пытаюсь сделать сейчас). Я не уверен, как продолжить, поэтому любая помощь ВОЗМОЖНО высоко ценится.

Если вам нужно больше кода (не дай Бог) просить и дастся вам: 3

ответ

0

Организация мудрый, я всегда любил делать это один конкретный путь, и структура вы использовали кредитует Сам по себе довольно хорошо к нему.

Я хотел, чтобы загрузить свои объекты, как так:

  1. Есть файл уровня (XML или TXT), который описывает то, что должно быть загружено для X задач/уровень.
  2. Итерации через это с циклом, каждый из объектов, которые должны быть загружены, имеет свой собственный файл (как и ваш Hero.xml).
  3. Каждый из них загружается через диспетчер уровней, который обрабатывает загрузку всех данных.
  4. Нарисуйте и перейдите к управлению уровнем с помощью диспетчера уровней.

Тогда в значительной степени зависит от вас, как вы называете своего менеджера уровней и реализуете его. Мне нравится использовать эти файлы конфигурации уровня, а также менеджеров уровней, потому что тогда вы можете делиться ресурсами по всему менеджеру уровня. Таким образом, вы можете использовать ссылочные текстуры из диспетчера уровней со словарем и передавать их вниз игровым объектам на вашем загруженном уровне, таким образом, у вас нет кратных загруженных текстур.

Из-за того, что я знаю в C#, «передать по ссылке» имеет тенденцию помешать этому быть проблемой, но я пришел из арены C++, где такое управление контентом для вашего уровня - отличная идея.

Что касается вашего игрока, вы можете создать класс менеджера игроков, который взаимодействует с менеджерами уровней, чтобы игрок мог оставаться постоянным между уровнями и нагрузкой.

Так я организую его в любом случае.

Cheers!

+0

Как в стороне, что-то, что мне нравится в целом с этими менеджерами, позволяет им иметь метод рисования. Этот метод принимает в SpriteBatch или GraphicsDevice и отображает его данные, а затем уходит. Это очень элегантно позволяет вам разделить вашу «Обновить логику» и «Рисовать» в вашем основном игровом цикле. Это также не дает вашим классам отделиться от знания слишком многого о другой логике игры и менеджерах. Мне нравится держать мои классы как можно более независимыми, чтобы предотвратить перенос ошибок. Надеюсь, что это поможет! Ура! –

Смежные вопросы