My names Cliff, generally known as Cliffski. I'm an Ex-Elixir, Ex-Lionhead games programmer who owns and codes Positech Games. From time to time I put fingers to keyboard on coding thoughts:
One of my favorite films has to be the martial-arts epic 'Hero'. There is a great scene where the King is examining some calligraphy done by a master assassin, and describes the various phases of swordsmanship and enlightenment that the assassin has passed through as he studied calligraphy. It's a great scene, and as obsessed with movie quotes as I am, I can't help but draw parallels with all kinds of stuff.
When you first learn to code games, especially in C++, it's all a bit overwhelming. Just getting a sprite on the screen that moves is a major headache, and once it works, you are jubilant. The readability of the code, what a computer science professor would think of your syntax etc, is all unimportant. What matters is IT WORKS! This is the very first phase of becoming a games programmer, and its a phase many people never move beyond This is the first phase of proficiency with the katana. The main problem with writing code like this (let's call it 'hacky code') is it's un-maintainability and lack of reusability. When you are sat on your own knocking up some space invaders clone, this is no big deal. If you never have to offer tech support or sell the product, even better! You get things done like this pretty quickly, and no real harm is done.
Now in most cases, a good lead programmer will wrestle 'hacky coders' into submissions. Poor coding techniques which aren't clear are knocked on the head (I eventually learned to pass strings by reference), and coding standards are strictly enforced. Copying and pasting code chunks is banished, and data-hiding and clear class hierarchies eventually begin to take shape. At this stage, even a blade of grass becomes a deadly weapon. This is all great stuff, and it's the only good reason to go work at a big games company (to have your rough edges smoothed by an experienced lead coder). There is one practice however, that I have begun to conclude is sometimes pushed beyond it's limits.
That's data-driven code.
Shock Horror! behold!, am I really suggesting that data-driven code is bad? Who is this idiot?
Don't panic. Data-driven code == good. I have not lost it totally. I have just passed beyond the status of intermediate swordsman (sensible coder who applies what he has read in code complete) on to the status of wise warrior, (where the sword dissapears and the desire to kill no longer exists).
'Data-driven' code means that you aren't writing any code that says (for example) CUberWarrior::FirePlasmaGun(). This is what we call a 'special case' bit of code. What a data-driven coder would do is to define a CWarrior class and a CWeapon class (or more likely be even more generic) and define what's 'uber' or 'plasmagun' about these objects in external data. This would be done by a non coder, probably a level designer, using either simple text files or purpose-written tools. The benefits are plentiful, as adding a new warrior type (L33t Warrior) or new gun (Lazy Gun) can be done by a designer without introducing expensive new code, new bugs, or a change to the executable. This is especially popular for games where an expansion pack or modding is considered a possibility, because ideally the executable can be left alone, with more content being added trivially.
It's a good principle. It's a good idea, and it works, and it saves time...Except when it doesn't. Sometimes the amount of effort involved in writing the 'data driven' version is not worth the reward. And much much worse, you can actually restrict the functionality of the game MASSIVELY by sticking too fundamentally to the doctrine of data driven == good, special case == bad.
I first concluded this working on AI for the Movies. A big chunk of the games AI was 'explainers', which started off as simple bits of code that assigned people to animations, managed how they interacted with a building etc. Gradually the functionality mushroomed, until we had 'roaming' explainers, meta explainers, range-based explainers, class-restricted explainers, hidden and inactive explainers etc etc. There used to be a console command that drew debug data onscreen for them, and it FLOODED the screen. Explainers had been made data-driven , and some of the artists and designers had got really into adding new ones at every corner. Have you noticed the guy who parachutes off the water tower? That's all in data, its no different to the guys hammering buildings, there is no code for this at all.
And we looked upon the world of explainers, and we saw that it was good. And Lo, we did smile.
And then came the restaurant. Not only the restaurant, but the bar. These weren't just a bloke sitting on a bench. They were complex uber-explainers that had to co-ordinate maybe a dozen people eating at different tables, all arriving at different times, some arriving in pairs, and some alone, all queuing up for a table, and all not bumping into each other. And verily didst the coder (me) hammer away at his faithful explainer code. And he added composite explainers, and he added code to load them in. And he fiddled with the queuing code, and he fiddled with the scene selection code, and he fiddled with this and he fiddled with that... And in the end he ended up writing a class called something like CRestaurant.
And everything was good. Special case code had been written, and lo, the heavens did not open, and locusts did not descend, and the code didst work, and it did not contain horrific AI bugs. Now, if there had been dozens of different 'facilities' that all needed similar ways of working, and plans for many more in expansion packs, then this would have been bad. I should have pulled the explainer code to bits and rebuilt it entirely to accommodate the new functionality. but this wasn't the case, and I darned well reckon I did the right thing. As I'm now experiencing similar situations in my latest game (the second since I worked on the Movies), I'm pretty certain there's a valid observation here.
"Special case code is not bad per-se. There are occasions where special case code is MUCH better than more generic, reusable, data-driven code"
The things is, its very hard (without tons of experience) to spot when its appropriate. The explainer code was VERY data driven, and this led to a huge explosion in the 'life in the lot' functionality that made the game feel alive. Making explainers data-driven made a huge difference to the game. The problems occurred when my religious fundamentalism for hating special cases made me blind to opportunity.
"Do not allow the war on special cases to blind you to opportunity"
Sometimes when we are given a coding task, we can see a very easy way to do it and to make it VERY cool, and do all sorts of cool stuff, which involves some horrid special case code that we are ashamed of. At times like this, you need to make a detached unemotional judgment call. If you are at the start of a project, its probably a bad idea. Special case code *is* a bit hacky, and you don't want that house of cards. But at the middle or end, adding a few horrid looking special cases here and then can allow you to add some very cool effects and features with minimal work. If you are working on a team, it's worth talking through whether to do it with other team members, just be sure that you aren't acting out of habit and throwing away an opportunity. My new game has some special case code, and I'm just adding a big chunk of it to 'Democracy'. I actually have code that says "if level == USA" (kinda). I finally realized, that my hatred of special casing anything was probably one of the major reasons for that games most-criticised issues (lack of variety between countries).
It's much easier to learn that special cases are bad and data driven is good. You know where you are with advice like that, it's like the ten commandments or the rules of acquisition. The trick is knowing when to treat the rules as "more kind of guidelines".
If you liked this article, buy my games. I need money, I am hungry...