Source Code and Coding Standards


I despise technical books that include source code that doesn't compile. I cursed the name of every author and editor that created these books, filled with errors and broken code. I'm now doomed to join their ranks.

Microsoft Word just doesn't handle C++ source code very well, and the code highlighting has to be turned off, since this book is printed in black and white. I understand, now, why so many programming books are crawling with errors. I apologize to every author and editor I maligned. Until I wrote this book I had no idea how difficult it was. Enough groveling! I will make a valiant effort to check and recheck the source code in this book, and I'll do what I can to set anything right if I find it broken.

Now that my conscience is at ease, you should know something about how to read the source code in this book.

Where the Code Comes From

Every line of source code has its beginning in an actual game. Of course, the code is not 100% verbatim. My front door would be knocked down by a wave of lawyers from Microsoft, Electronic Arts, Mattel, and who knows else. Instead, the code has been sufficiently tweaked to protect the intellectual property of myself and for everyone who was crazy enough to employ me. The original code is much harder to read anyway. It usually contained optimizations and external references that I couldn't easily include in any form. Since they came from nearly twelve years of coding experience, you can imagine the wide variety of style and structure.

If you want to make your own game the source code in this book should give you a head start. You'll find some great skeletal work on which you can hang your own code. I'm even hoping that some of the code in here will save you some headaches so you can concentrate on your game.

The code in this book was written and tested on the Win32 platform under Visual Studio.NET. Console programming is a different beast, and where it makes sense I'll pull some advice from experts regarding a particular solution. If you're looking to use this code on a Win32 box but want to know how programming the same thing on the Xbox or PS2 is different, you're holding the right book.

You can freely use my code any way you see fit. If you happen to make millions, buy me a beer. Most likely you'll look at the code, get an idea to make it better, and write your own. That's the way it should be.

Coding Standards and Style

Source code standards are important. I'm not necessarily a standards dictator. I can find room for other opinions on code style and I'm happy to adopt reasonable standards when and where I must. I look at it like trying to learn a bit of the local language if you travel abroad. The locals will appreciate it and you might even learn something.

Origin Systems didn't have company wide coding standards. I was part of no less than three standards committees while I was there, to no avail. Every time we attempted to discuss C++ bracing style the meeting simply broke down into a screaming match. There were many programmers at Origin that simply wouldn't adapt to anyone else's style. It got so bad that somebody wrote a little utility that would parse a source file and change the bracing style from one to the other. Madness!

Your coding standards and style exist solely to communicate useful information to other programmers, and sometimes a future version of yourself.

I use a coding style in this book extremely similar to what I use professionally. The only departures are those that make the code simpler to read. For example, the source code in the book frequently eliminates obvious error detection and handling. If I used every line of source code exactly as it appeared in real projects this book would have to be twice as long. It was a tough tradeoff, but it's better to have more examples and leave the obvious stuff out of the book.

A Tale from the Pixel Mines

At Origin Systems, a particular programmer on Martian Dreams used goto at a frequency you'd find unpleasantly surprising. The new version of the Borland compiler was on everyone's desks, fresh from the presses. He'd just finished installing it, and went to lunch. I went to his machine and edited the compiler executable. I changed the keyword goto to goat. When he came back from lunch, three or four of us were pouring over the Borland docs in my office. We told him that Borland's software engineers decided to eliminate goto from their implementation of C. We were astonished, but he didn't believe us until he compiled a small test program in his newly installed compiler and received a "unexpected identifier or keyword: goto" message for his trouble. We told him the truth before he reached someone at Borland's customer service department.

Using Prefixes

Use one prefix letter per identifier, and don't under any circumstance worry about using prefixes for type, like Win32 APIs use. Modern IDEs like Visual Studio.NET expose the type of an identifier with a tooltip, so programmers don't have to clutter the prefix with redundant information.

g

Use with global variables -

g_Counter

m

Use with member variables -

m_Counter

V

Use with virtual functions -

VDraw()

I

Use with Interface classes -

class IDrawable

I've seen some crazy use of prefixes that attach three or more characters to the front of any identifier. It must be hard to program in Hungary. The problem with this style is that every identifier that has the same prefix looks exactly alike. That's why the prefix should be as small as possible, and separated from the identifier with an underscore: it conveys useful information without overpowering the identity of the variable name. In your own code, feel free to add more prefixes to this list as you find good use for them. Just don't go overboard!

Prefixing variables for scope is an excellent use for prefixes. Programmers who change the value of something with global scope need to be slapped in the face so they can take proper precautions. Class member variables have a different scope than local variables. The 'm' prefix is a clean way to differentiate locals and members when they are used in the same method.

Virtual functions are powerful, and therefore dangerous when used to evil ends. A prefix on virtual functions reminds programmers that they should call the parent's overloaded virtual function, and that the cost of calling the function is higher.

I find it useful to apply a prefix to interface classes, ones that only define pure virtual functions and no data members, so programmers feel safe multiply inheriting from them. I avoid multiple inheritance of non-interface classes, and I advise you to do the same. The resulting code can be very confusing and hard to maintain.

Capitalization

I use capitalization to distinguish different classes of identifiers and make identifiers easier to read.

  • Variables and Parameters: Always start with lower case, use a capital letter for each compound word—g_BufferLength, m_BufferLength, returnValue

  • Classes, Functions, Typedefs and Methods: Always start with upper case, capitalize each compound word—SoundResource, MemoryFile

  • Macros: All capitals, separate compound words with underscores - SAFE_DELETE, MAX_PATH

The first two capitalization styles help programmers distinguish between definitions of class and instances of those classes:

 SoundResource soundResource; MemoryFile memoryFile; 

Macros, a source of frequent pain and suffering, should boldly state their existence in all capitals. If you want to find the definition of a macro it's easy to search for the #define MACRO_NAME. This sets them apart from functions or methods.

Const Correct Code

I try my best to make code const correct, and the code in this book is no exception. I'm sure some of you hardcore const correct programmers will be able to throw a few thousand consts in where I've forgotten them.

Const correctness is a pain in the ass, but it's important. Adding const to member variables, function returns, pointers, and references communicates important information to other programmers.

Strings and Localization

If you make your game for English speakers only, you're slashing your sales. Europe and Asia, especially mainland China, are hungry for quality games. Most players will put up with English but they'd rather get their hands on a good translation in their native language. Good localization technique deserves an entire book and a masters degree in foreign cultures. Since the book as a decidedly Win32 bias, I'm going to use TCHAR as the basic character data type. It can compile with or without _UNICODE defined. You'll notice that CHAR and unsigned CHAR is still used in code that needs eight bit values, specifically when dealing with graphics or sound data.

While I don't do this in the book, it's a good idea to use a good string class to hide the data type of the string. Most string classes depend on #defines or compile macros to set them into single or double byte strings, and the code that uses them doesn't have to care.

In the code samples I put literal strings in for clarity. In a real project every string that could possibly be seen by anyone playing the game is declared in a string table. The string class that I mentioned above is a great place to stick a constructor that uses the resource constant; and you have a simple way to grab strings from the resource table:

 RString unknownErrorMessage(STRING_UNKNOWN_ERROR); 

Regarding the resource constant, I don't attempt to encode the exact text of the string in the macro. It takes too long to type and muddles the code. I usually find a good abbreviation.

One final note about strings in real game code: debug strings or names for objects are fine as literals. You can declare them at will:

 if (impossibleError == true) {   OutputDebugString(_T("Someone enabled the impossible error flag!")); } 

Commenting

Really good code comments itself, and I'm hoping the code in this book does exactly that. Good variable names and logic should obviate the need for wordy explanations. In the book, I'll sprinkle comments in the code where I think they do some good, but you'll usually find some meaty explanation immediately after the code sample.

In a real game, the meaty explanation should be inserted into the code, perhaps at the beginning of the file, so that other programmers can figure out what's going on. What seems obvious the moment you type the code degrades linearly with time to a confusing mess. For me, total confusion sets in approximately three months after I write the code—how could I possibly expect anyone else to understand it if I'm completely lost in something I wrote myself?

I always start projects with the intention of putting good comments in my code. I always end projects disappointed in other programmers and myself—we just didn't have enough time. That happens. Projects under pressure will see comments disappear because the programmers are spending 100% of their time coding like mad. The best policy is to start off with a lean, light commenting policy and keep it up as long as you can. If there comes a point in the project where comments are dwindling, try to make a good effort to go back in the code base after the project releases to document the code. A good friend of mine at Microsoft told me that shipping the product was a good feature. I agree.

Namespaces

The only namespace I use in the book is one that distinguishes code that I want you to write yourself. It is usually pretty simple stuff like line drawing or code that exists on the Internet. Here's how to recognize it:

 yourcode::LineDraw(0,0,10,25); 

Error Handling

There is very little error handling code in this book, so little that when I look at it I cringe. The fact is that robust error code gets a little wordy, and I wanted to spend time on the lines of code that will teach you about making games. You can use any form of error checking you want, and I talk about some different options in the chapter on debugging.

Best Practice

Every hard exit in your game should have an error message that is presented to the player:

"Bummer - your game is hosed because of some bug in objectdata.cpp, line 6502". Use __FILE__ and __LINE__ to identify the offending code. Unique error codes are a hassle to maintain. This data can be invaluable for the development team and customer service after the game ships. Many a patch or workaround traces their roots to a few hundred telephone calls and emails that finger a particular error code.

Where Is the Code? Must I Actually Type?

There's no CD in this book, as you've noticed. When we planned this book, the publisher convinced me to create a web site where you can download all of the code examples. This has two distinct advantages. First, the book is much cheaper to print, and therefore cheaper for you to buy. Second, it's much easier to fix problems on a web site that it is to fix them on thousands of CDs. Find the website here: http://www.paraglyphpress.com.




Game Coding Complete
Game Coding Complete
ISBN: 1932111751
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net