Tower Defence Engine

 

Pipeline

Page history last edited by AP Erebus 2 yrs ago

Custom Content Pipeline Info

 

 

In this section I will briefly give examples on code and how I have written a custom content pipeline to handle different custom file (like a level file).

 

Level Pipeline

 

I am writing all my level in an XML file that look like this:

 

<?xml version="1.0" encoding="utf-8" ?>
<Level>
<!-- This the name of the level/map.-->
<Name>Test</Name>
<!--This is the grid the will be used to load the tiles.-->
<Grid>
<row>00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
<row>00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00</row>
</Grid>
<!--The two paths that the critters can travel along to. The first points the last points must correspond with the entrys and exits.-->
<Paths>
<path1>01,01;01,02;</path1>
<path2>03,14;12,10;</path2>
</Paths>
<!--The two entry points-->
<Entries>
<entry1>0,2</entry1>
<entry2>12,14</entry2>
</Entries>
<!--The two exits-->
<Exits>
<exit1>0,29</exit1>
<exit2>32,0</exit2>
</Exits>
</Level>

 

I decided that people could change the level by editing the XML file during play... and we don't want that!

Thus a pipeline was written to change the file into a binary .xnb format the people couldn't edit.

 

Step 1: Write a custom importer.

Easy right? Wrong... I had issues with this class but they were all my stupid coding fault, so I can live with that!

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
namespace TD_Pipeline.Level
{
[ContentImporter(".tdl")]
public class TDP_LevelImporter : ContentImporter<TDP_LevelDescription>
{
public override TDP_LevelDescription Import(string filename, ContentImporterContext context)
{
TDP_LevelDescription description = new TDP_LevelDescription();
int currentRow = 0;
FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
XmlReader reader = new XmlTextReader(stream);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.LocalName.Equals("Name"))
{
description.Name = reader.ReadString();
}
if (reader.LocalName.Equals("row"))
{
parseRow(reader.ReadString(), currentRow, ref description);
currentRow++;
}
if (reader.LocalName.Equals("path1"))
{
parsePath(reader.ReadString(), 1, ref description);
}
if (reader.LocalName.Equals("path2"))
{
parsePath(reader.ReadString(), 2, ref description);
}
if (reader.LocalName.Equals("entry1"))
{
parseEntry(reader.ReadString(), 1, ref description);
}
if (reader.LocalName.Equals("entry2"))
{
parseEntry(reader.ReadString(), 2, ref description);
}
if (reader.LocalName.Equals("exit1"))
{
parseExit(reader.ReadString(), 1, ref description);
}
if (reader.LocalName.Equals("exit2"))
{
parseExit(reader.ReadString(), 2, ref description);
}
}
}
return description;
}
private void parseRow(string row, int rowNumber, ref TDP_LevelDescription description)
{
string temp = "";
int col = 0;
for (int i = 0; i < row.Length; i++)
{
if (row[i] != ',')
{
temp += row[i];
}
else
{
description.Grid[col, rowNumber] = Int32.Parse(temp);
temp = "";
col++;
}
}
}
private void parseEntry(string entries, int entryNumber, ref TDP_LevelDescription description)
{
string temp = "";
int x = 0, y = 0;
for (int i = 0; i < entries.Length; i++)
{
if (entries[i] != ',' && entries[i] != ';')
{
temp += entries[i];
}
else if (entries[i] == ',')
{
x = Int32.Parse(temp);
temp = "";
}
}
y = Int32.Parse(temp);
description.Entries[entryNumber - 1] = new Point(x, y);
}
private void parseExit(string exits, int exitNumber, ref TDP_LevelDescription description)
{
string temp = "";
int x = 0, y = 0;
for (int i = 0; i < exits.Length; i++)
{
if (exits[i] != ',' && exits[i] != ';')
{
temp += exits[i];
}
else if (exits[i] == ',')
{
x = Int32.Parse(temp);
temp = "";
}
}
y = Int32.Parse(temp);
description.Exits[exitNumber - 1] = new Point(x, y);
}
private void parsePath(string path, int pathNumber, ref TDP_LevelDescription description)
{
string temp = "";
int x = 0, y = 0;
for (int i = 0; i < path.Length; i++)
{
if (path[i] != ',' && path[i] != ';')
{
temp += path[i];
}
else if (path[i] == ',')
{
x = Int32.Parse(temp);
temp = "";
}
else if (path[i] == ';')
{
y = Int32.Parse(temp);
temp = "";
if (pathNumber == 1)
description.Path1.AddLast(new LinkedListNode<Point>(new Point(x, y)));
else if (pathNumber == 2)
description.Path2.AddLast(new LinkedListNode<Point>(new Point(x, y)));
}
}
}
}
}

 

Step 2: Writing a custom Writer...

 

now we need a writer to write the content to the .xnb file. The level description class just hold all the relevant data.

 

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;
namespace TD_Pipeline.Level
{
[ContentTypeWriter]
public class TDP_LevelWriter : ContentTypeWriter<TDP_LevelDescription>
{
protected override void Write(ContentWriter output, TDP_LevelDescription value)
{
output.WriteObject(value.Name);
for (int i = 0; i < value.Grid.GetLength(1); i++)
{
output.WriteObject(getSingleArray(ref value.Grid,i));
}
output.WriteObject(value.Path1);
output.WriteObject(value.Path2);
output.WriteObject(value.Entries);
output.WriteObject(value.Exits);
}
private int[] getSingleArray(ref int[,] grid, int row)
{
int[] array = new int[32];
for (int i = 0; i < grid.GetLength(0); i++)
{   
array[i] = grid[i,row];
}
return array;
}
public override string GetRuntimeReader(TargetPlatform targetPlatform)
{
return "TD_Engine.Pipeline.TD_LevelReader, TD_Engine, Version=1.0.0.0, Culture=neutral";
}
public override string GetRuntimeType(TargetPlatform targetPlatform)
{
return "TD_Engine.Game_Objects.Levels.TD_Level, TD_Engine, Version=1.0.0.0, Culture=neutral";
}
}
}

 

Step 3: Now we need a reader.

 

The reader reads the xnb file and creates a level object. This is run when content.Load is called... the above classes are used during buildtime

 

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using TD_Engine.Game_Objects.Levels;
namespace TD_Engine.Pipeline
{
public class TD_LevelReader : ContentTypeReader<TD_Level>
{
protected override TD_Level Read(ContentReader input, TD_Level existingInstance)
{
int[,] grid = new int[32, 20];
string name = input.ReadObject<string>();
for (int i = 0; i < grid.GetLength(1); i++)
{
addRow(ref grid, input.ReadObject<int[]>(), i);
}
LinkedList<Point> path1 = input.ReadObject<LinkedList<Point>>();
LinkedList<Point> path2 = input.ReadObject<LinkedList<Point>>();
Point[] entries = input.ReadObject<Point[]>();
Point[] exits = input.ReadObject<Point[]>();
return new TD_Level(name, grid, path1, path2, entries, exits);
}
private void addRow(ref int[,] multiArray, int[] array, int row)
{
for (int i = 0; i < array.GetLength(0); i++)
{
multiArray[i, row] = array[i];
}
}
}
}

Comments (0)

You don't have permission to comment on this page.