Gamasutra: The Art & Business of Making Gamesspacer
Sponsored Feature: Programming Sins - Common Errors From Down In The Trenches

Printer-Friendly VersionPrinter-Friendly Version
arrowPress Releases
April 19, 2014
PR Newswire
View All





If you enjoy reading this site, you might also want to check out these UBM TechWeb sites:


 
Sponsored Feature: Programming Sins - Common Errors From Down In The Trenches

July 13, 2011 Article Start Page 1 of 3 Next
 

[In this article, originally published in Game Developer Magazine and brought to you now in association with Intel, Michael, Noel, and "Anonymous Developer" share several programming sins relevant to the game industry. It reminds me of one programming "sin" at Insomniac Games. We were very close to shipping Resistance: Fall of Man and there were a few corner cases in the multiplayer code that were driving us crazy.

Specifically, there was a timing issue with players dropping from the game. A player would leave the game and other players would release the associated resources, but eventually an old message from the Interwebs would show up and reference the dropped player. Ugh, timing was important and very difficult to repro.

This would result in a NULL pointer being accessed. Specifically, a member function would be called on a NULL object. Since we didn’t have time to track down the issue, we added a check "if( this == NULL ) return;" at the top of the offending function.

Wow, that’s ugly. But it works because member functions are ultimately independent of any instance of the object. Hacks like this are a maintenance nightmare. Ultimately, this was an artifact of "late joining" not being architected from the start of the project. As you can image, player management was a big rewrite in Resistance 2.

I’m glad Intel has the opportunity to republish this great article on Gamasutra. As Albert Einstein said, "Anyone who has never made a mistake has never tried anything new." Please share your "programming sins" in the comments!! -- Orion Granatir]


From the very first line of code an engineer writes, he or she starts to develop their personal list of "dos and don'ts" that, even if they are never written down, have a tremendous effect on how we design and build our games. The list evolves and changes over time -- we add to it, delete from it, and re-evaluate the lists of others. Experience is the driving force behind a lot of the changes; in short, we make a mistake, and we learn from it. These game programming sins are essentially a number of dos and don'ts with some recollections of how and why they came to be on this list.

Michael A. Carr

Creating Obfuscated Code

For those that don't know me, one of my major flaws is that I simply don't remember everything. My brain, it would appear, is completely incapable of storing all the facts and information I ask it to. Over the years, I have used different methods to help me remember, all with varying degrees of success. My current system is to write everything down. I carry a black leather writing notebook with me, and I take lots of notes. I use a P.D.A. for some things like contacts and mind maps, but when it comes to making lists and notes, paper and pen have yet to be beaten.

One of the effects of this forgetfulness is that I couldn't tell you the intimate details of a function I wrote three weeks ago, let alone six months or a year ago, without at least re-reading it and refreshing my memory. It's because of this that I consider obfuscated code a sin. This also fits nicely with working in a team of programmers where someone might have to debug and/or add new functionality to someone else's code. The quicker it is to understand, the easier it is for them to make the required modifications.

int m_MyMumIsBetterThanYours;

No it wasn't a game about mums ... although I wonder if there is a game there somewhere ...

bool m_BumCheeks;

Enough said.

float m_Saving;

Is this a flag to indicate saving, in which case why a float? Or is it a percentage of save completed?

void * m_pAudioSample;

Not very useful. Wouldn't SAudioSample * m_pTheWarCry; be more useful?

int CCharacter::GetLife( int y )

Nothing wrong with this function ... only why is there a variable passed in? More importantly, y isn't exactly very descriptive. Turns out this function did indeed get the amount of life and return it; and while it was there, it also updated the life value by applying the damage modifier to the life counter, and also applied the adjustment of y. When this function wasn't called every tick, the whole life counter on the character broke.

Abusing ternary operations. Consider the following code. (Remember that this would normally be on a single line, so you would have to scroll to see the entire line.)

if (CPhysicsManager::Instance().RayCast(m_Position + (CVector::Up * METRES(2.0f)), m_Position - (CVector::Up * METRES(2.0f)), &contact_data, pActor->GetPhysicsActor() ? pActor->FindRealActor()- >GetPhysicsActor() : NULL, ePhysicsShape_Static))

Would you have spotted the use of ? and : inside the function parameter list?

Some coding standards I have worked with ban the use of ? and : altogether, mainly because it's easy to abuse. As you can see, it contributes handily to the jumbled code in the example above. However, there are cases where I consider them to be fair enough. That's usually where the use is obvious. For example:

m_Level = level_specified ? start_level : default_level;

result = a > b ? b : a;

Abbreviating English. There was a time when the length of our variable or function names would have a significant effect on the performance of the compiler. This has not been the case for a very long time, but some engineers seem to like using shorthand.

int NmbrChars( );

vs.

int GetNumberOfCharacters( void );

int m_LCnt;

vs.

int m_LifeCount;

English is my first spoken and written language, and I find it easier to read code that says what it is in plain English.

Not Failing Gracefully

The world would be a dull place if we all had the same thoughts, the same ambitions, ideas, and methods of working. But there are some thoughts and methods that should be discouraged whenever and wherever they are encountered.

Working on a project that was just over halfway through its development cycle, I was investigating why the game kept crashing whenever a specific sound event was triggered. I quickly tracked the problem down to some missing audio files, an easy fix. Still, it was the fact that a missing file was causing the entire game to crash that got me looking a little more closely at the sound manager. It turned out that the manager never validated its data; even though a file had failed to load, it carried on processing the non-existent sound data as though the file load had been successful.

Talking to the engineer who maintained the system, I thought he was pulling my leg when he said he considered it acceptable behavior for the code to crash when something goes wrong. After he repeated himself, I realized that he was serious. From his perspective, the problem was the missing data, not that the manager didn't handle itself in a graceful fashion.

There are always going to be unexpected issues that arise, certainly during development and very possibly after our games have shipped. There might be a missing texture, a corrupt file, an out-of-range data value, or even a lack of resources. We can and should make our code as bulletproof as we possibly can. The game should be continuously fighting to keep itself running. This makes the game experience more stable for the player, and if something does go wrong, there should be fail-safes in place so that he or she will not even notice.

Here are three basic coding practices that I advocate as a minimum. You might also recognize what I'm talking about as essentially being "defensive programming."

Pointers. Pointers are pointers because it's possible they might be NULL (otherwise they would have been references). Validate pointers at least once before accessing them.

Validate Data. Sanity checking data can help prevent a lot of issues. Clipping them to valid ranges means you are less likely to have invalid calculations later on down the line.

Default State. Some data can't be clipped or ignored, an example might be a texture, in this scenario having a memory resident fallback is a good solution. During development it can be bright pink and yellow and stands out a mile while in shipped mode it can be transparent.


Article Start Page 1 of 3 Next

Related Jobs

Activision Publishing
Activision Publishing — Vancouver, British Columbia, Canada
[04.19.14]

Principal Graphics Programmer
Insomniac Games
Insomniac Games — Burbank, California, United States
[04.18.14]

Associate Engine Programmer
Insomniac Games
Insomniac Games — Burbank, California, United States
[04.18.14]

iOS Programmer
Insomniac Games
Insomniac Games — Burbank , California, United States
[04.18.14]

Senior Engine Programmer






Comments


Matthew Campbell
profile image
I'm very glad to see the story about premature optimization.. I get so tired of seeing folks out there who don't think about how their code and data is going to perform using that phrase as the reason.. There is always a balance but if you put off optimization until the end or don't consider it until later, you end up with exactly what the story described: There are not many hotspots, the whole program is just plain slow!

Daniel Higdon
profile image
Agreed - designing for performance is not premature optimization. Especially in our field, it's a design constraint.

Mathieu MarquisBolduc
profile image
There`s architecture and then there's coding. Yes, you should design your architecture for performance (among other things). But "premature optimization" refers to local hacks that you put while coding, before profiling.



Code for robustness and easy maintenance, and then profile. Dont make the code any uglier if it wont save you anything significant. Also, start profiling early in a project and do it regulary. If there is something I learned, its that profiling never return what you expect :)

Nick Harris
profile image
Tremendous article. Are you the same Noel who wrote this?



http://gamesfromwithin.com/data-oriented-design

brandon sheffield
profile image
yes, it's the same noel.

Tyler Hoffmann
profile image
Excellent Article. I would love to see more like this for both good and bad things to do when programming games. The way everything was described was at a perfect level between technical and enjoyable reading.

John Byrd
profile image
I loved the bit about cache coherency at the end. I've lost count of the number of programmers who have learned that accessing memory isn't free. It's really only the sort of thing you can get after you've been on a few projects.

Justin Nafziger
profile image
Is the 'Cut-and-Paste' section trying to be ironic by having a 'Cut-and-Paste' bug in it?



[the 'c' and 'd' in DrawSquare() should be 'x' and 'y', respectfully]

Tiago Raposo
profile image
Excelent article. So good, in fact, that yesterday I finished a huge amount of printf commands on a code of mine, just to track a logical bug. Looks like I'm the hot spot!

Gregory Kinneman
profile image
Excellent article. Readable yet technical.

Cartrell Hampton
profile image
Hey.



Flash game developers should especially be taking notes here. I've seen some atrocious code written by others from projects that have been passed on to me. I guess they figure "just because it's ActionScript and Flash", they can throw code together any kind of ol' way. And this is AS3.0. Don't even get push my buttons and me started on timeline code, and the obsolete AS2.0.



- Ziro out.

Matthew Mouras
profile image
Fantastic article. Really a pleasure to see this on Gamasutra. I got red-faced at a couple of points thinking, "Ooo... I remember doing that on my last project." Thanks so much for the post!

Kyle Jansen
profile image
Interesting read. I'm a bit curious, though, as to what other programmers think of my own solution to the printf() debugging problem.



For a while, I did just use printf(), but I independently realized just how problematic it was. My solution involved adding a few small functions to the common library:



void Trace(char* str, ...){

#if WARNING_LEVEL <= 0

va_list vlist;

va_start(vlist, str);

printf("Trace: ");

vprintf(str, vlist);

printf("n");

va_end(vlist);

#endif

}



void Debug(char* str, ...){

#if WARNING_LEVEL <= 1

va_list vlist;

va_start(vlist, str);

printf("Debug: ");

vprintf(str, vlist);

printf("n");

va_end(vlist);

#endif

}



And so on for Warning(), Error() and Crash(), the latter including a return code argument and a call to exit(). All it really does is pass the message along to printf() - I can do stuff like:



Error("Entry "%s"."%s"."%s" not found, trying less specific search", name, subname, variant);



or



Crash(ERROR_NOFILE, "Could not open tile DB (%s)!", FILENAME_TILEDB);



or



Warning("Tried to add Tile "%s" to the cache, but there's already a Tile with that name, ignoring...", n.getName().c_str());



So instead of having a bunch of commented-out printf() debug statements, I have relatively clean-looking calls to common functions. It even keeps the format consistent - if I see something printed out preceded by "Error: " or "Warning: ", I know that it's not normal.



The code is even more maintainable - I can tell the difference between a UI-related printf() and a debugging-related printf() instantly (the game's currently command-line only - it helps to focus on the gameplay when I don't even HAVE graphics to worry about).



And I can vary just how detailed my debugging needs to be - a build with Trace() enabled takes about two minutes to load a game save right now, compared to a few seconds when at Debug().



So, how does this game-dev newbie's code stack up?

Jonathan Lawn
profile image
We (in my non-game dev) do this automatically for all code.



We also add a global variable to control the logging level too though, so that you can change the log level in a debug build at runtime. No-one really uses it though! It might be handy for you though, depending on the other tools you have, and whether performance is normally an issue in your testing.

Pallav Nawani
profile image
I had this kind of code in my early games, then got rid of it after I realised that there was no need of half a dozen trace levels. Now I just do minimal logging and throw exceptions if an unrecoverable error happens. If an error is recoverable, I just log it and continue.



When an exception happens, I show a MessageBox, log it, and quit.

Joakim Hagdahl
profile image
Instead of using logging levels just use categories for all logging. In games there are always a number of different systems that all have a need for their own logging. By using categories you can quickly mask out any systems that you don't care about for the time being and you can keep all the logging intact.



However, most of the time the log is not something an artist or designer has access to and they would much prefer having warnings and errors shown to them as message windows.



And testers love having access to the log and programmers love having access to a minidump/crashdump.



Throw up a bug report window if the tester/artist/designer chooses that. Attach all the information that the user selects. Send that report to your bug db.



Etc.

Andrew Grapsas
profile image
I've seen all of these things done very, very well! The bug report window is clutch!

Rob Schatz
profile image
The Anonymous Developer was definitely my favorite - instead of preaching, he was actually telling the reader exactly how game devs think. He mentions the word "lazy" a lot and it's true. But don't misinterpret. The person probably has seen a lot of projects where someone is ready to sound a red alert at the smallest issue. And if not, well...

Daniel Kinkaid
profile image
God, I loved the part about the Engineer who thought crashing was an acceptable behavior. IT IS NEVER ACCEPTABLE.



Sure, the application may not be able to proceede, but you can certainly intercept the fault, give an error code on WHY it happened [do you want to answer the call of "Hey, my app went away for no reason. Can you reproduce the problem?"], and give no other option but to exit the application, but allowing a crash is NEVER the correct answer.

Kain Shin
profile image
Like

Matthew Fairchild
profile image
very interesting to read! I'm only about to begin to study game engineering, but a while back i started teaching myself programming through books ,tutorials on the internet etc. and find it particularly interesting that things i just started doing because i don't know better (until now) actually pop up on this list put together by professionals ^^

Thomas Engelbert
profile image
Great article. One of my sins back when I started learning OOP was having MASSIVE constructors. Like:



public Ship(string type)

{

switch(type)

{

case "scout":

[dozens of lines for every attribute]

break;



case "cargo":

[dozens of lines for every attribute]

break;



case "fighter":

[dozens of lines for every attribute]

break;



default:

[dozens of lines for every attribute]

break;

}

}



Most sins I've seen and done are about students, and what and how they're tought. I'm not a big fan of having code-hygiene "at some later point", when everybody learned one or another way to get things done, and then suddenly has to get used to completly new styles and rules. Before the students have their first team-assignment they just have to know how to code in a way that everyone else involved with that code can read and understand it by him- or herself.



Uh, and on multinational teams - always use english (or another language on which everybody agreed) for just about everything in the code. First time I've seen a fellow student using french (which I don't speak) for comments, variable-names, etc., I had big, shiney eyes and questionmarks floating around my head, asking him if he was effing kidding me... Ah, good times... *gg*



But I must say - I don't see that much of a problem with using abbreviations. As long as you a) put the full name in the comments and b) have an IDE that brings up a tooltip with said comment when you hover the mouse over the variable-names. But, well, there are pros and cons for both...

Kevin Fee
profile image
I just want to point out that the code crashing on the missing sound got you LOOKING FOR THE MISSING SOUNDS.

I see so many projects where, even in the full, final version, there's a log file a mile long of:

Audio file sfx_something_002.wav not found

Audio file sfx_something_002.wav not found

Audio file sfx_something_002.wav not found

Audio file sfx_something_002.wav not found

Texture file tile_something_05.bmp not found

Audio file sfx_something_002.wav not found

Texture file tile_something_03.bmp not found

Audio file sfx_something_002.wav not found

Audio file sfx_something_002.wav not found

Texture file tile_something_08.bmp not found

Texture file tile_something_03.bmp not found

Audio file sfx_something_002.wav not found

Audio file sfx_something_002.wav not found



(Yes, sfx_something_002.wav is the same file, as is tile_something_03.bmp)



See, if it crashed instead of logging, you would have actually bothered to fix it. On development builds, you should crash early and crash often, on every single tiny error, even a WARNING should FAIL your build. However, unlike said engineer, you should always validate. This is a part of "crash early". If the file doesn't exist, crash when the file is requested, not at some random time down the line when the code is trying to play the garbage on the heap.



On release builds, go ahead and fail gracefully, though. But Dev builds? Fail hard.

Michael Carr-Robb-John
profile image
Lets imagine a mid-sized development team, 7 engineers, 12 artists, 2 designers and an audio designer. Do you really want to interrupt 21 people while waiting for 1 person to check-in a file that they might of forgotten or had lost?



Crashing the game means the problem gets fixed asap agreed, but it isn't very efficient for modern game development.

Sean Clark
profile image
I am currently enrolled in DeVry University's Game and Simulation Programming program, and just starting out in the actually C++ programming portion. I am looking to become a Gameplay Programmer, so I am very grateful to have read this article, as it was very informative as to how the code is both used and misused in game development applications. I am currently learning C++ with the DarkBasic library, is that library commonly used in development? If not, what libraries are most commonly used? Also I was looking to start learning game engines now, does anyone have a suggestion for a free or low cost engine to start working with?

Samuel Batista
profile image
My suggestion to you is to learn how to use Google properly and get used to doing the legwork yourself. I don't know much about DarkBasic, other than it's a starter friendly language.



If you're working with C++ you should be working with a library that provides low level access to OpenGL or DirectX, so you can learn how to work with the graphics card. SDL comes to mind, but there are a lot of options. Whatever it is that you're doing, if you don't like it keep moving, there's lots of languages and APIs to make games with.

adeel shahid
profile image
very helpful article.

m a noob as far as game programming is concerned.

What i consider my worst sin is that i dont write the algo first, instead i just start typing in the compiler until i have the skeleton correctly drawn in my mind.

it leads to tedious re-writing of code and some constantly changing logic.

But recently i have developed the habit of constructing an algo on paper which saves a lot of time, because when i start typing i just know what to write at which point.


none
 
Comment: