Its Easy Once Its Going: Of Beginnings and Endings

I often wonder about the challenges of videogame programming. Most people seem to assume the real key lies in domain specific knowledge. Are you some super math whizz? Can you read straight assembler like it were a book? Can you code learning AI which takes advanced statistical analysis of the battlefield into account? Because if you can do that, the common belief goes that you would be able to write the most awesome game!

Ofcourse you know as well as I that it is all smoke and mirrors. Most of a game can be written by people who know how to use matrices, but don’t understand them. Most of a game can be written by people who would be dumbfounded if they saw assembler. Most of a game can be written with only the most basic of AI models. I will make a partial exception here for programming on consoles as the limited nature of their hardware introduces a number of logistical issues with regard to: memory allocation, memory fragmentation, very slow load times from storage media, and unforgiving performance hits when blowing instruction/data caches. However, I’d like to make it only a ‘partial exception’ on the grounds that the aforementioned is akin to understanding your target environment (which would be the same for any programming) more so than knowledge specific to creating games.

So, if you don’t need a large amount of domain specific knowledge when it comes to game algorithms, and neither do you need to be some super whizz to create a polished game… then where do the challenges come in?

While there are a number of things I have realised, I suppose there are two key points that I’d like to make mention of here. Maybe you’re reading this because you are about to embark on a game project. If so, then let me at least raise these thoughts to your conscious mind because strangely enough its not always clear what the causes are for frustrations later in the project. Perhaps this will spur you into thinking of techniques to address the issues.

1) Getting the Ball Rolling

Most games it seems start with the ‘game phase’. The bit where the player interacts and invokes the core game mechanics (moving, shooting, collecting things, etc.). After some time, the environment gets fleshed out and bits of a HUD start to appear. Eventually menu’s come into the picture – often the pause menu first as that houses ‘restart’ and other handy features.

Now, the problem as I see it lies in the foundations. Or lack thereof. You wouldn’t inline all your render calls in the middle of arbitrary functions (or at least you might have, and if so I’m guessing you just wrote your first game outside of a commercial environment). You wouldn’t write a bunch of network code to transmit data for each object if you hadn’t already laid down the framework for getting a connection and managing peers.

In a similar way, you shouldn’t write the actual playable game without the framework that it would fit in.

Say what?

At some point your game is going to have to transition from a menu, and into a state where it can begin. Because it has always been programmed with a hacked up initialisation which ensured that by the time the application started running its main update loop, the game was already in a running state and it’s quite difficult to then make the application aware of non-game states (such as the main menu).

Typically the transition phase from a newly added menu to the ingame is the most flaky, error intolerant, and poorly architected part of the game. Whole chunks of memory can be left hanging, handles left open, and hacks to get loaded save games to correctly restore state abound.

Once the game is actually setup, objects are instantiated, a render or two has been called, and some ‘fake updates’ to get objects to set state before the physics/ai/whatever runs, then its usually solid. After that, its into a well-tested predictable state which has been worked on since the birth of the project.

I know this well. I only had to make this mistake in my approach to code once before I realised it, luckily.

The solution, ofcourse, is to lay the groundwork for the game phase before you begin work on it. Know that you’re going to need special ‘loading’ phases, menu phases, and maybe areas where network connectivity will be active. Put something basic in place before you write a single line of code toward giving the player an object or reading his input. Just a day taken to do something up front can save a lot of headache later.

2) One Frame Glitches

A personal hate of one of my bosses. This is usually apparent when games change camera views, flick in and out of cutscenes, or experience large time delta’s due to pauses for loading or similar. The effect is usually that of the scene being very rapidly ’set up’ over the course of 1 or 2 frames. The cause? Update dependencies between objects or systems.

On projects which involve code teams larger than 2 or 3 people, these sorts of things crop up fairly often. Without the coherency of a single mind divising the code (or least so few minds as to allow each to know everything the others are doing), there will be dependencies between objects which are not made explicit. At some point these inconsistencies boil to the surface.

It may be as simple as a few disparate decisions which were made – each seemingly quite simple – but together, they cause mayhem. For example, lets imagine each update the game object just transfers its local state to its physics representation in a 3rd party physics library. Someone sticks this in the objects Update() or similar function and all seems good. A few cached transforms for the graphics render need to be kept, so the graphics guy adds in some calculation and caching where the physics guy put his code because the rationale is: if the object is moving, I’ll need to recalculate these.

Next thing a separate programmer was adding inter-object interactions, which means a second object can modify the velocity of another. Nobody notices that if one object is updated after the one it affects, then it takes one additional update to propagate the physics change.

Still later an AI programmer is getting a smart object to intercept another. It does this by looking at the physics representation because he wishes their physics volumes to aim at each other for a collision. The AI seems to pretty much work fine – a bit of on screen debug output looks accurate, and the objects are colliding.

Lastly someone adds in a teleporter. Suddenly strange and wierd things are happening. The camera changes to show the first frame of a new scene which doesnt have all objects in the right places. Nobody quite knows why, but it seems a very small thing so it gets ignored.

Move forward to the end of the project and suddenly these small things are priorities. Somebody investigates.

What they find turns out to be quite a can of worms!

It seems that the teleporter got updated after the player object. A decision made early on meant objects have their own code for changing the player object’s state. This means the teleporter detects the collision and sends the player object off to another part of the world. However the graphics and the physics for the player have not had this propagated to them yet! Now the camera has started its fancy shake at the ‘exit’ for the wormhole which seemed pretty cool – only for one frame the player’s character is not there.

Furthermore, the local AI objects in the area suddenly wake up because they’ve all been triggered by the player warping in. However, when they query his physics position – its still back at the teleporter… so they decide to head in that direction. At least for a second or two until their next ‘think()’ is called.

Yes, its a trivial example. However, this kind of thing happens a lot on a large project. All a project needs to do is exceed the ability for the people working on it to keep the entire thing in their heads at once, and voila! Things get out of sync, rare crashes happen, and hacks start to abound.

Changing the order of updates or modifying the base code seems way too risky by this point! So what to people do? Add extra Update() calls, extra Render() calls, and write special case code where teleporters might iterate over all objects which are not teleporters and Update() them when something teleports. This in turn causes an unusually large amount of execution on this one particular frame, and can be noticeable on limited hardware devices (pocketpc’s, phones, etc).

Conclusion?

There is no silver bullet. Unforunately. However you can take measures to allieviate the pain when you consciously realise that there are going to be these sorts of issues.

1) Communication! Documentation! Write comments that make it plain there will be a frame or two ‘propagation delay’. Make the update order for the application explicit so that everyone on the team knows the broad overview of how things are planned to tie together. Who knows – maybe you end up taking a break and need to pick up the project after you’ve forgotten the specifics. How good would it be to see reminders for class and function responsibilities!

2) It always pays to consider the whole. If you’re making a game, brainstorm all the software bits you expect to need – including a brief mention of installers, 3rd party libraries. Then brainstorm a rough overview of whats in the game – even if its not accurate, at least provision for the core items will hopefully see a more flexible framework which will accommodate unknowns without too much pain.


About this entry