Search code examples
c#xmloopxnaxna-4.0

Loading level data from xml file in XNA


I am creating a tile-based game in XNA and will be loading the level information from xml files. I have no problem with loading xml data but I would like feedback on the approach I'm thinking of using for reading the xml file.

The code has a class hierarchy such that:

A Level class contains:
- a collection of TileLayers
- a collection of Tilesets

A TileLayer class contains
- a collection of Tiles

A Tileset class contains:
- a collection of TilsetTiles

A TilesetTile class contains:
- a TileId
- a collection of TileProperties
- a TileRectangle

Each of the above classes requires some information from the xml level file.

When I load a level I would like to simply call Level.Load();

My intention is that each class will have a Load() method that will:
1. Load any specific info it needs from the xml file.
2. Call Load() on its children.

The problem I see is that the code for processing the xml will be scattered around in different files making changes difficult to implement (for instance if I decide to encrypt the xml file), and no doubt breaks several aspects of the SOLID principles.

I have this idea that I could create an xmlLevelReader class whose sole purpose is to read an xml level file. This class could then expose methods that can be called from the Load() method in each of the classes described above.

For example the TileLayer class Load() method could call xmlLevelReader.GetTiles() which would return an IEnumerable<Tile>

Do you think this approach will work?
Can you foresee any difficulties?
Is this approach too simplistic/complicated?
Any constructive criticism welcomed!

Thanks


Solution

  • Based on your comment, I see that you are using Tiled Map Editor. This lead me to suggest that you use TiledLib. Here is a brief explanation of how you can get up and running with importing your .tmx files for use in game.

    Content Pipeline Overview

    File -> Content Import -> Content Process -> Content Write -> .xnb -> ContentRead -> Game Object

    TiledLib

    TiledLib only handles the ContentImporter part of the above diagram. It will essentially handle reading the .tmx XML and allow you to process the data into whatever objects you need at run time. Fortunately, the TiledLib author has provided a Demos section in the download as well.

    Basic Tiled Map Processor Demo

    • BasicDemo main game project which contains the ContentManager.Load call.
    • BasicDemoContent project which has the .tmx file from Tiled
    • BasicDemoContentPipeline project which has the ContentProcessor
    • TiledLib which has the ContentImporter

    You really only need to worry about how the ContentProcessor works because TiledLib handles all the importing for you. Although I do suggest looking through the different classes to understand how it is deserializing the XML (for educational purposes).

    The example ContentProcessor in the Basic Demo project takes in a MapContent object from the ContentImporter (TiledLib) and outputs a DemoMapContent object which is serialized to .xnb at build time and deserialized to a Map object at run time. Here are the classes that represent the map after being processed completely:

    [ContentSerializerRuntimeType("BasicDemo.Map, BasicDemo")]
    public class DemoMapContent
    {
        public int TileWidth;
        public int TileHeight;
        public List<DemoMapLayerContent> Layers = new List<DemoMapLayerContent>();
    }
    
    [ContentSerializerRuntimeType("BasicDemo.Layer, BasicDemo")]
    public class DemoMapLayerContent
    {
        public int Width;
        public int Height;
        public DemoMapTileContent[] Tiles;
    }
    
    [ContentSerializerRuntimeType("BasicDemo.Tile, BasicDemo")]
    public class DemoMapTileContent
    {
        public ExternalReference<Texture2DContent> Texture;
        public Rectangle SourceRectangle;
        public SpriteEffects SpriteEffects;
    }
    
    • A Map contains a tile width, tile height, and a list of MapLayers.
    • A MapLayer contains a width, a height, and a list of Tiles.
    • A MapTile contains a texture, a source rectangle (proper rectangle in the tileset), and optional sprite effects (I've never used any).

    How It's Made

    I suggest reading the comments of the ContentProcessor to understand what is happening, but in brief, this is the basics:

    • Load texture data for tile set
    • Get source rectangles for each tile from within the texture
    • Iterate over all layers of the map
    • For each layer, iterate over all tiles
    • For each tile, get the proper texture and source rectangle
    • Assign all data from the input to the output properly

    Caveats

    As long as you stick to basic types (vague, I know), you also do not need to worry about ContentWriter and ContentReader parts of the content pipeline. It will automatically serialize and deserialize the .xnb files at build and run time, respectively. See a question I asked about this: here.

    Also, you'll notice that if you're using object layers from within Tiled, the demo does not show you how to process those. TiledLib does properly import them, you just need to pull the data out and stick it in your proper classes. I'll try to edit this answer later with an example of how to do that.