问题
你想从一个XML文件中加载数据到XNA项目。你可以使用默认的.NET文件IO功能在XNA项目启动时读取文件做到这点,但这在Xbox360平台上无法工作。
你想使用内容管道将一个XML文件串行化为一个二进制文件,这样就可以在XNA项目中读取包含在这些文件中的内容了。
解决方案
在XML文件中,只需简单地将你想要加载的对象插入到<XNAContent>和<Asset>标签之间,下面的XML示例文件是一个自定义的MapData类对象:
<?xml version="1.0" encoding="utf-8"?> <XnaContent?> <Asset Type="XMLDataPLine.MapData"?> <mapName>Battle In The Middle</mapName> <numberOfCastles>8</numberOfCastles> <allies> <Item>Humans</Item> <Item>Elves</Item> <Item>Dwarves</Item> </allies> </Asset> </XnaContent>
技巧:如果你不知道如何从一个对象自动创建一个XML文件,你可以在本教程的最后学到方法。
XNA框架自带有内容导入器可以将XML文件转换为定义在XML文件中的对象。
这里因为对象已近建立了,所以你无需使用处理器,对象可以立即串行化为一个二进制文件。这里对象是自定义的MapData类,所以你需要定义一个自定义的TypeWriter和TypeReader。
注意:如果内容管道知道如何串行化/反串行化XML文件描述的对象,你就没必要编写一个新的TypeWriter或TypeReader。
图5-25显示了简图。
图5-25 当从XML文件导入对象时无需处理器。
工作原理
将一个. Xml文件导入到XNA项目中。在解决方案浏览器中,选择一个文件,它的属性显示在屏幕右下角。表面你想使用默认的XML内容导入器,选择No Processing Required表示导入器的输出不使用处理器就被串行化为一个文件。图5-26显示了最终的属性窗口。
图5-26 一个导入的XML文件的属性
如果XML文件包含一个内容管道知道如何串行化/反串行化的类对象,你可以将这个对象在LoadContent方法中加载到一个变量。但是本教程中是一个自定义类,所以你需要定义一个TypeWriter。
按照教程4-15中解释的步骤在解决方案中添加一个内容管道。这次,你无需定义处理器,只需定义MapData类 及对应的TypeWriter和TypeReader。
注意:确保添加内容管道项目的引用,解释请见教程4-15。这是必需的,这样你的主程序才可以访问到MapData类的定义,这个类定义是存储在内容管道项目中的。
定义自定义的MapData类
本例中的MapData类包含一个string,一个int和一个string的集合,将这个定义添加到内容管道项目中:
public class MapData { public string mapName; public int numberOfCastles; public List<string> allies = new List<string>(); }
技巧:验证一下包含在前面的XML文件中的数据提供了这三个变量的数据。
定义一个可以串行化MapData类对象的TypeWriter
如教程4-15所述,TypeWriter需要从对象串行化足够的数据,这样以后对象才能被TypeReader重建。和以往一样,仍需要提供 TypeReader的位置:
[ContentTypeWriter] public class MapDataTypeWriter : ContentTypeWriter<MapData> { protected override void Write(ContentWriter output, MapData value) { output.WriteObject<string>(value.mapName); output.WriteObject<int>(value.numberOfCastles); output.WriteObject<List<string>>(value.allies); } public override string GetRuntimeReader(TargetPlatform targetPlatform) { return typeof(MapDataReader).AssemblyQualifiedName; }
你表明了这个TypeWriter可以串行化MapData类对象。默认内容管道知道如何串行化一个string,一个int和一个List,所以你只需简单地将它们串行化为一个二进制文件。你传递了一个MapDataReader TypeReader的链接,下面就会定义这个链接。
定义一个可以串行化MapData类对象的TypeReader
TypeReader只是简单地创建了一个新MapData对象,读取string,int和List (按正确地顺序!),并将它们存储在MapData对象中。这个对象被返回并发送到XNA游戏项目。
class MapDataReader : ContentTypeReader<MapData> { protected override MapData Read(ContentReader input, MapData existingInstance) { MapData map = new MapData(); map.mapName = input.ReadObject<string>(); map.numberOfCastles = input.ReadObject<int>(); map.allies = input.ReadObject<List<string>>(); return map; } }
使用方法
LoadContent方法的第一行代码就可以实时从MapData对象中读取数据,第二行代码只是在代码中添加一个断点,这样让你可以检查数据是否正确:
protected override void LoadContent() { MapData loadedMap = Content.Load<MapData>("data"); System.Diagnostics.Debugger.Break(); }
注意:只要编译项目,XML文件就会被转换为一个二进制文件。当项目开始时,只调用了TypeReader从二进制文件构建了MapData对象。这意味着如果你改变了XML的内容,你必须重新编译。
代码
自定义内容管道只包含类定义,TypeWriter和TypeReader,这在教程前面已经写过了。
扩展阅读
从一个已存在的对象创建一个XNA可用的XML文件
本节解释了如何以一个XML文件存储任意类对象,然后你就可以使用默认XML导入器加载它了。首先需要添加下面的命名空间:
using System.Xml; using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;
你还需添加System. XML和Microsoft. XNA. Framework. Content. Pipeline的引用;你可以通过从Project菜单中选择Add Reference添加这些引用。
然后,要么链接到自定义内容管道,要么通过将下列代码放在项目命名空间的外面手动重定义MapData类。
namespace XMLDataPLine { public class MapData { public string mapName; public int numberOfCastles; public List<string> allies = new List<string>(); } }
如果你手动重定义类,请确保将它放在与自定义内容管道相同的命名空间中(我教程中叫做XMLDataPLine),所有的变量也是相同的。
接下来,回到XNA项目的命名空间,确保已近有了一个MapData类对象:
XMLDataPLine.MapData myMap = new XMLDataPLine.MapData(); myMap.mapName = "Battle In The Middle"; myMap.numberOfCastles = 8; myMap.allies.Add("Humans"); myMap.allies.Add("Elves"); myMap.allies.Add("Dwarves");
然后使用这个代码存储到一个XNA可用的XML文件中,叫做data . xml:
string fileName = "data.xml"; XmlWriter writer = XmlWriter.Create(fileName); IntermediateSerializer.Serialize<XMLDataPLine.MapData>(writer, myMap, fileName); writer.Close();
当你运行程序时,data . xml文件会被创建到与. exe相同的位置中。
注意:在示例中你也能找到这个代码。