1

我回来了。再次。:3 现在,我正在做我的 RPG 项目(只是为了好玩,我不幻想它会很容易),我已经到了编写底层框架的地步,我现在想写一个优雅的方法来从精灵映射中绘制精灵。

目前我正在使用来自神奇宝贝钻石的精灵图(只是为了测试,因为它很容易获得。我不会制作下一个“神奇宝贝”游戏),其中主要英雄在一行中的所有三个方向行走,37px x 37px 精灵,图像中有 12 个精灵。

图片在这里:http ://spriters-resource.com/ds/pkmndiamondpearl/lucas.png (我目前正在使用“Walking”子集)。

我创建了 SpriteSheet 类(以及 SpriteSheetData 类,它是 spritesheets 集合的 XML 文件的表示)和 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;
    }
}

精灵管理器.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;
        }
    }
}

我用来“读入”精灵的文件是:Sprites/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

4

1 回答 1

0

明智地组织,我一直喜欢以一种特殊的方式来做,而您使用的结构非常适合它。

我喜欢像这样加载我的对象:

  1. 有一个级别文件(XML 或 TXT)来描述应该为 X 任务/级别加载的内容。
  2. 用循环遍历这个,每个要加载的对象都有自己的文件(很像你的 Hero.xml)。
  3. 这些中的每一个都是通过处理所有数据加载的级别管理器加载的。
  4. 绘制并继续通过关卡管理器运行关卡。

然后,很大程度上取决于您如何调用您的级别经理并实施它。我喜欢使用这些关卡配置文件以及关卡管理器,因为这样您就可以在整个关卡管理器中共享资源。因此,您可以将关卡管理器中的引用纹理与字典一起使用,并将这些纹理传递给您加载的关卡中的游戏对象,这样您就不会拥有多个加载的纹理。

根据我在 C# 中的了解,“通过引用传递的东西”往往会阻止这成为一个问题,但我来自 C++ 领域,这种针对您的关卡的内容管理是一个好主意。

至于您的播放器,您可以创建一个与您的关卡管理器接口的播放器管理器类,以便播放器可以在关卡和加载之间保持持久性。

无论如何,这就是我组织它的方式。

干杯!

于 2012-06-11T22:54:01.427 回答