Introduction

I always had this fantasy that game programmers were the cream of the crop.
Anyone can write a word processor or a Visual Basic application, and anyone can
hack a save file, but game programmers have to push the limits of the machine!
They have to get every aspect of an environment on the screen and somehow not
have a frame rate hit. I always thought game programmers were the cream of the
crop, until I actually joined the industry. The harsh reality hit me almost
instantly and I began to think -even say aloud, I'm amazed our game even runs.
Traditionally, game programmers worked in their basement or garage, piecing
together assembly to put pixels on the screen. Back when the programmer was the
artist because the resolution was so bad actually paying an artist would be
ludicrous. However a lot has changed since then, games aren't made by two
buddies in a garage, they are made by teams of people over many months of
development.
In this day and age, we are not individuals who work on the entire project. We
are a piece of a living breathing company that must work together to achieve
success. Now there are Game Developer Conferences ( http://www.gdconf.com/ ),
Online Tutorials ( http://www.flipcode.com/ , http://www.gamedev.net/ ),
Articles ( http://www.gamasutra.com/ ), Message Boards ( http://www.thedc.com/
), Magazines ( http://www.gdmag.com/ ), and Books (
http://www.satori.org/gamegems/ ) that allow us to share new ideas and
techniques. Yet somehow, with all this teamwork, and all these wonderful new
resources at our disposal, we still manage to code ourselves into corners.
If there was an approach to programming that dodged all obstacles and bugs while
somehow avoiding backtracking or rewriting code, then I'm sure we'd all be using
that technique. Since there is not a perfect way to program, all we can do is
keep our eyes and ears open, observe the programmers around us and see what they
are doing right and wrong.
Here are a few basic programming techniques that I've observed first hand which
greatly improved my own programming habits. The most important thing to
remember when reading them is that although some of them may sound simple and
elementary, knowing a technique is worthless unless you practice it. Many
programmers effortlessly shrug off things like error checking and commenting
conventions as a waste of time. So I'd just like to point out that these
suggestions are intended to improve the amount of time and effort needed to
complete tasks. After practicing only a few of these programming techniques, I
have noticed that my own code writes itself much faster and more efficiently
than it ever did before.
|
Plan For The Worst

Okay, let's just assume that you are a super human programmer, and you're code
is never buggy and never breaks. Let me be the first to congratulate you on
writing the perfect code. However, what will happen when your perfect code is
not given perfect data. Will the code assume a pointer is valid? Will it try
to use a sound file as a texture map?
As basic as it may sound, code should not assume anything. The C language has a
brilliant standard function called "assert," which was designed to trap errors.
Every time your code is given outside data, get in the habit of making sure that
data is what you expected it to be. If it's not, assert AND print a message
that explains exactly what is wrong and why the program asserted.
It is important to give a message, this way anyone can read it and instantly say
to themselves "oops, I see the mistake". Ninety percent of the time that's what
a bug is, a simple mistake. So instead of letting it cripple your game, and
waste everyone's time as people try to trace a bug backward in an effort to
pinpoint it's cause, why not just tell them the moment something goes wrong.
Nine times out of ten it can be painlessly fixed. The other ten percent of the
time the team will be made aware of a major coding issue long before it becomes
an unfixable problem that everyone will just have to work around.
Regardless of what language you're working in. The first subroutine you should
ever write should be a simple error printing function. Make it work like the
language's basic print function, so it's simple and painless for other
programmers to remember and use. Then when the program asserts in a case that
is not obvious from the error message, you can place a break point inside the
error printing function and the program will break exactly when the problem
occurs. This will save time on locating the first occurrence of the problem
while making it simple to debug.
|
Expect To Forget

Don't expect to remember what your code is doing. Projects these days last for
months and even years. You will not remember what you programmed at the
beginning of the project. In fact, it's probably the only real guarantee you'll
ever get. The sky is blue (if it's not raining, and not dusk, or night time),
the grass is green (if it's watered regularly and it's not winter ), and you
will always forget the details of a bunch of text you wrote months ago (always).
So comment everything as if you're teaching it to someone who doesn't know it at
all --someone like yourself six months from now. Think about a team member
telling you there is a bug somewhere in your old code, and you have to revisit
it and fix it. If you can pop open your file and understand what you were
doing, without having to care about variables or parameters. Finding and fixing
the bug will be quick and almost painless.
The technique is simple, when ever you use parentheses, have a comment to read
what the line is doing. Just write it as if you were reading the line out
loud. If someone else read it, they'd speak it letter for letter not knowing
what the variables and crazy calculations were doing. If you read it, you
would speak it after your mind converts all those calculation to what they
really mean. For example, if a line of code that should only be executed if
"(frmp>10)", "(plist[i].bdown & x03)", and "(plist[i].y > pond.y)". Why not
have a comment that says "if( ten frames have passed AND button 3 is still down
AND player not underwater )".
When you comment this way, you will benefit twice. First, anyone will be able
to read exactly what the function is supposed to be doing without even knowing
how to program in that particular language. This makes checking logic much
easier. Second, if the logic is sound, it's easy to spot errors because a line
of code won't do what it says it should be doing. In the previous example, if
the actual code was running when the player was underwater, it would be easy to
spot what part of the code the bug was in because the comment tells us "AND
player not underwater." If the line wasn't commented, you may look at that if
statement a hundred times thinking it was right before it hits you, "wait a
minute, is that greater than, or less than pond.y?"
Comments have to be one of the most powerful tools a programmer has. They are
universal because all programming languages have a way to write comments. It's
a shame that being simple and common also makes them a prime target to be taken
for granted.
This actually leads into our next point.
|
Documentation

At one time I wrote documentation. I remember writing a few dozen pages on
various systems and modules. The documentation itself was sound, but ultimately
counter-productive. No one, including myself, ever used it. Many people would
forget it even existed, and if they came to me and asked about the system I
could explain to them exactly what they needed to know in a fraction of the time
it would have taken for them to read the documentation. Not to mention, there's
a chance someone might read this so-called "documentation" from start to finish
and get absolutely no insight on what they wanted to know. The time it took me
to write those pages of text was completely wasted. Worse yet, every time we
changed one of those various systems or modules, we had to change the document
as well. So I effectively doubled our work load.
However, the solution is most definitely NOT to skip documentation altogether.
Instead, the solution is to make your code files the documentation. Before
every function, make a comment block that explains what it does, how to use it,
and what to watch out for. If it's a complex piece of code, explain the
approach your about to take to the reader (maybe even yourself six months from
now) so they will understand what they are about to see before they jump into
the actual code. If things need to be used in a specific order, say so. If a
function only works in one obscure case tell people right there -as plain as
day.
Now, instead of having to open a separate document and search for what you need
to know, the documentation will be right there in the code. The things you'd
normally have to make an effort to learn will be exactly where you need them,
when you need them, and everyone will be saved the time of looking for them.
Other programmers will be more likely to use your code correctly if they find
that you've laid out all of the issues for them to see.
Also, unlike having a separate document, other programmers will unconsciously
proof read your self documenting code. When anyone approaches you because they
do not understand a piece of code, you can safely conclude that the documenting
comments in that area are lacking. This is something you probably would never
have been aware of if they hadn't just unintentionally told you. So while
you're clarifying what they need to know, comment that piece of code further.
Now the next person won't have to ask you.
|
Human Error and Monkey Business

I read an article on Gamasutra entitled
"Postmortem: Angel Studios' Smuggler's Run" (by Charles Eubacks) and it
introduced me to a concept that was so simple, yet so incredibly effective
-maybe even brilliant. In this postmortem, they gave an entire "What Went
Right" point to something they called a "buildmonkey." The buildmonkey was a
tool that automatically compiled the entire game every morning at 3:00 am and
logged the process. This way, at the start of each day, the programmers would
have a one hundred percent freshly compiled version of their project. If for
any reason the project didn't compile, they would have a log with errors to tell
what went wrong. It even sent emails to key people to inform them if the built
was successful or if it had problems.
Although I'm not proposing everyone go out and code a buildmonkey, I am pointing
out the time saved in writing a tool that's purpose is to perform a repetitive,
mundane task and prevent/catch human error.
I've already touched on using error checking devices in your function calls, and
how much time is saved when mistakes are caught and corrected as they are made.
Now I'd like to point out the exact same advantage in data outside the code base
itself. Most games these days rely heavily on scripts, ini files, and other
data sources to make the game run as desired. However many times, while in
development, a script writer will make a mistake, or an exporter will dump a
command incorrectly.
Instead of waiting until this data is actually loaded into the game to see if it
will work. Why not have a simple DOS program that could check the syntax of
such files on the designer and artist's end. This program could flag mistakes
before the data is ever loaded into the game and would be a huge time saver.
Now I'm sure many of you probably just thought to yourselves "that would be
great, but nobody would actually use it." Well, it's simple. The next time
someone comes to your desk asking you why the game is crashing, you're first
response can always be "did you run the syntax monkey?"
Another great example of a monkey would be a function that prints out the latest
known script commands every time the game is run (on a build flag of course).
This would also eliminate the need to constantly update documentation, and
answer questions like "can you tell me again, what was the syntax for an
AIPoint?"
Humans make mistakes, some more than others. However there are these nifty new
fangled things call "computers" that are made to do mind numbing, mundane tasks
over and over again. It's about time we start putting them to good use, and
save ourselves some time and effort.
|
An Engine Has Belts And Rods

The best metaphor for a game engine that I've ever heard came from one of my
coworkers, Jerry, someone who isn't even a programmer. He actually posed the
question to me, "What is an engine? Why make one? What's the big deal?" I did
my best to explain, and when I was done he paraphrased to say that a game engine
is a lot like a car's engine. Without it, the car could never move, but at the
same time, a running engine without the chassis (tires, etc.) was just as
useless.
I thought this was a good example and listened as he continued to clarify.
Jerry explained that when a piece of your car's engine breaks, you can replace
it. If a belt breaks, you insert a new one and tweak the tension to optimal
performance. At the same time, however, that engine has a transmission, a
block, and rods. They are vital to the engine's functionality, but if you throw
a rod, you can't just pop in a new one without disassembling half the engine.
Then he concluded that if you were going to build a game engine from the ground
up, you'd want to build it with a lot of belts and no rods, so you could
constantly swap parts out and tune performance without bringing the entire
engine to a screeching halt.
Brilliant. Not knowing a thing about programming, he hit the nail right on the
head. One of the biggest things that separates good programmers from great
programmers is writing code that will fit in the engine like a belt, rather than
a rod. The concept of writing flexible, reusable, and replaceable code can
actually be quite simple.
The key to writing general case code is to not let you're sub-routines become
more than a screen long. The way to do this is NOT to write as much compact,
cryptic crap as you can squeeze on one line and NOT to use global variables.
The solution is to break down everything you can into tight, reusable functions
that either perform one task, or call the necessary function to complete one
task.
For example: VectorAdd() would contain a small piece of code to add two vectors
together, while SceneDisplay() would contain calls to PrepRender(),
Render3dObjects(), RenderHud(), RenderDebugText(), and SwapBuffers(). However
both functions would only be a few lines long.
As you break the code into smaller pieces, you will start spotting functions
that can be reused in other places without making much effort at all. In the
previous example, RenderHud() and RenderDebugText() will probably share some
core function calls since they both draw objects on top of everything else on
the screen.
Many believe that there must be some grand design before you can make general
case code. When writing modules and systems, design is an important step, but
that is not the point I'm making. I'm saying, make it a habit to write
everything you program as general and reusable as possible. Then, when you
start designing new systems and modules, you will have much of the functionality
already available, and you need only call those functions in an organized
manner, instead of reinventing the wheel a couple hundred more times.
|
A Shiny New Hammer

As I stated in the beginning of this article, there is no single approach to
programming that will handle the many hurdles you'll be put up against. Many
consider programming a kind of black art form all it's own. I like to think of
us as being more like carpenters than wizards.
Carpenters build structures from raw materials using the knowledge they've
gained through first and second hand experience. Early in their career, maybe
they built a deck in their own backyard with a hammer, nails, saw, and timber.
As they gained experience, doing more advanced things, like adding an extra room
to a house, became more feasible. Before long many tasks like setting cement,
constructing an internal frame, laying insulation, putting up sheetrock, and
shingling the roof all become second nature to them. When a carpenter reaches
that point, perhaps they will get the chance to actually build an entire house
with a team of people such as plumbers, electricians, and so on.
These carpenters are valued not because they have a lot of tools, but because
they know how to use them --and more importantly, when to use them. Give a
carpenter a hammer and ask him to hang a door on it's hinges and he will ask you
for a drill and a screw driver. Give an ordinary man a hammer and he might
assume the door was meant to be hung with nails. Give a child a shiny new
hammer and suddenly everything becomes a nail, just begging to be hit.
It's important to learn patience and good judgment when approaching a problem
instead of charging at it and unconditionally using our latest technique. Just
because you finally read a quaternion tutorial, doesn't mean their useful in all
cases. Use your tools and techniques responsibly and if you don't have the
correct knowledge to make the call on what would be the best approach, admit it.
|
Admit Your Own Limits

We're all only human, and although it would be nice to know everything, we
don't. The danger in situations where we have to complete an unfamiliar task is
not our limited experience or knowledge, it's how we deal with it. It's very
tempting to accept a task you know nothing about and not tell anyone it may be
problematic because you don't want to loose value in management's eyes. In
cases like this, some people follow the reasoning that it would be better not to
say anything than to have someone think you might not know what you're doing.
Sometimes this approach may work. You might keep your mouth shut, work some
extra hours, read a book and a few on-line articles and get the task completed
on time.
More often than not, you keep your mouth shut, you work lots of extra hours,
read anything you can find and the task still gets completed a week to a month
late. You did it, but at what cost?
If you don't have the correct knowledge for the task at hand, get over yourself
and admit it. Don't hide or lie about it. If you express that you are
unfamiliar with or simply don't know how to solve a problem, then something can
be done about it. A phrase as simple as "I've done , but
this is fairly new to me. What direction were you envisioning this code being
written with? Is there something else in the project that is coded with the
same general approach, something I could reference as an example?" You're not
saying, "I can't do this. I'm Worthless -just fire me now!" You're saying "I
may not know this, but I want to. I will do what I can to make this work right
the first time." In many cases other programmers will be happy to share some
knowledge and experience with you. In a way, it's flattering to have someone
ask for your advice. Just remember, it's only flattering once or twice so
listen to what they have to say to you, and everything will be fine. Sit there
with a pad and pen so you can write it all down if you have to. As long as you
don't come back repeatedly and say "what was that again?" That's where you will
push them across the line between feeling flattered and being annoyed.
Regardless, don't be afraid to say something. When you do, someone can teach
you; you will be allocated time to learn the correct techniques; or someone else
can do it. The important thing is not to let the project suffer, and not
admitting your own limits is the best way to do just that. No one will know you
can't handle it if you don't tell them, and it's better to let them know before
hand, than when you're in way over you're head.
|
Fix It, Don't Hack It

Far too often prototype code is written and it ends up becoming game code. Many
times people (management) will walk by and see something on the screen and
assume it's done when it's not. So that programmer gets push onto another task
since they are "done," and a hack gets left behind to plague the project some
day down the line. Don't let this happen to you. Prototype code is fine so
long as it stays prototype code. Once you understand the process, take the time
to write code that will work right, instead of only working "for now."
Further more, if there is code that is causing a problem, don't hack over it.
Fix it. If you can add a line of code that will hack it into a state that
works, why not spend another five minutes to understand why that works. After
that you can make it work in all cases. Do it right and you won't have to deal
with it again. Then there will be one less thing to cause problems in the
future.
|
Step Softly and Carry Nothing

Many of us also get caught in the trap of "figuring things out for ourselves."
Some times we will see nice code that we would like to apply to another aspect
of our programs, however we need to understand what exactly it's doing to use it
properly. Prime examples of this are online tutorials.
When I stumbled onto Jeff Molofee's OpenGL tutorials,
I thought they were heaven sent. With his help, I was able to jumpstart my
use of OpenGL, since those first couple of steps are the toughest. Excited
about the API, I set out to write "my 3d engine" (as I'm sure many before me
have). Before long, I found that I was relying on Jeff's code as a crutch
rather than for what it was intended to be used as. I'd spend countless hours
fiddling with enum values and commands that I really didn't know, then looking
back to the tutorials to see how he was doing it. Finally it occurred to me
just how much time I was wasting because I had to guess what things were doing,
rather than just knowing. Jeff's tutorials were great, but trying to mimic what
he did shouldn't be our goal. Now I have an OpenGL book, and instead of
guessing at the details of the API, I began to understand them, and in turn I've
come to appreciate Jeff's code in a whole new light.
Choose your stepping stones wisely, but remember they exist only to get you
where you're going and will only slow you down if you try to pick them up and
carry them with you along the way. It also helps to leave a few stepping stones
behind for those to come. Others will benefit as you have, but above all, you'd
be amazed just how much you yourself will learn in the process of trying to
teach others.
|
Don't Get Personal

This is, by far, the hardest thing to do. Many of us can get very protective of
our code on an unconscious level. Programming is not an easy thing to do, and
it's nice to be able to take pride in our work and our accomplishments. It
feels good to have someone point at the screen and say "wow that's really cool,"
and to know that you did that.
However there is always more to learn. If someone has a different approach to
something you've written, don't take it personal and don't dismiss them or get
angry with them. Talk with them and one of two things will result. Either
you'll learn how to program better or you can explain to them what you've
concluded and hopefully they will become a better programmer. Being good at
programming is nice and all, but working with others who are good at programming
is an honor that is far too often taken for granted. Be professional and learn
from each other.
|
Summarize

I've touched on a lot of topics in this article. So before I sign off, here's a
quick recap.
Plan For The Worst
Regardless of what language you're working in. The first subroutine you should
ever write should be a simple error printing function.
Expect To Forget
Comment everything as if you're teaching it to someone who doesn't know it at
all --someone like yourself six months from now.
Documentation
Make your program files contain the documentation, put it right there in the
code, exactly where you will need it, and you will ultimately save everyone
time.
Human Error and Monkey Business
Humans make mistakes, some more than others. However computers were made to do
mind numbing, mundane tasks over and over again. It's about time we start
putting them to good use, and save ourselves some time and effort.
An Engine Has Belts And Rods
Writing code that will fit in the engine like a belt, rather than a rod using
concept of flexible, reusable, and replaceable general case code.
A Shiny New Hammer
Give a child a shiny new hammer and suddenly everything becomes a nail, just
begging to be hit. It's important to learn patience and good judgment when
approaching a problem instead of charging at it and unconditionally using our
latest technique.
Admit Your Own Limits
No one will know you can't handle a task if you don't tell them, and it's better
to tell them before hand, than when you're in way over you're head.
Fix It, Don't Hack It
Do it right and you won't have to deal with it again. Then there will be one
less thing to cause problems in the future.
Step Softly and Carry Nothing
Choose your stepping stones wisely, but remember they exist only to get you
where you're going and will only slow you down if you try to pick them up and
carry them with you along the way.
Don't Get Personal
Being good at programming is nice and all, but working with others who are good
at programming is an honor that is far too often taken for granted. Be
professional and learn from each other.
|
Return To The Tutorial Index
|
The views expressed in this document are those of the author, not neccesarily anyone else related to flipCode.
This document may not be reproduced in any way without explicit permission from flipCode and the author.
|
| |