Posts tagged ‘LINQ’

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.