Friday, August 31, 2012

XNA and Level Validation

In the previous post, we discussed the topic of XNA and System Testing.
Now, let's build on this information to discuss Level Validation with XNA.

Note: this post includes complete code sample on CodePlex.
Download code sample here.

Level Validation
In game development, level data is most often stored in text files. For example, the Platformer starter kit contains 3x levels, although an unlimited number of levels could be added using data driven design.

Level data can then be validated through system tests to ensure all data is true and correct, and that all rules are observed before the level is actually loaded into the game.

Example
As an example, let’s revise the Platformer starter kit to demonstrate XNA and Level Validation.
First, create a system that can parse text files on demand; use the interface from previous post:
public interface IFileManager
{
 IList<String> LoadTxt(String file);
 T LoadXml<T>(String file);
 XElement LoadXElement(String file);
}
Next, define all the rules for each Level in the game; for example, each Level must have:
  • Valid characters only for player, enemy, tiles + gems
  • Same number of tiles width for each row in level
  • Exactly one entry for player - no more, no less
  • Exactly one entry for the exit - no more, no less
  • A passable tile immediately above the exit
Sample
The following code sample refactors the starter kit to integrate an unlimited number of levels using data driven design and validates each level through system tests.
First, define a simple text file that will store all levels to load in the game and in correct order.
Next, add all the corresponding level data for all levels in the game; one level per text file:

E.g. Level 10
...........G.G.G.G..
...1............A...
..######..########..
.....G.G.....G.....G
......B.............
G...-----...--.G..--
....................
--.G.G........--...G
....C........G......
..----.G.G..--....--
........D...........
.......----...--....
.G.G.........G.G.G..
..X.................
######......########
Add a method to the Level class to validate all level data according to the rules as defined above:
public class Level : IDisposable
{
 public void Validate()
 {
  if (null == levelData)
  {
   throw new Exception("No level data loaded.");
  }

  string validCharacters = ".XG-ABCD~:1#";

  // RULES:
  // Ensure only valid characters parsed.
  // Ensure same tile width for each row.
  // Exactly one entry for player!
  // Exactly one entry for exit!
  // Ensure exit point passable!
 }
}
Finally, write system tests to validate all data before fully integrating into the game.
Note: an IoC Container will be used to construct all components used throughout.

FILE MANAGER TESTS
[TestFixture]
public class FileManagerTests
{
 // System under test.
 private IFileManager fileManager;
 
 [SetUp]
 public void SetUp()
 {
  fileManager = IoCContainer.Resolve<IFileManager>();
 }

 [Test]
 public void LevelDataValidateTest()
 {
  String levelsFile = GetPath("LevelData.txt");
  levelIndexes = fileManager.LoadTxt(levelsFile);
  numberOfLevels = levelIndexes.Count;

  // There must be at least one level!
  Assert.IsTrue(numberOfLevels > 0);

  for (Int32 levelIndex = 0; levelIndex < numberOfLevels; ++levelIndex)
  {
   String levelFile = levelIndexes[levelIndex];
   Validate(levelFile, levelIndex);
  }
 }

 private void Validate(String levelFile, Int32 levelIndex)
 {
  String levelPath = GetPath(Constants.LEVELS_DIRECTORY, levelFile + ".txt");
  IList<String> levelData = fileManager.LoadTxt(levelPath);
  level = new Level(levelData);
  level.Validate();

  String text = String.Format("Level #{0} = {1}.txt [{2} rows]", (levelIndex + 1), levelFile, levelData.Count);
  Console.WriteLine(text);
 }
}
Download code sample here.

Summary
The revised Platformer starter kit demonstrates how to integrate an unlimited number of levels using data driven design: simply update the text file to add more levels without constant need to recompile and validate all data quickly and efficiently through system tests.

In addition to complex text files, complex XML files may also be loaded into a game, for example, to build game objects using component based design.

This will be the topic in the next post.

No comments:

Post a Comment