Hardcoding vs. Data files
In our previous lecture, we showed how to create animations in XML, but we were somehwat sloppy. In addition to creating animation variables in the main class (bad), we also hardcoded the name of the animation file, and size of the animation in our source code (worse!). If our artist wants to change the number of frames of animation, or tweak the width of a character, they will need to go to the code. Both artists and engineers get very grumpy when the artists are forced to hack at the actual source code. The proper way to do this is to have all of the information about which animations you want to use, and the properties of each animation, stored in a data file (or data files), which can be read in a runtime.
There are a number of ways to store data -- you can create your own custom file format (and then create an importer to import that data), or you can use a standard file format, like XML, and use a predefined importer to read in your code. XNA has a reasonable importer for XML, which we will be investigating now
XML Data
XML is a standard, flexible file format that is (more or less) human readable, and easy to work with. It can be edited in almost any text editor (many of which have syntax highlighting, auto-completion of tags, and error checking built in). The XNA content pipeline has facilities for (mostly) automatically filling data structures in from XML files.
XNA and XML
XNA offers a content pipeline that includes loading XML files.
Loading Simple Strings in XNA
First, we will look at the simplist possible way to read in XML files from XNA -- reading in a single string (OK, so storing a single string in a data file is pretty silly, but this is for illustrative purposes).
- First, right-click on the content project, and select Add -> New Item .... When the dialog box comes up, select XML file. You probably want to change the name field (at the very bottom of the dialog box) from XMLFile1.xml to something more useful before you click add. We'll use Name.xml in this example
- Left-click on the the file Name.xml in content project to bring up
an editor. The file will look like the following:
<?xml version="1.0" encoding="utf-8" ?> <XnaContent> <!-- TODO: repace this Asset with your own XML asset data. --> <Asset Type="System.String"></Asset> </XnaContent>
This XNA file stores a single string (which is currently empty). We will modify it to have the standard "Hello World" as follows:<?xml version="1.0" encoding="utf-8" ?> <XnaContent> <!-- TODO: repace this Asset with your own XML asset data. --> <Asset Type="System.String">Hello World</Asset> </XnaContent>
- Now we are all ready to load up the content. In the LoadContent()
method of your game, add in the line:
String testString = Content.Load
Put in a breakpoint at this line, step over it, and examine the value of testString. Voila, we have loaded data from and XML file.("Name");
Loading Collections in XNA
OK, so what if we wanted something more interesting -- like, say, and array of elements? If we use the XML file IntList.xml:<?xml version="1.0" encoding="utf-8" ?> <XnaContent> <Asset Type="int[]"> 4 6 7 11 </Asset> </XnaContent>and then in our LoadContent method we use the line
int[] data = Content.LoadThen we have loaded up our array of integers. Want something even more fun? How about a dictionary that maps strings to recctangles? Create an XML file called DictionaryTest.xml :("IntList");
<?xml version="1.0" encoding="utf-8" ?> <XnaContent> <Asset Type="System.Collections.Generic.Dictionary[System.String, Microsoft.Xna.Framework.Rectangle]"> <Item> <Key>firstbox</Key> <Value>0 0 10 20</Value> </Item> <Item> <Key>secondbox</Key> <Value>4 5 10 20</Value> </Item> </Asset> </XnaContent>Note that you need to use the full names, with all the namespaces (Microsoft.Xna.Framework.Rectangle) to specify types in the .xml file (there is no "using" analog in the XNA xml file format) Now, you can load in your LoadContent method as:
Dictionary<String,Rectangle> dict = Content.Load<Dictionary<String,Rectangle>>("DictionaryTest");
Custom Classes
What if we wanted to load our own custom classes into XNA? That gets a little bit more complicated, since the content importer needs to know the format of our classes in order to load them in. What goes on behind the scenes is that when your code is compiled, the .xml documents in your Content project are also compiled into an intermediate form for quicker loading at runtime. For that whole process to work correctly, you need to tell the Content project about the data format of the classes you need to load. The steps are as follows:- In the solution explorer, right-click on the solution, and select Add -> New Project .... A dialog box will come up, select Windows Game Library. Give it a resonable name (The default will be WindowsGamek, for some k)
- Add a class to the new library. This is is the data class that we are going to fill in from the the XML file. You can name it anything you like -- for now, call it AnimData. (When you create a new library, you get an empty class added for free -- you can go ahead an use this class if you like) Add whatever public members you would like -- for now, we will just add a string for the name of the sprite sheet, and an int for the width of the animation in the sprite sheet. You probably also want to change the default namespace from WindowsGameLibrary1 to something more appropriate for your project
namespace MyGame { public class AnimData { String spriteSheet; int width; } }
<?xml version="1.0" encoding="utf-8" ?> <XnaContent> <Asset Type="Full class name (with namespace)"> <Variable Name1>Value</Variable Name1> <Variable Name2>Value</Variable Name2> </Asset> </XnaContent>So, for a file describing the above animation, with the values "coin" and 50, is:
<?xml version="1.0" encoding="utf-8" ?> <XnaContent> <Asset Type="MyGame.AnimDate"> <spriteSheet>coin</spriteSheet> <width>50</width> </Asset> </XnaContent>
In-Class Project
Time to get your fingers dirty. You are going to create a quick game with an animation, using XML serialization.- Create a new game project
- Add an Animation class, as describe in Last week's notes
- Add a new library project to describe animation data, as above
- Add a new xml file that decribes a single animation
- Add code to read in the XML file, and then use that data to read in an animation, and play it back
- Add code to read in the XML file, and then use that data to read in an animation, and play it back
- Once that works, modify your XML and game code so that the XML file stores a dictionary that maps strings (animation names) to animation data. Read in the dictionary, the go through all of the elements of the dictionary and create an animation for each one. Finally, add code to display all animations.