Simple Step By Step XML Tutorial

advertisement
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);
}
}
Download