3 min read

Extending the Content Pipeline - Part 5

Level Files

Well, we are ready to move on to the processor, but first, we need to determine the file format that we are reading in. We have avoided this up until now because the importer has just read everything into a string and passed it along. But now we will need to know what type of data we’re reading so that we can create a level out of it. So the file format we are going to use for this tutorial is this: On the first line of the file is the number of rows in the level grid, the second line is the number of columns, and then the actual level data is included with each row in the level grid being placed on its own line with each item in the row separated by a space. So for example, a file might look like this:

5
6
1 1 1 1 1 1
1 0 0 0 0 1
1 2 0 0 0 1
1 0 0 2 0 1
1 1 1 1 1 1

So our processor will need to be able to take a string that looks like this and turn it into a level.

Building the Processor

Once again, we can use the template that XNA supplies to create our content processor. To do this, right-click on the LevelContentPipelineExtension project and choose Add > New Item from the menu, as we have done before. In the Add New Item dialog that appears, choose the Content Processor template and give the processor a name. Remember, this name needs to be the same name you gave it in the importer class. I’ve called mine “LevelProcessor.cs”. Click OK to create the class.

The first thing we will need to do is near the top, you will find two lines of code that say:

using TInput = System.String;
using TOutput = System.String;

These are the input and output types for the processor. The input type (TInput) will be whatever the importer creates, which in our case is a string, so we don’t need to make any changes on the first line. Our output, however, will be a Level object, so change the second line to say:

using TOutput = LevelLibrary.Level;

Once this is done, go down to the line that says:

[ContentProcessor(DisplayName = "LevelContentPipelineExtension.LevelProcessor")]

and change the display name to whatever name you would like. I’ve changed the line in mine to say:

[ContentProcessor(DisplayName = "Level Processor")]

The last thing we need to is to actually do the importing. Replace the stuff in the Process() method with the following:

string[] lines = input.Split(new char[] { '\n' });
int rows = Convert.ToInt32(lines[0]);
int columns = Convert.ToInt32(lines[1]);

int[,] levelData = new int[rows, columns];
for (int row = 0; row < rows; row++)
{
    string[] values = lines[row + 2].Split(new char[] { ' ' });
    for (int column = 0; column < columns; column++)
        {
            levelData[row, column] = Convert.ToInt32(values[column]);
        }
    }

return new LevelLibrary.Level(levelData);

This will read in the data like we described at the top of this page, and turn it into a Level object. This should complete the entire code for the processor, which should look something like the following:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;

using TInput = System.String;
using TOutput = LevelLibrary.Level;

namespace LevelContentPipelineExtension
{
    [ContentProcessor(DisplayName = "Level Processor")]
    public class LevelProcessor : ContentProcessor<TInput, TOutput>
    {
        public override TOutput Process(TInput input, ContentProcessorContext context)
        {
            string[] lines = input.Split(new char[] { '\n' });
            int rows = Convert.ToInt32(lines[0]);
            int columns = Convert.ToInt32(lines[1]);

            int[,] levelData = new int[rows, columns];
            for (int row = 0; row < rows; row++)
            {
                string[] values = lines[row + 2].Split(new char[] { ' ' });
                for (int column = 0; column < columns; column++)
                {
                    levelData[row, column] = Convert.ToInt32(values[column]);
                }
            }

            return new LevelLibrary.Level(levelData);
        }
    }
}