2 Comments

Tips for robust code

As an independent developer, I need to manage my time carefully. I want to ensure that my test burden and time spent debugging are kept to a minimum. Over the years I’ve developed some guidelines of a sort that let me write robust code that is hard to break. In this post I’ll share some of the things I do.

Any code samples I use here are pseudo-code (some kind of bastardization of C++ and C#).

Compile time failures are better than runtime failures

A bug found at compile time is obviously much less costly than one found at runtime. As much as possible you should design your code to fail at compile time if something is wrong.

Service locators

One common thing I see is the service locator pattern. It looks something like this:

services.Add(RenderServiceId, new RenderService());

Then later,

FooSystem(services)
{
    renderService = services.Get(RenderServiceId);
}

This seems nice, because you may have several of these services that some other part of your code needs. You can simply pass in the generic service container to your systems and they can retrieve what they want, without cluttering up their constructors with parameters.

The problems here are that the dependencies have been made less explicit and/or hidden. In the above example, FooSystem requires a RenderService in order to do its job – it’s broken without it. But if changes are made such that RenderService is no longer added to services, or it is moved so that it is added after FooSystem is created, then you you have a bug that will only be evident at runtime.

A more robust solution is to pass the needed objects into FooSystem directly (or provide some  context object with strongly-typed objects in it – although this is less than ideal). Yes, it makes  the code more wordy to type, but it is more important that code should be easy to read than easy to write. In this case, if you moved the creation of RenderService after the creation of FooSystem, or decided you didn’t need RenderService at all, you would get a compile error.

FooSystem(RenderService renderService)
{
   this. renderService = renderService;
}

Of course, there are still cases where it makes sense to use the service locator pattern – you should just have a good reason for doing so.

String Ids

Anywhere in your code where you use strings to identify resources is a potential problem. For instance, if you have a sound that is stored using the string key “HornSound”, and then piece of gameplay code that plays that sound by referencing “HornSound”, then you have a potential problem.

This kind of ties into the next section about code coverage, but the danger here is that if the string is spelled incorrectly you won’t find out about it until it results in a runtime crash (or simply a failure to play the sound). It’s not a big problem if you’re sure that code is always hit whenever you play the game. But if it’s in an obscure part of code that isn’t frequently executed, it is more likely you could ship with that bug.

A solution is to only use string constants defined in code:

wchar_t wszHornSound[] = L"HornSound"; // C++
public const string HornSound = "HornSound"; // C#

This way you’ll get compile errors if you’ve mistyped something.

Of course, this may not be practical if these strings are used in different libraries, or in scripting languages. But if it’s possible, then it’s another way to move errors to compile time instead of runtime.

Code coverage

Code coverage is a measure of how much of your code is exercised during a particular scenario. You may have specific tests that run which attempt to exercise all features of your game. Of course, this is ideal – but honestly, I don’t have time to write all these tests.

So when I code something up, I try to think about how I can structure the code so that as much as possible is “touched” simply by casually playing the game. This doesn’t mean that I end up calling unnecessary code of course (although sometimes in DEBUG builds I do exactly that). It can be as simple as following the DRY principle, and factoring out any common code into separate functions.

For a more subtle example, consider the code to move a character in one of 4 different directions based on which key is pressed. You might have something like:

if (IsKeyPressed(Key_W))
{
    position.Y -= 1;
}
else if (IsKeyPressed(Key_S))
{
    position.Y += 1;
}
else if (IsKeyPressed(Key_A))
{
    position.Y -= 1;
}
else if (IsKeyPressed(Key_D))
{
    position.X += 1;
}

A better solution would be to have a table that maps keys to a value that should be added to the current position.

struct KEYTODIRECTION
{
 int key;
 Point direction;
};
KEYTODIRECTION keysToDirection_MoveCharacter[]
{
 { Key_W, Point(0, -1) },
 { Key_S, Point(0, 1) },
 { Key_A, Point(-1, 0) },
 { Key_D, Point(1, 0) },
};

then later,

foreach (KEYTODIRECTION ktd in keysToDirection_MoveCharacter)
{
 if (IsKeyPressed(ktd.key))
 {
  position += ktd.direction;
 }
}

This might be a little more verbose, but it is less code logic. In general, the less logic and more data-driven code you have, the better (in fact, can you spot the error in my first example?). Certainly, you can still end up with bad data which doesn’t get code coverage – but at least you have eliminated logic that doesn’t get code coverage (and it’s much easier to write tests that operate over tables of data as opposed to code).

Calculate, don’t cache

I have a broad rule I often follow: if the data I want can be calculated from existing data, then do that instead of maintaining a separate cache of that value.

As an example, consider a game where a new enemy appears every 10 seconds, but there can be no more than 5 enemies around at any one time. So when we hit the 10 second mark, we need to check that there are currently less than 5 enemies before creating a new one.

The obvious thing to do might be to maintain an enemy counter in the code that creates new enemies. Each time we create an enemy, we increment that. Each time an enemy is killed, we decrement it (perhaps the enemy-creating code listens to some event that indicates an enemy is killed). 

That will work. But a better option in my opinion is to query the number of enemies each time we need that data. This may be as simple as checking the count of items in an enemy list, or a more complex query scan all game objects for ones with certain attributes (it depends how your enemies are identifiable in your game world).

The main benefit is that you never have a number that can get out of sync. If on the other hand you keep a separate count, any new piece of code that is written that creates or destroys enemies needs to be aware of this count. This is sort of a hidden dependency and is prone to breaking.

Another side benefit is that it makes it simpler to serialize your game world (e.g. for saves games). It’s one less piece of data to worry about.

So in general, the less duplicated state the better. In my experience “out-of-sync cache” bugs can be very insidious to debug.

Of course if “calculate, don’t cache” leads to a performance issue, then you should reconsider. But in that case it should be an deliberate decision to cache a piece of calculated data, driven by performance considerations.

Advertisements

2 comments on “Tips for robust code

  1. I spotted the deliberate error:

    else if (IsKeyPressed(Key_A))
    {
    position.Y -= 1;
    }

    It should be position.X -=1;

    Do I get a prize?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Space Quest Historian

Adventure game blogs, Let's Plays, live streams, and more

Harebrained Schemes

Developer's blog for IceFall Games

kosmonaut's blog

3d GFX and more

Halogenica

Turn up the rez!

bitsquid: development blog

Developer's blog for IceFall Games

Game Development by Sean

Developer's blog for IceFall Games

Lost Garden

Developer's blog for IceFall Games

Memories

Developer's blog for IceFall Games

Casey's Blog

Developer's blog for IceFall Games

Blog

Developer's blog for IceFall Games

Rendering Evolution

Developer's blog for IceFall Games

Simon schreibt.

Developer's blog for IceFall Games

Dev & Techno-phage

Do Computers Dream of Electric Developper?

- Woolfe -

Developer's blog for IceFall Games

Ferrara Fabio

Game & Application Developer, 3D Animator, Composer.

Clone of Duty: Stonehenge

First Person Shooter coming soon to the XBOX 360

Low Tide Productions

Games and other artsy stuff...

BadCorporateLogo

Just another WordPress.com site

Sipty's Writing

Take a look inside the mind of a game developer.

Jonas Kyratzes

Writer, game designer, filmmaker.

%d bloggers like this: