I put this post up due to Ben Carter’s post on generic C# XML serialisation and I thought this post is complimentry. To read his article go here: /2011/07/02/the-blunt-but-useful-instrument-that-is-xml-serialisation-in-c/
The XNA XML subsystem I find is woefully under documented. It’s not up to Microsoft’s usual standards.
So the concept of the XML system, is that XNA will map the XML to a object contained inside your code project/assembly. So you can define a class, then inside your content project you add a new XML file which members map one to one to the members on the class. This subsystem uses reflection to set the values.
The XML approach offers two nice features. the first being it will automatically produce compile time errors to alert you of the fact if one of your XML elements does not match up the corresponding class. This makes it easy to track down problems when you change your code, and your data needs to be updated. The other advantage of these errors is you can setup a build server that checks each new revision for any errors and automatically alert the development team with an email if any errors are introduced. This can save hours of hunting down errors that are introduced.
The other advantage to the XNA XML system is the data is converted to a binary file during the compile step, this makes the data much more compressed then in its native XML format making it much nicer for platforms such as the Windows Phone 7 which have potentially limited capacity.
So the first thing when using the system is that your classes must be contained in a separate project to both the main game project and the content project. The reason for this is the content project needs to be aware of the classes for its validation stage of the XML and the main game project also must be aware. You cannot have circular dependencies in .NET projects so therefore a third project must be introduced. You can then setup a project reference in both the main game project and the content project to the data project.
Now to the undocumented stuff which drove me crazy.
The XML serialisation system in XNA is very sophisticated, much more so then the Microsoft Documentation gives it credit for.
The first concept I want to introduce is containers of objects.
Say you want to serialise a series of sub nodes like so:
/// /// The world contains all the destinations that users are able to visit on their journey through a game. /// public class WorldMap { /// /// Gets or sets the nodes in the world the user is able to visit. /// public WorldMapNode[] WorldNodes { get; set; } } /// /// A node in the world which represents locations that the user can visit. /// public class WorldMapNode { /// /// Initializes a new instance of the WorldMapNode class. /// public WorldMapNode() { } /// /// Gets or sets the name of the world node that the user can visit. /// public string Name { get; set; } }
So you have a series of WorldNode’s contained inside your WorldMap. I had to tinker around with the XML format to work out how to get the sub-elements to work. Here is the end result:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?xml version="1.0" encoding="utf-8" ?> <XnaContent> <Asset Type="MyGame.Data.World.WorldMap"> <WorldNodes> <Item> <Name>Start</Name> </Item> <Item> <Name>End</Name> </Item> <Item> <Name>Cobblestone</Name> </Item> <Item> <Name>Inbetween</Name> </Item> </WorldNodes> </Asset> </XnaContent> |
The Item element denotes an additional object to be created inside the array or container. Then you must as discussed before provide xml elements to match all public properties exposed by the child class. So for instance in our example above we had to expose the Name for each WorldNode item.
There was one scenario even more complex then this. Lets say we had a container inside a class containing a reference a interface or abstract base class. We want to be able to define that the Item is of the type of a derived class. XNA XML serialisation can also handle this scenario also. On the Item tag you need to define the type attribute containing the Type you wish to instantiate.
For example, if we had inside a GuiScreen a series of GuiElements we wanted to construct (such as Buttons, TextBoxes etc) then we could use the following XML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?xml version="1.0" encoding="utf-8" ?> <XnaContent> <!-- TODO: replace this Asset with your own XML asset data. --> <Asset Type="MyGame.Data.Gui.GuiScreenData"> <TopLeft>0 0</TopLeft> <Size>0 0</Size> <Rotation>0</Rotation> <Name>Main Menu</Name> <Children> <Item Type="MyGame.Data.Gui.GuiTextBoxData"> <TopLeft>20 0</TopLeft> <Size>20 20</Size> <Rotation>0</Rotation> <Name></Name> <Text>Hello World</Text> <FontPath>Screens/Assets/Fonts/Arial</FontPath> </Item> </Children> </Asset> </XnaContent> |
As you can see above we are making a TextBox for one of the GuiElements. This feature inside the XNA XML is not documented inside any of the XNA documentation and I found it purely through chance attempting to get the above scenario to function properly.
You can then use the ContentManager provided by .NET to import the GuiScreen for example.
1 | GuiScreen screen = Content.Load<GuiScreen>("Screens/MainMenu"); |
I hope this helps anyone using XNA out there.