Reading XML file data in an XNA game
The question of how to read XML files seems to come up multiple times every day on the AppHub forums. For most games, using the Content pipeline is overkill. Fortunately, there’s an easier way – LINQ to XML.
Say you’re working on a dungeon crawler and you’ve got an XML file that describes rooms in the dungeon:
<?xml version="1.0" encoding="utf-8" ?> <Rooms> <Room> <id>1</id> <width>10</width> <height>10</height> <locationx>2</locationx> <locationy>3</locationy> <items> <item> <id>5</id> <locationx>3</locationx> <locationy>7</locationy> </item> </items> </Room> <Room> <id>2</id> <width>5</width> <height>5</height> <locationx>15</locationx> <locationy>25</locationy> <items> <item> <id>2</id> <locationx>1</locationx> <locationy>1</locationy> </item> </items> </Room> </Rooms>
So you’ve added this file containing this data to your Content project. Make sure you change the Build Action for the file to Content and the Copy to Output Directory to Copy always.
How do you get this data into some objects that are easy to work with in your code. One simple way would be to create two classes:
public class Room { public int id { get; set; } public int width { get; set; } public int height { get; set; } public int locationx { get; set; } public int locationy { get; set; } public List<Item> items { get; set; } } public class Item { public int id { get; set; } public int locationx { get; set; } public int locationy { get; set; } }
All that needs to be done to get this data into a list of Room objects is:
using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; using Microsoft.Xna.Framework.Storage; namespace Xbox360Game1 { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; List<Room> rooms; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { System.IO.Stream stream = TitleContainer.OpenStream("Content\\Rooms.xml"); XDocument doc = XDocument.Load(stream); rooms = new List<Room>(); rooms = (from room in doc.Descendants("Room") select new Room() { id = Convert.ToInt32(room.Element("id").Value), width = Convert.ToInt32(room.Element("width").Value), height = Convert.ToInt32(room.Element("height").Value), locationx = Convert.ToInt32(room.Element("locationx").Value), locationy = Convert.ToInt32(room.Element("locationy").Value), items = (from i in room.Descendants("item") select new Item() { id = Convert.ToInt32(i.Element("id").Value), locationx = Convert.ToInt32(i.Element("locationx").Value), locationy = Convert.ToInt32(i.Element("locationy").Value) }).ToList() }).ToList(); base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); } protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); base.Draw(gameTime); } } }
The only problem with using LINQ to XML, that I haven’t figured out a way around, is that debugging the query is basically impossible. If you don’t have the query exactly right you’ll get an error and you may not know what the problem is. In my case some missing parentheses in getting the Items list set up correctly caused me some problems and it wasn’t immediately obvious why. Part of that is probably my n00bness in using LINQ. Hopefully Microsoft will come up with a way, if it doesn’t already exist, to make debugging easier.
[...] This post was mentioned on Twitter by asdeoz, Jim Perry. Jim Perry said: My longest blog post so far this year. Hopefully a sign of things to come.
http://bit.ly/efSlMO [...]
Thanks, I have looking for days for a xna tutorial like this!
how do i know about how this works?
from room in doc.Descendants(“Room”)
select new Room()
It seems your getting room which is descending down from rooms item is my guess but still confused.
The LINQ expressions are a bit more in the functional language paradigm than most C#. You’re essentially doing a select (like in SQL) on any Room element and building that up into an IEnumerable which then you simply are saying .ToList() on to get the final result that is more digestable for the game. The same goes for the items inside each room. The functional / imperative mix of C# can be quite tricky at first if you’ve never seen a functional language.
Do you know your code blocks are being heavily clipped on the right on my very wide screen? I also see lots of white space on both sides.
Hey your tutorial is just what i was looking for.
adding more to your tutorial.. how would you cycle thru the ites if you had more than one under the same level.?
for example if your xml file was something like this
1
10
10
2
3
5
3
7
8
9
3
That’s not XML, unless I’m not seeing it correctly. As for looping through the items (which I’m guessing is what you meant), you would select the items just like I do the rooms. You can then loop through them just like any other array.
sorry it was xml, the website changed it. not sure how to put xml tags in here without it been changed.
thanks i found out that it already gets all of the items if there is more then 1.
now my other question..
what if i just wanted to pick a specific room lets say room 1 instead of loading every single room how can i search for a specific room and load that one.
the way i have it is
creating a Xelement called leveldefinition
going thru rooms
foreach (var room in rooms)
checking for room i want
it so
leveldefinition = room
then i add your code just changed doc to leveldefinition..
when i run it it dosent look like it goes inside
rooms = (from room in doc.Descendants(“Room”)
i can upload my code if i knew how