When it comes to game development, you end up changing constants a lot. Make an enemy hit harder, make a character move just a tiny bit faster, etc. They’re still constant as far as the player’s concerned, but the compiler will get a little annoyed if you try and change them without recompiling. While my compile times are still reasonable (only a few seconds), it takes a few moments to exit my game, edit some source files, and load it up again. Even after the game’s started up, I still need to load a saved game to get back to where I was. All said, a hassle if I’m looking to fine-tune things.

I spent some time today adjusting various physics parameters to compensate for the fact that I’m getting ready to drop in the actual sprites for my player characters. The actual sprites are much, much larger than the placeholders I’ve been using, so various parameters – run speed, jump velocity, etc – all needed adjustment. Tuning these by hand was a pain.

The solution is a simple one, but it requires a little magic: Replace game-related constants with something more malleable that can still be used like a constant.

The first step is to find all the constants I care about. Hopefully, they’re all marked const… so, they’ll look something like this:

        public const float StandingSearchDistance = 4.0f;

I change them to this:

        public static Constant<float> StandingSearchDistance = 4.0f;

Easy to do with a regular expression, or a simple search/replace. Once I’ve defined the Constant<T> class appropriately, everything compiles and runs just like it used to.

    public class Constant<T> {
        public readonly T DefaultValue;

        public Constant (T defaultValue) {
            DefaultValue = defaultValue;
        }

        public static implicit operator T (Constant<T> constant) {
            return constant.DefaultValue;
        }

        public static implicit operator Constant<T> (T constant) {
            return new Constant<T>(constant);
        }
    }

This class doesn’t do much yet, but it’s not a bad start! It meets our most pressing need: Keeping the code working correctly, and setting the foundation for us to do more interesting things.

So, the next step is to make the Constant type a little less constant. Let’s give it a way to take on a new value at runtime:

    public class Constant<T> where T : struct {
        public readonly T DefaultValue;
        private T? _CurrentValue;

        public Constant (T defaultValue) {
            DefaultValue = defaultValue;
            _CurrentValue = null;
        }

        public T Value {
            get {
                return _CurrentValue.GetValueOrDefault(DefaultValue);
            }
            set {
                _CurrentValue = value;
            }
        }

        public static implicit operator T (Constant<T> constant) {
            return constant.Value;
        }

        public static implicit operator Constant<T> (T constant) {
            return new Constant<T>(constant);
        }
    }

Not a difficult change, really, and we’ve already got something useful to play with! If we wanted to, we could rig some code up to let us change these constants already, and if the game code is structured right, it’ll work just fine. It’s done! Ship it!

Well, okay. There’s more work to do. Two major things missing:

  • There’s no easy way to get a list of constants to modify. Seems silly to leave that responsibility up to the game code, so let’s handle it in this bit of utility code instead.
  • We need a way to save out constants once they’re modified, so that the changes you’ve painstakingly tested don’t get lost.

The first one is easier, and happens to be a prerequisite for the second one. So let’s start there.

The solution to this is pretty easy, really: We can use reflection! We know that every constant is going to be in a static field, and it’s going to be an instance of a generic type: Constant<>. This lets us write a simple bit of reflection code to find all the constants defined in the current assembly:

    public static class ConstantManager {
        public static Dictionary<string, object> Constants = new Dictionary<string, object>();

        public static void Initialize () {
            var genericConstant = typeof(Constant<>);

            foreach (var type in Assembly.GetCallingAssembly().GetTypes()) {
                var typeName = type.Name;

                foreach (var field in type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) {
                    var fieldName = field.Name;
                    var fieldType = field.FieldType;

                    if (!fieldType.IsGenericType || fieldType.GetGenericTypeDefinition() != genericConstant)
                        continue;

                    var constant = field.GetValue(null);
                    if (constant != null) {
                        var constantName = String.Format("{0}.{1}", typeName, fieldName);
                        Constants.Add(constantName, constant);
                    }
                }
            }
        }
    }

Well, okay. I might have been exaggerating a little – 20 lines isn’t ’simple’. But it’s not bad! There’s a few things going on in here: We scan over all the types in the assembly for static fields, check to see if they’re instances of Constant<>, and then pull them out, figure out their full name, and store them in a dictionary. Once we’ve done this, we now have a list of constants we can iterate over, so we can populate a UI to let the user fiddle with values.

So, as long as we call ConstantManager.Initialize at some point, the first problem is solved. Now for the second one!

As you may have noticed, we don’t actually have a clean way to interact with constants yet, because they don’t all share an underlying type – you can’t cast from object to Constant<> and fiddle with an untyped value, unfortunately. But the solution to this is simple – define an interface, and implement it:

    public interface IConstant {
        object Value { get; set; }
        object DefaultValue { get; }
        Type Type { get; }
        void Save ();
    }

Simple enough, right? Toss a little magic into the class to wire things up:

    public class Constant<T> : IConstant, where T : struct {
        ...

        object IConstant.DefaultValue {
            get {
                return this.DefaultValue;
            }
        }

        object IConstant.Value {
            get {
                return this.Value;
            }
            set {
                this.Value = (T)value;
            }
        }

        public Type Type {
            get {
                return typeof(T);
            }
        }

        ...

Now we can use this interface to interact with constants – get their current/default values, and change their values. Combine this with the names stored in the ConstantManager and we have everything we need to populate a UI. Coincidentally, this gets us closer to being able to save constants out at runtime, too!

We’ve got a way to get a list of all the constants at runtime, and now we’ve got a way to get their values. All we need is a way to save them out to disk. We could try something elaborate based on digging through our source code, or save constants out to XML or something silly like that. But instead, why not let the compiler help us out?

The compiler already knows where our constants live, and it so happens there’s a way for us to get that information – in debug mode: The StackFrame class. Every StackFrame comes with a file name, line number, and even a column number. As it happens, it’s possible for us to get a StackFrame for the static member that contains a Constant! All we need to do is walk a couple steps up the stack:

public class Constant<T> : INamedConstant where T : struct {
    ...
    public readonly string SourceFile;
    public readonly int SourceLine;

    public Constant (T defaultValue) {
        var sf = new StackFrame(2, true);
        SourceFile = sf.GetFileName();
        SourceLine = sf.GetFileLineNumber();
        ...

We pass 2 to the StackFrame constructor because: 0 is Constant’s constructor, 1 is Constant’s implicit conversion operator (invoked because we didn’t change our constants to use the constructor directly), and 2 is the static constructor for the type that holds the constants.

Now, one would assume that the line number we get would point to the start of the class, or out into the middle of nowhere. But it turns out that somebody on the compiler team was feeling generous, because we actually get the exact line and column where the constant was declared. Awesome!

Given the file name, line number, name of the constant, and value, we have everything we need to save a constant back out to disk – right into the source file it came from, so it’s ready to be compiled or checked into version control.

I’ll admit – mucking with text files is annoying. So here’s the cheap hack I wrote to get the job done:

        public void Save () {
            var lines = File.ReadAllLines(SourceFile);
            var line = lines[SourceLine - 1];
            var typeSuffix = "";

            if (Type == typeof(float))
                typeSuffix = "f";

            lines[SourceLine - 1] = Regex.Replace(
                line, @"=(\w*)([^;]*);",
                String.Format(@"= {0}{1};", Value, typeSuffix)
            );
            File.WriteAllLines(SourceFile, lines, Encoding.UTF8);
        }

You’ll notice I had to hack in a fix for floats – if you write them out raw without an ‘f’ on the end, the compiler will treat them as doubles, and yell at you. I’m willing to bet there’s a better way to solve this problem, but I don’t know what it is. :)

After this, the rest is pretty simple – pop a button into the UI where you show your constants, and in the click handler, walk over all the constants and save them. You probably want to only save constants that’ve been changed from their default value, and that’s easy since we store the default value separately from the current value.

Now that you know how it works, here’s what my take on it looks like. It’s pretty simple – I wrote a couple dozen lines of code to hook it up to a DataGrid and some extra code to highlight constants that’ve been changed from their default values.