问题

你想从一个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显示了简图。

image

图5-25 当从XML文件导入对象时无需处理器。

工作原理

将一个. Xml文件导入到XNA项目中。在解决方案浏览器中,选择一个文件,它的属性显示在屏幕右下角。表面你想使用默认的XML内容导入器,选择No Processing Required表示导入器的输出不使用处理器就被串行化为一个文件。图5-26显示了最终的属性窗口。

image

图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相同的位置中。

注意:在示例中你也能找到这个代码。