So far, I’ve been building level geometry and objects in code directly. As my test level got more complicated, though, building it in C# got more and more tedious, and obviously that wasn’t going to scale up to a full game. So I built a simple XML reader that can load level geometry and objects.

The first problem to solve was how to construct the different geometry types correctly. At first I messed around with .NET’s XmlSerializer class, but it turned out to be about as hard as manual parsing, due to the fact that XmlSerializer struggles with polymorphism and inheritance. So instead, I built a simple stream parser on top of XmlReader that walks over all the XML nodes in a level file. I didn’t implement any hierarchies or contextual data, so all I need is a single switch statement on the name of the current node. I’m storing all the data in the attributes of the node, so the file format and loader both ended up pretty simple. Some sample nodes look like this:

    <quad a="0,20" b="26,21" />
    <tri a="14,14" b="15,13.5" c="15,14" />
    <quad a="15,13.5" b="24,14" />
    <tri a="18,13.5" b="19,13" c="19,13.5" />

As an example, when the parser encounters a ‘tri‘ node, it parses it like this:

    case "tri": {
        var geom = new LevelTriangle(
            parseVec2(reader.GetAttribute("a")),
            parseVec2(reader.GetAttribute("b")),
            parseVec2(reader.GetAttribute("c"))
        );
        result.Geometry.Add(geom);
    } break;

Fairly simple, really, and it ends up being way less text than the original code-based solution, so it’s already easier to work with.

Rigging up switches and doors was more challenging, though; I needed a way to tie the state of a door to the state of one or more switches in the level. I ended up using a rather general solution to the problem that works pretty well, though the performance isn’t particularly amazing.

Each object in the level has a unique string identifier – switches, doors, etc. An object that relies on other objects for its state also has a state attribute that specifies how to attach it to other objects. Here’s an example:

    <pressure_plate id="plate1" a="8,24.85" b="9,25" />
    <pressure_plate id="plate2" a="4,24.85" b="5,25" />
    <switch id="switch1" a="6,14" />
    <door id="door1" a="6,21" b="7,25" state="plate1 | plate2 | switch1" />

Essentially, the first three elements define objects with a value that other objects can be attached to. The fourth element defines a door that’s attached to the first three elements.

The only challenging part here is the state attribute. The first step is to store all the objects that have an id attribute in a dictionary by their name, so that they can be looked up easily to retrieve their state. Once I have all the objects in a dictionary, it’s time to convert the state attribute into something that’s useful at runtime. The first step is to parse the attribute’s text into a set of meaningful tokens. Right now, since I’m lazy, I just use a combination of String.Split() and a switch() block to convert the string into a stack of function objects:

    switch (part) {
        case "&": {
            _Oper d = (lhs, rhs) => lhs() && rhs();
            stack.Add(d);
        } break;

        case "|": {
            _Oper d = (lhs, rhs) => lhs() || rhs();
            stack.Add(d);
        } break;

        default: {
            var key = part;
            Func<bool> d = () => (NamedObjects[key] as IState).State;
            stack.Add(d);
        } break;
    }

After running that logic over every part of the state attribute, I have a stack of function objects that represents the expression that can be evaluated to determine the state of the object while the game is running. But walking over a stack of functions every frame to determine the state of the object is pretty unwieldy, so the next step is to collapse the stack into a single, callable function:

    int i = stack.Count - 1;
    while (stack.Count > 1) {
        var op = stack[i] as _Oper;

        if (op != null) {
            var lhs = (Func<bool>)stack[i - 1];
            var rhs = (Func<bool>)stack[i + 1];

            Func<bool> d = () => op(lhs, rhs);

            stack.RemoveRange(i - 1, 3);
            stack.Insert(i - 1, d);
        }

        i -= 1;
        if (i < 0)
            break;
    }

One thing of note in these code snippets is the use of extra local variables to hold values used by the delegates. This is done in order to construct unique delegates for each particular value – if you just use an expression or an outer variable (for example, if I used ‘stack[i]‘ directly instead of the local variable ‘op‘), the compiler will reuse the single variable for all of the delegates. The local variable only exists until the end of its scope, so constructing a delegate from it allows you to essentially store that variable inside the delegate. This allows you to transform a value into a unique delegate that will return that value.

So, using this basic principle, I walk down the stack (starting at the top), looking for operators. When I find an operator, I grab the functions to the left and right of it on the stack, and collapse the three into a new function that represents the value of that portion of the expression. Once I’ve walked all the way down the stack, I have a single function that, when invoked, computes the complete value of the expression. I can take that function and use it at runtime every frame to determine whether the door should be open or not.

Mind you, this approach of constructing a composite delegate out of dozens of other delegates is somewhat expensive – there are faster ways of doing it, like using DynamicMethods or LINQ Expression objects.

All things considered, it took about 150 lines of code to replace my existing 50-line C# function with a 35-line XML file. The next logical step is to extend the game code so that I can reload a changed level file at runtime, without having to restart the game, so that I can iterate more quickly on changes to level geometry and logic.