MotoGP: tweakables

Originally posted to Shawn Hargreaves Blog on MSDN, Friday, May 1, 2009

Games are full of constant values. How high can my character jump? How long does it take to reload my crossbow? How much damage can a cat inflict on a zombie? What is the balance between ambient and diffuse in my lighting rig? How long does my game take to fade out when I quit back to the main menu?

Some of these constants are crucial to gameplay. Others determine whether your graphics look good. But most importantly, finding the right values creates the feeling of polish and attention to detail that will delight gamers and make them want to give you money :-)

I think one of the most underappreciated areas of game development is making constants easy to tweak. Consider the hack we used to simulate lightning in MotoGP. This only worked because we had exactly the right values for the frequency and length of flashes, the brightness, the difference in intensity from near to far, etc. The same technique would look ridiculous if any of these were even slightly wrong. To find the right values, we needed a way to experiment with different numbers and immediately see the results. It would take far too long if we had to recompile, redeploy, and reload the game every time we wanted to tweak a constant!

Y'all remember the console in Quake, right? That's how id Software made their constants easy to tweak. But a console is kind of intrusive, covering up part of the game, and also a pain to use on an Xbox that has no keyboard.

For MotoGP, we implemented a remote console using telnet. The telnet protocol is easy to implement: just open a TCP socket on port 23, then send text back and forth. We programmed the game (running on an Xbox devkit) to accept console commands in this way, and could connect to it from PC with a standard telnet client.

We used a TweakVariable class to create tweakable settings. We would declare a number of constants at the top of each source file:

    BoolTweakVariable ShowSun(true, "sun/show");
    IntTweakVariable RayCount(42, "sun/rays");

These types had automatic conversion operators, so we could use a BoolTweakVariable any place a bool was expected, an IntTweakVariable in place of an integer, etc:

    if (ShowSun)
    {
        for (int i = 0; i < RayCount; i++)
            ...
    }

These types had constructors that automatically registered them with the telnet console, so we could connect to a running game and change their values at any time:

    > telnet mydevkit
    sun/rays=30

This stuff was seriously useful. Within days of adding it, tweakvars were sprouting up all over the place. We used them to tweak graphical effects, physics, AI, cameras, sounds, rumble settings, UI screen layout, transition effects, controller input, gameplay rules, and a multitude of debug features. Some settings were tweaked by programmers, some by designers, and others by artists. By the time we shipped, we had amassed more than three thousand tweakable constants!

It got to the point where we were doing so much tweaking that Damyan decided to make a proper user interface for it. He wrote a Python program that used the existing telnet protocol to communicate with the devkit, but replaced the text commands with buttons and sliders. Because our tweaking console was separate from the game, we didn't have to alter any game code to enable this new GUI, and could use a language (Python) that was not available on Xbox.

 

Tweakables in XNA Game Studio

If you are a Game Studio developer, telnet is obviously not an option. But there are several alternatives:

Blog index   -   Back to my homepage