Our Properties: Gamasutra GameCareerGuide IndieGames Indie Royale GDC IGF Game Developer Magazine GAO
My Message close
Contents
What Your Mother Never Told You About Game Programming
 
 
Printer-Friendly VersionPrinter-Friendly Version
 
Latest News
spacer View All spacer
 
February 9, 2012
 
Road to the IGF: Alexander Bruce's Antichamber
 
What Nintendo's 2011 sales mean for Wii U, third parties [7]
 
Zynga's sales boosted to $335M during holiday quarter, says analyst
spacer
Latest Jobs
spacer View All     Post a Job     RSS spacer
 
February 9, 2012
 
Airtight Games
Senior Environment Artist
 
Airtight Games
Software Engineeer
 
Tencent Boston
Senior Server Programmer
 
Zindagi Games
Associate Producer
 
Spooky Cool Labs
Software Developer - Games - Front End (Unity 3D)
 
Spooky Cool Labs
Marketing Director
spacer
Latest Features
spacer View All spacer
 
February 9, 2012
 
arrow Principles of an Indie Game Bottom Feeder [8]
 
arrow Postmortem: CyberConnect 2's Solatorobo: Red the Hunter [1]
 
arrow Jerked Around by the Magic Circle - Clearing the Air Ten Years Later [34]
 
arrow Building the World of Reckoning [4]
 
arrow SPONSORED FEATURE: TwitchTV - How to Build Community Around Your Game in 2012 [13]
 
arrow Happy Action, Happy Developer: Tim Schafer on Reimagining Double Fine [9]
 
arrow Building an iOS Hit: Phase 1 [11]
 
arrow Postmortem: Appy Entertainment's SpellCraft School of Magic [5]
spacer
Latest Blogs
spacer View All     Post     RSS spacer
 
February 9, 2012
 
The Principles of Game Monetization
 
Double Fine's Kickstarter Windfall: Will Patronage Supplant Traditional Game Publishing?
 
Did DoubleFine Just break the publishing model for good? [3]
 
The Devil Is in the Details of Action RPGs - Part One: The Logistics of Loot [3]
 
Xbox LIVE Indie Games at it Again
spacer
About
spacer Editor-In-Chief/News Director:
Kris Graft
Features Director:
Christian Nutt
Senior Contributing Editor:
Brandon Sheffield
News Editors:
Frank Cifaldi, Tom Curtis, Mike Rose, Eric Caoili, Kris Graft
Editors-At-Large:
Leigh Alexander, Chris Morris
Advertising:
Jennifer Sulik
Recruitment:
Gina Gross
 
Feature Submissions
 
Comment Guidelines
Sponsor
Features
  What Your Mother Never Told You About Game Programming
by Peter Warden [Programming, Production]
Post A Comment Share on Twitter Share on Facebook RSS
 
 
January 19, 2001 Article Start Page 1 of 2 Next
 

Game programming, because of its history, has an image that no longer fits with the reality of the job. The popular view of game programmers, at least in nerdy circles, is of a bunch of code cowboys coding six impossible things before breakfast, spending most of their time worrying about how to squeeze those last few cycles out of that inner loop. Micro-optimization and glorious hacks are still very important in some areas of the job, but, as can be witnessed by the ill fate of some recent high-profile projects, the biggest challenge is actually getting the damn game shipped. The successful companies are those that have adapted, and have strategies for getting the projects done.

There's a lot of software engineering literature already out there, and I've mentioned some of my favorites in my list of resources at the end of this article. In the course of my work, I've run across some practices and patterns that I've found useful. Some are quite specific to the unique challenges of creating games, such as the fact that you almost never get what software engineers would consider an adequate list of requirements. Others are not talked about in polite circles because they're treating the symptoms of a bad coding process rather than the causes. I think that these are still worth knowing, because sometimes trying to minimize the damage is the best thing to do.

My main aim with this article is to share some of the ways I've found to keep my code usable in an imperfect environment. The real solution is of course an overhaul of the whole development process, but for the times that isn't possible, I hope these guidelines will be of some help.

The Importance of Naming


"Are the identifiers in the code clear and fitting?" is a useful rule of thumb for determining whether the code I'm writing is going to be maintainable. Since the compiler ignores the names when it generates the final machine code, it's common for naming to be seen as a cosmetic detail. However, I believe the quality of the names is a good indicator of how well thought out the code's design was. Fundamentally, you should be able to describe to someone else precisely what your code does, or you might as well have written it in binary machine code. If you can't describe what it does, then neither you nor anyone else will be able to work with it in the future, which leaves it useless. If you can describe it, then the best way to keep that description for future need is to bake it into the code, by using clear, meaningful identifiers.
This is not about whether you want Hungarian notation, or prefer verbose or snappy variable names, it's about whether they convey any information or just muddy the waters. Seeing a long list of "x"s, "foo"s, or swear words -- or more commonly, very vague and ambiguous nouns -- implies that the writer of the code is confused about what he or she is writing. If I'm having trouble with naming, I revisit my code's design, because I obviously don't have the concepts involved clear enough in my head.

An especially important case is function and method names. A function with a name that's unclear or misleading will waste a lot of debugging and maintenance time. ("Of course Player.Render() polls the keyboard, why didn't I think of that earlier?")

Listings 1.1 and 1.2 for a practical demonstration of how I'd approach a simple function.

// Example 1.1
// Original
int IsFree(int Num, int Mode)
{
int RangeX, RangeY;
for(int i = 1; i <= g_MaxUnit; i++)
{
if(i != Num)
{
switch(Mode)
{
case NX_NEAR_ALL:
RangeX = g_Units[Num].Width + g_Units[i].Width;
RangeY = g_Units[Num].Height + g_Units[i].Height;
break;
case NX_NEAR_ENEMY:
case NX_NEAR_ENEMYDANGER:
RangeX = g_Units[i].Range + g_Units[i].Width + g_Units[Num].Width;
RangeY = g_Units[i].Range + g_Units[i].Height + g_Units[Num].Height;
break;
default:
assert(false);
break;
}
if((abs(g_Units[i].X - g_Units[Num].X) < RangeX) &&
(abs(g_Units[i].Y - g_Units[Num].Y) < RangeY))
{
if(Mode == NX_NEAR_ALL) return i;
if((Mode == NX_NEAR_ENEMY) &&
(g_Units[i].Civilization] != g_Units[num].Civilization)) return i;
if((Mode == NX_NEAR_ENEMYDANGER) &&
(g_Units[i].Civilization != g_Units[num].Civilization) &&
(g_Units[i].Force > 0)) return i;
}
}
}
return 0;
}

// Example 1.2
// Version with meaningful names added
int GetNearUnit(int MyUnitIndex, int AcceptableType)
{
int RangeX, RangeY;
const SUnit& MyUnit=g_Units[MyUnitIndex];
for(int OtherUnitIndex = 1; OtherUnitIndex <= g_CurrentUnitCount; OtherUnitIndex++)
{
const SUnit& OtherUnit=g_Units[OtherUnitIndex];
if(MyUnitIndex != OtherUnitIndex)
{
switch(AcceptableType)
{
case ANY_UNIT:
RangeX = MyUnit.Width + OtherUnit.Width;
RangeY = MyUnit.Height + OtherUnit.Height;
break;
case ENEMY_UNIT:
case DANGEROUS_ENEMY_UNIT:
RangeX = OtherUnit.Range + MyUnit.Width + MyUnit.Width;
RangeY = OtherUnit.Range + MyUnit.Height + MyUnit.Height;
break;
default:
assert(false);
break;
}

const int SeperationX=abs(OtherUnit.XPos - MyUnit.XPos);
const int SeperationY=abs(OtherUnit.YPos - MyUnit.YPos);
if ( (SeperationX
(SeperationY
{
if(AcceptableType == ANY_UNIT) return OtherUnitIndex;

const bool OtherIsEnemy=(OtherUnit.Civilization] != MyUnit.Civilization);
if((AcceptableType == ENEMY_UNIT) && OtherIsEnemy) return OtherUnitIndex;

const bool OtherIsDangerous=(g_Units[OtherUnit].Force > 0);
if((AcceptableType == DANGEROUS_ENEMY_UNIT) && OtherIsEnemy && OtherIsDangerous) return OtherUnitIndex;
}
}
}
return 0;
}

Fat Classes, Fat Functions

Classes packed with members and functions packed with variables both worry me, for pretty much the same reason; nobody ever designs one of these crawling horrors, they just happen. Usually things were added to them a little bit at a time as short-term hacks to get something working, often under pressure to get a feature or bug fixed for a deadline. Once in, they never get removed, other changes to the code start to rely on them being there, and ultimately people take a more relaxed view of hacking the code around some more, since it's a mess already.

The example that sticks most in my mind was a camera class that ended up with responsibilities for player input, the player's on-screen character, informative displays, and plenty of other stuff. The code limped along, though debugging was made a nightmare by the convoluted and nonintuitive paths that the code took through this beast of a class. The real crunch hit when camera changes were needed. The camera class was at that point so closely tied to everywhere else in the game that changing it cascaded bugs through the whole system.

The textbook answer to this sort of problem is to revisit the whole design and figure out how to incorporate the changes cleanly. If, as usual, there's deadline pressure, I now prefer to put in placeholder non-member functions that are as loosely coupled to the rest of the code as possible if a 'home' for the functionality isn't immediately obvious. This is a far from perfect solution, but at least contains the hackiness rather than infecting a whole class, and makes it obvious that the code is a hack rather than hiding it away.

Listings 2.1and 2.2 show one way I might tackle code that was threatening to grow into a crawling horror.

// Example 2.1
// Bad
void
ProcessEverything(void) {

g_Camera.Process();

}

void
CCamera::Process(void) {

// lots and lots of code

g_Player.rotY+=SomeMemberVariableThatGetsTheEffectWeWant;

// lots more code

}

// Example 2.2
// Still bad, but a lot more visible!
void
ProcessEverything(void) {

g_Camera.Process();

// PW-HACK for Alpha!
g_Camera.HACK_AlterPlayersYRotation();

}

void
CCamera::HACK_AlterPlayersYRotation(void) {
g_Player.rotY+=SomeMemberVariableThatGetsTheEffectWeWant ;
}

void
CCamera::Process(void) {

// lots and lots of code
// no hack hidden away here, it's at a higher, more visible level, much more likely to get fixed
// lots more code

}

When You've Got a Shiny New Hammer, Every Problem
Looks Like a Nail

When I learn about a new feature, I naturally find lots of places in the code I'm writing where it could come in handy. The problem is, having little experience with the new feature, I'll inevitably end up using it where it will cause problems further down the line. Trying to debug an object whose inheritance tree looks like a cat's cradle is not fun, exceptions can be equally obscure unless used with care, and working out how templates work on different type parameters can be nearly impossible. Another consideration is whether the next person to work with the code will understand the feature you're using. When you definitely need them, inheritance et al. are lifesavers, but they come with costs too, and can be used to obscure the code far more easily than to make it clearer. A good coding standard will help, but thinking, "Can I use a simpler way?" rather than, "Can I use here?" is most of the battle.

I find this also applies to using new technology. There's a lot of kudos to be gained by learning to use a new technique before your peers, but I have to control my enthusiasm before I decide how to approach a problem that could be helped with a new technique. It's vital to be critical and look at the costs and benefits of using the technology as opposed to going with a less sexy but better-understood technique. A good example is Tom Forsyth's presentation looking at subdivision surfaces and asking what they'll bring to a game. With most graphical techniques the main cost is not implementing them in the engine, it's giving creative control to the artists by giving them the tools to create wonderful effects with them.

 
Article Start Page 1 of 2 Next
 
Comments


none
 
Comment:
 




UBM Techweb
Game Network
Game Developers Conference | GDC Europe | GDC Online | GDC China | Gamasutra | Game Developer Magazine | Game Advertising Online
Game Career Guide | Independent Games Festival | Indie Royale | IndieGames

Other UBM TechWeb Networks
Business Technology | Business Technology Events | Telecommunications & Communications Providers

Privacy Policy | Terms of Service | Contact Us | Copyright © UBM TechWeb, All Rights Reserved.