Simple Step By Step XML Tutorial By: Billy Olden So you got this game and you want to add some file loading from xml in it, but just how do you go about doing that? First off why xml? o o o o It is user Friendly It can be Downloaded into silverlight via “DownloadStringAsync” Most Important, it CAN be included as Content in your Xap File meaning that it CAN be safe from people messing with it (if that’s what you want) Its easy! First lets start with a simple class: public class XMLLevel { } We want this class to do 2 things, first to load in the string values and then load and store the textures realted to it. First thing to do is to add the variables that will be present in the xml document into the class (make sure they are labeled “public”!). For this tutorial I want to load: -4 Enemy Spritesheets -2 Water Tiles -4 Variety Tiles -a Boss Texture -3 Layered Background Textures -Level To load We should then have a class that looks like this (Note: I’m using arrays but lists work as well!): public class XMLLevel { public string[] Enemies = new string[4]; public string[] WaterTiles = new string[2]; public string[] VarietyTiles = new string[4]; public string Boss; public string[] LevelBackground = new string[3]; public string LevelString; public int TimerMinutes = 2; } Now we need to be able to convert this XMLLevel class into a xml string. The 2 methods for doing this are: /// <summary> /// Turns an object into an xml File /// </summary> /// <param name="pObject">The object to be converted</param> /// <returns>the object as an xml string</returns> public static String SerializeObject(object pObject) { try { String XmlizedString = null; MemoryStream memoryStream = new MemoryStream(); XmlSerializer xs = new XmlSerializer(pObject.GetType()); XmlWriter k = XmlWriter.Create(memoryStream); xs.Serialize(k, pObject); XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray()); return XmlizedString; } catch (Exception e) { System.Console.WriteLine(e); return null; } } private static string UTF8ByteArrayToString(byte[] characters) { UTF8Encoding encoding = new UTF8Encoding(); string constructedString = Encoding.UTF8.GetString(characters,0,characters.Length); return (constructedString); } This method will turn any object you pass to it into an xml string. In this case we would be passing it a XMLLevel and thus will get a XMLLevel xml string. Now lets say that I have no idea what this string will look like. To figure that out at the beginning of your game make a new (Insert name of whatever class you are converting) and save the serialization to a string. For this example the lines of code are: XMLLevel level = new XMLLevel(); //insert anything you might not know what it looks like in xml level.Boss = "DMNAK"; level.Enemies[0] = "SADNKASLDAS"; string s = XMLLevel.SerializeObject(level); Console.Write(s); Once this prints out, copy the line and paste it into notepad and hit enter to space it out where needed and get familiar with the layout. Remember to get rid of these lines of code once you know the layout of the xml document. At this point you can create your xml document in the project (Note: If you want it in the xap file make sure you set the build action to “Content”, and if you want it to be present in the directory to set the build action to “None” and copy to output directory to “Copy if newer”). To bring it into the project the following method is needed. Note: change “XMLLevel” to the name of your class (I made it red) /// <summary> /// Load a XMLLevel from memory /// </summary> /// <param name="filetoLoad">The path to where the xml is stored</param> /// <returns>the loaded XMLLevel</returns> public static XMLLevel DeserializeObject(string filetoLoad) { XmlSerializer xs = new XmlSerializer(typeof(XMLLevel)); XMLLevel loaded = (XMLLevel)xs.Deserialize(XmlReader.Create(filetoLoad)); return loaded; } And that’s about it! If that’s all you needed then see ya! For those who are going to keep reading however, We are going to add a bit more functionality to our XMLLevel class! Since this is going to be our Level holder I don’t want it to just store the name of the file I want to load it now and once so I won’t have to do it again. So I’m going to add a few variables to this class: private private private private private Texture2D[] _LoadedEnemies = new Texture2D[4]; Texture2D[] _LoadedWaterTiles = new Texture2D[2]; Texture2D[] _LoadedVarietyTiles = new Texture2D[4]; Texture2D _LoadedBoss; Texture2D[] _LoadedLevelBackground = new Texture2D[3]; Note: These are private for a reason! When you xml Load and Save only the public variables are minipulated! So if you want anything to NOT be saved mark it as private or add an Attribute to it (discussed later). Now that we have the variables We are going to add a method that will be called after the Deserialization that will import the textures. Note: Since some of my strings may be set to the default I have to check for null parameters, make sure you do the same! private void LoadFiles() { ContentManager content = PlatformerGame.Instance.Content; //load enemies for (int i = 0; i < Enemies.Length; i++) { if (Enemies[i] != "") _LoadedEnemies[i] = content.Load<Texture2D>(Enemies[i]); } //load WaterTiles for (int i = 0; i < WaterTiles.Length; i++) { if (WaterTiles[i] != "") _LoadedWaterTiles[i] = content.Load<Texture2D>(WaterTiles[i]); } //load VarietyTiles for (int i = 0; i < VarietyTiles.Length; i++) { if (VarietyTiles[i] != "") _LoadedVarietyTiles[i] = content.Load<Texture2D>(VarietyTiles[i]); } //load boss dude if (Boss != "") _LoadedBoss = content.Load<Texture2D>(Boss); //load level background for (int i = 0; i < LevelBackground.Length; i++) { if (LevelBackground[i] != "") _LoadedLevelBackground[i] = content.Load<Texture2D>(LevelBackground[i]); } } Great! Now all of the Textures are loaded and ready to go! However, they are unreachable by any other class because they are private! You can either fix this too ways, create a public method that will return what you need OR you can create a public variable or property to return it to you. Yes I know I said that adding a public variable will cause the serialize and deserialize to look for such a variable and will throw an error for more complex types saying that it is not a serializable class, to get around this we are going to use an Attribute called XMLIgnore. What an Attribute is is basically tellng the compiler “the next thing you come across will have these properties”. In the case of XMLIgnore we are saying “the next thing you come across will be under no circumstance Serializable or Deserializable” thus we can get away with setting it to public! So for this example I’m going to make public properties and set the properties to read only. [XmlIgnore] public Texture2D[] LoadedEnemies { get { return _LoadedEnemies; } } [XmlIgnore] public Texture2D[] LoadedWaterTiles { get { return _LoadedWaterTiles; } } [XmlIgnore] public Texture2D[] LoadedVarietyTiles { get { return _LoadedVarietyTiles; } } [XmlIgnore] public Texture2D LoadedBoss { get { return _LoadedBoss; } } [XmlIgnore] public Texture2D[] LoadedLevelBackground { get { return _LoadedLevelBackground; } } Properties are members that provide a flexible mechanism to read, write, or compute the values of variables. In this case we are setting this property to be read only, meaning that when this is called you can only get the texture but not set it (which is good because we only want this set once!). Another cool use for properties is that it can compute overused computations and turn it into a more reader friendly code. Example: public bool IsInWater { get { return TriggerVolume == PlayerVolume.Water; } } Now Instead of having to write TriggerVolume == PlayerVolume.Water everywhere I can just use IsInWater. A more complex example is: public Rectangle BoundingRectangle { get { int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X; int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y; return new Rectangle(left, top, localBounds.Width, localBounds.Height); } } This way the “Bounding Rectangle” that is returned will always be up to date with the newest Position,sprite, and local bounds information without having to reset it every time one of those values have changed! That’s about it for this tutorial I’ll add the full class at the end of this document for those to see! Good Luck Programming! public class XMLLevel { public string[] Enemies = new string[4]; public string[] WaterTiles = new string[2]; public string[] VarietyTiles = new string[4]; public string Boss; public string[] LevelBackground = new string[3]; public string LevelString; public int TimerMinutes = 2; private Texture2D[] _LoadedEnemies = new Texture2D[4]; [XmlIgnore] public Texture2D[] LoadedEnemies { get { return _LoadedEnemies; } } private Texture2D[] _LoadedWaterTiles = new Texture2D[2]; [XmlIgnore] public Texture2D[] LoadedWaterTiles { get { return _LoadedWaterTiles; } } private Texture2D[] _LoadedVarietyTiles = new Texture2D[4]; [XmlIgnore] public Texture2D[] LoadedVarietyTiles { get { return _LoadedVarietyTiles; } } private Texture2D _LoadedBoss; [XmlIgnore] public Texture2D LoadedBoss { get { return _LoadedBoss; } } private Texture2D[] _LoadedLevelBackground = new Texture2D[3]; [XmlIgnore] public Texture2D[] LoadedLevelBackground { get { return _LoadedLevelBackground; } } private void LoadFiles() { ContentManager content = PlatformerGame.Instance.Content; //load enemies for (int i = 0; i < Enemies.Length; i++) { if (Enemies[i] != "") _LoadedEnemies[i] = content.Load<Texture2D>(Enemies[i]); } //load WaterTiles for (int i = 0; i < WaterTiles.Length; i++) { if (WaterTiles[i] != "") _LoadedWaterTiles[i] = content.Load<Texture2D>(WaterTiles[i]); } //load VarietyTiles for (int i = 0; i < VarietyTiles.Length; i++) { if (VarietyTiles[i] != "") _LoadedVarietyTiles[i] = content.Load<Texture2D>(VarietyTiles[i]); } //load boss dude if (Boss != "") _LoadedBoss = content.Load<Texture2D>(Boss); //load level background for (int i = 0; i < LevelBackground.Length; i++) { if (LevelBackground[i] != "") _LoadedLevelBackground[i] = content.Load<Texture2D>(LevelBackground[i]); } } /// <summary> /// Turns an object into an xml File /// </summary> /// <param name="pObject">The object to be converted</param> /// <returns>the object as an xml string</returns> public static String SerializeObject(object pObject) { try { String XmlizedString = null; MemoryStream memoryStream = new MemoryStream(); XmlSerializer xs = new XmlSerializer(pObject.GetType()); XmlWriter k = XmlWriter.Create(memoryStream); //XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); xs.Serialize(k, pObject); //memoryStream = (MemoryStream)k.BaseStream; XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray()); return XmlizedString; } catch (Exception e) { System.Console.WriteLine(e); return null; } } /// <summary> /// Load a XMLLevel from memory /// </summary> /// <param name="filetoLoad">The path to where the xml is stored</param> /// <returns>the loaded XMLLevel</returns> public static XMLLevel DeserializeObject(string filetoLoad) { XmlSerializer xs = new XmlSerializer(typeof(XMLLevel)); XMLLevel loaded = (XMLLevel)xs.Deserialize(XmlReader.Create(filetoLoad)); loaded.LoadFiles(); return loaded; } private static string UTF8ByteArrayToString(byte[] characters) { UTF8Encoding encoding = new UTF8Encoding(); string constructedString = Encoding.UTF8.GetString(characters,0,characters.Length); return (constructedString); } }