There’s a couple methods to working with XML documents in the XNA games. First, you can just serialize and deserialize your class files at run-time.
Why use XML files for game development?
By saving data outside of your executable, you can keep your data and code separate. With external data, you can make updates to the files without needing to write more code. You can also easily update and maintain the files without rebuilding your projects.
What’s the difference between using XnaContent and Serialization methods?
Run-time vs. Design-time Validation
The biggest difference between the two methods, is that using XnaContent XML files allow you to catch errors when you build your project rather than getting XML errors at run-time.
Of course, you could build XSD documents to validate your serialized class files or just trust that however the files were created, that they were created correctly.
XNB vs. XML
The second difference is that the XnaContent XML documents will be converted and compressed to XNB format and your serialized files will still be XML documents.
Okay, let’s get coding
First, if you prefer instructions on how to do this by experts, then you should download the Authoring Particle Systems Using XML and the Content Pipeline tutorial from XNA.com.
Another good read is the “Introduction to XNA XML Content Files in XNA Game Studio 2.0” article written by Nick Gravelyn.
Step 1: Our Content class (Content Pipeline Project)
First, let’s define what we will be loading. In this example, I’m going to code a XnaContent document to store some settings for when I render a planet. Right now, I only need three things: the planet name, the asset name for the heightmap, and the asset name for the skydome.
In your Content Pipeline project, create a new class and let’s call it PlanetSurfaceMapData. We will define it as such:
public class PlanetSurfaceMapContent { public string TerrainAssetName; public string SkyAssetName; public string Name; }
Step 2: Our ContentTypeWriter class (Content Pipeline Project)
Next, let’s start on our ContentTypeWriter. Right click in your Solution Explorer and choose “Add->New Item…”. Choose “Content Type Writer” from the list of templates. This template fills out nearly everything that we need for our writer, but we’ll need to update a few parts.
In the section that says “using TWrite =”, let’s update it to reference our new PlanetSurfaceMapContent class. You’ll need to fill out the full class name with namespace here.
Next, let’s move down into the Write method. We need to write the three values from our XnaContent XML document to the XNB format, so let’s update the function to look like this:
protected override void Write(ContentWriter output, TWrite value) { output.Write(value.Name); output.Write(value.TerrainAssetName); output.Write(value.SkyAssetName); }
Finally, we need to update the GetRuntimeReader to reference the class that we’ll be building in the next step.
Step 4: Our Data class (Game Library Project)
This class will be a copy of the PlanetSurfaceMapContent class that we’ll name PlanetSurfaceMapData . This part is probably wrong by me, but it’s how I’ve been doing it. This class is the receiver of the content and you could probably add to this data class to have more than just the fields, such as methods, etc.
I separate the two classes into Data (run-time) and Content (design-time) classes so I can have just the fields in the Content class and the Data class to be treated like any normal class within my game.
Don’t forget to update your namespaces if you copy the file. Also, if you copy the file, you’ll need to add a GetRuntimeType method to the ContentTypeWriter to specify this Data class as it’s runtime class.
Step 5: Our ContentTypeReader class (Game Library Project)
To start, let’s use the built-in template to generate some of the code for us. Right click on your Solution Explorer, choose “Add->New Item..” and then choose the “Content Type Reader” template.
Update the “using TRead =” statement to reference our PlanetSurfaceMapData class.
In the Read function, let’s create a new instance and then read in the data.
protected override TRead Read(ContentReader input, TRead existingInstance) { existingInstance = new TRead(); existingInstance.Name = input.ReadString(); existingInstance.TerrainAssetName = input.ReadString(); existingInstance.SkyAssetName = input.ReadString(); return existingInstance; }
Almost done…
Step 6: Making our XnaContent XML Document
There’s two ways to make your first document. If the file is just simple data such as string and numbers, then you can probably figure it out. However, if your Content class contains complex data, then you can use the intermediate serializer.
The intermediate serializer will create a XnaContent file for your data. Here’s an example of it. You can even use this as part of your game/scene editor to generate XnaContent documents from your classes at run-time. (However, this creates XML documents not XNB documents).
Here’s the example code (from the above XNA sample link) modified to work with my classes:
using System.Xml; using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate; namespace MyXmlCreatorTest { public static class Program { public static void Main() { object testPlanet = new PlanetSurfaceContent(); XmlWriterSettings xmlSettings = new XmlWriterSettings(); xmlSettings.Indent = true; using (XmlWriter xmlWriter = XmlWriter.Create("test.xml", xmlSettings)) { IntermediateSerializer.Serialize(xmlWriter, testPlanet, null); } } } }
Or, you can just do it by hand, by right clicking on your Solution Explorer of your Game’s Content project and choose “Add->New Item…” and choose “XML File”. This will create a template XnaContent XML document that we can fill out.
The important things to remember is that you need to provide the full class name as the Asset Type value. Within the <Asset> node, you can define each field by enclosing the field name as an XmlElement and the value within the element tags.
Below is a example of my XnaContent XML document:
<?xml version="1.0" encoding="utf-8" ?> <XnaContent> <Asset Type="GameEngine.ContentPipeline.PlanetSurface.PlanetSurfaceMapContent"> <TerrainAssetName>Models\\Heightmaps\\DemoMap2\\heightmap</TerrainAssetName> <SkyAssetName>Models\\Skys\\Space</SkyAssetName> <Name>Test Planet</Name> </Asset> </XnaContent>
Step 7: Finally, we get to the game code
After all that, we’re almost done. Now we can load the class using the Content Manager.
PlanetSurfaceMapData currentSceneData =
contentManager.Load<PlanetSurfaceMapData>(@"Xml\Planets\TestPlanet");
That’s it. Here’s my planet data loaded into my Scene Editor.
