It's free to join Gamasutra!|Have a question? Want to know who runs this site? Here you go.|Targeting the game development market with your product or service? Get info on advertising here.||For altering your contact information or changing email subscription preferences.
Registered members can log in here.Back to the home page.

Search articles, jobs, buyers guide, and more.

by Peter Warden
Gamasutra
[Author's Bio]
January 19, 2001

The Importance of Naming

Over-general Designs

Printer Friendly Version
 
Discuss this Article

Letters to the Editor:
Write a letter
View all letters


Features

What Your Mother Never Told You
about Game Programming

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?")

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

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.1 and 2.2 show one way I might tackle code that was threatening to grow into a crawling horror.

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 <feature> 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.

________________________________________________________

Over-general Designs

 


join | contact us | advertise | write | my profile
news | features | companies | jobs | resumes | education | product guide | projects | store



Copyright © 2003 CMP Media LLC

privacy policy
| terms of service