【问题标题】:Loading level data from xml file in XNA从 XNA 中的 xml 文件加载级别数据
【发布时间】:2012-10-19 21:02:52
【问题描述】:

我正在 XNA 中创建一个基于图块的游戏,并将从 xml 文件加载关卡信息。我在加载 xml 数据方面没有问题,但我想就我正在考虑使用的读取 xml 文件的方法提供反馈。

代码具有这样的类层次结构:

一个Level类包含:
- TileLayers 的集合
- Tilesets 的集合

TileLayer 类包含
- 瓷砖集合

一个 Tileset 类包含:
- TilsetTiles 的集合

一个 TilesetTile 类包含:
- 一个 TileId
- TileProperties 的集合
- 一个 TileRectangle

上述每个类都需要一些来自 xml 级别文件的信息。

当我加载一个关卡时,我想简单地调用 Level.Load();

我的意图是每个类都有一个 Load() 方法,它将:
1. 从 xml 文件加载所需的任何特定信息。
2. 对其子级调用 Load()。

我看到的问题是用于处理 xml 的代码将分散在不同的文件中,这使得更改难以实施(例如,如果我决定加密 xml 文件),无疑会破坏 SOLID 原则的几个方面.

我有这样的想法,我可以创建一个 xmlLevelReader 类,其唯一目的是读取 xml 级别文件。 然后,此类可以公开可以从上述每个类中的 Load() 方法调用的方法。

例如,TileLayer 类的 Load() 方法可以调用 xmlLevelReader.GetTiles(),这将返回一个 IEnumerable<Tile>

您认为这种方法可行吗?
你能预见到任何困难吗?
这种方法是否过于简单/复杂?
欢迎任何建设性的批评!

谢谢

【问题讨论】:

  • 我真诚地建议考虑使用Tiled Map Editor 并使用TiledLib 导入数据。
  • 如果您想使用自己的模型,为什么不使用本机 .NET 序列化机制呢?
  • @Justin 我正在使用 Tiled Map Editor,但从不知道 TiledLib 的存在,所以谢谢!你自己用过 TiledLib 吗?如果是这样,也许您可​​以发布一个答案,给出一些关于使用它和使用关卡文件中的信息“膨胀”类的指示?此外,内容管道上的任何指针(或指向引用的链接)以及它应该/不应该如何与 TiledLib 一起使用都会有很大的帮助。谢谢
  • @millimoose 我对 .Net 中的序列化还不是很熟悉,我怀疑贾斯汀的回应意味着我这次不需要使用它,但我会记住它以用于其他用途。也许我可以用它来保存游戏进度。谢谢。

标签: c# xml oop xna xna-4.0


【解决方案1】:

根据您的评论,我了解到您正在使用平铺地图编辑器。这导致我建议您使用 TiledLib。下面简要说明了如何开始并运行导入 .tmx 文件以在游戏中使用。

内容管道概述

文件 -> 内容导入 -> 内容处理 -> 内容写入 -> .xnb -> ContentRead -> 游戏对象

TiledLib

TiledLib 仅处理上图中的ContentImporter 部分。它本质上将处理读取 .tmx XML 并允许您将数据处理为运行时所需的任何对象。幸运的是,TiledLib 作者也在下载中提供了一个 Demos 部分。

基本平铺地图处理器演示

  • BasicDemo 包含 ContentManager.Load 调用的主游戏项目。
  • BasicDemoContent 具有 Tiled 的 .tmx 文件的项目
  • BasicDemoContentPipeline 具有ContentProcessor 的项目
  • TiledLib 其中有 ContentImporter

您真的只需要担心ContentProcessor 的工作原理,因为 TiledLib 会为您处理所有导入。尽管我确实建议查看不同的类以了解它如何反序列化 XML(出于教育目的)。

Basic Demo 项目中的示例ContentProcessorContentImporter (TiledLib) 中获取一个MapContent 对象并输出一个DemoMapContent 对象,该对象在构建时序列化为.xnb 并反序列化为@987654334 @对象在运行时。以下是完全处理后代表地图的类:

[ContentSerializerRuntimeType("BasicDemo.Map, BasicDemo")]
public class DemoMapContent
{
    public int TileWidth;
    public int TileHeight;
    public List<DemoMapLayerContent> Layers = new List<DemoMapLayerContent>();
}

[ContentSerializerRuntimeType("BasicDemo.Layer, BasicDemo")]
public class DemoMapLayerContent
{
    public int Width;
    public int Height;
    public DemoMapTileContent[] Tiles;
}

[ContentSerializerRuntimeType("BasicDemo.Tile, BasicDemo")]
public class DemoMapTileContent
{
    public ExternalReference<Texture2DContent> Texture;
    public Rectangle SourceRectangle;
    public SpriteEffects SpriteEffects;
}
  • Map 包含平铺宽度、平铺高度和MapLayers 列表。
  • MapLayer 包含宽度、高度和Tiles 的列表。
  • MapTile 包含纹理、源矩形(图块集中的正确矩形)和可选的精灵效果(我从未使用过)。

它是如何制作的

我建议阅读 ContentProcessor 的 cmets 以了解正在发生的事情,但简而言之,这是基础知识:

  • 为图块集加载纹理数据
  • 从纹理中获取每个图块的源矩形
  • 遍历地图的所有层
  • 对于每一层,遍历所有图块
  • 对于每个图块,获取正确的纹理和源矩形
  • 将所有数据从输入正确分配到输出

注意事项

只要你坚持基本类型(模糊,我知道),你也不必担心内容管道的ContentWriterContentReader 部分。它将分别在构建和运行时自动序列化和反序列化 .xnb 文件。请参阅我提出的一个问题:here

此外,您会注意到,如果您在 Tiled 中使用对象图层,该演示不会向您展示如何处理这些图层。 TiledLib 确实正确地导入了它们,您只需将数据提取出来并将其粘贴到适当的类中。稍后我将尝试通过一个示例来编辑此答案。

【讨论】:

  • 感谢您抽出宝贵时间在这里详细介绍。我会接受这个答案,因为虽然它没有回答我关于我想到的具体方法的问题,但它确实提供了一个很好的替代方法,并有足够详细的解释让我启动并运行。即使我选择不使用 TiledLib,查看代码和 Content Pipeline 演示也会非常有用。
【解决方案2】:

如果您只想加载 XML 而不操作数据,您可以使用内置的 XNA Content Serializer。

本质上,您定义一个映射到您的 xml 格式的类,然后将 XML 读入该类的实例。

例如。这里我定义了我要加载的类:

SpriteRenderDefinition.cs

我选择了这个,因为它有嵌套类,就像你描述的那样。请注意,它会进入 XNA 解决方案的 ContentDefinitions 项目。

现在这里是填写 SpriteRenderDefinition 内容的 xml 文件:

Sprite.xml

该 XML 的格式直接映射到 SpriteRenderDefinition 的成员名称。

最后,在运行时将 XML 数据实际加载到实际对象中的代码非常简单:

SpriteRenderDefinition def = GameObjectManager.pInstance.pContentManager.Load<SpriteRenderDefinition>(fileName);

调用该行后,您将拥有一个 SpriteRenderDefintion 对象,其中填充了 XML 文件的所有内容!这就是我写的所有代码。其他一切都内置在 XNA 中。如果你花一个小时左右的时间来弄清楚它真的非常漂亮和有用!

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多