Tricks of the Windows Game Programming Gurus:Journey into the Abbys

All ITKnowledge Source Code Search Tips Advanced Search |

| Tricks of the Windows Game Programming Gurus by Andre LaMothe Sams, Macmillan Computer Publishing ISBN: 0672313618 Pub Date: 09/27/99

Search this book:

Previous | Table of Contents | Next

### General Game Programming Guidelines

Next I want to talk about some general game programming techniques and philosophies that you should think about and try to adopt (if you can) to make game programming much easier.

To begin with, video games are ultra-high-performance computer programs. No longer can you use high-level APIs for time-critical or memory-critical code sections. For the most part, you must write everything yourself that is related to the inner loop of your game code, or else your game will suffer terrible speed and performance problems. Obviously, this doesn’t mean that you can’t trust APIs like DirectX, since DirectX was written to be as high-performance and “thin” as possible. But in general, avoid high-level function calls.

With that in mind, take a look at a list of tricks to keep in mind as you’re programming.

Don’t be afraid to use global variables. Many video games don’t use parameters for a lot of time-critical functions, instead using a global parameter passing area. For example, if a function looks like this:

       void Plot(int x, int y, int color)

       {

       // plots a pixel on the screen

       video_buffer[x + y*MEMORY_PITCH] = color;

       } // end Plot

The body of the function takes less time than the function call. This is due to the parameter pushing and popping on the stack. In this case a better method might be to create a global parameter passing area and then make assignments before a call, like this:

       int gx,gy,gz,gcolor; // define some globals

       void Plot_G(void)

       {

       // plot a pixel using globals

       video_buffer[gx + gy*MEMORY_PITCH] = gcolor;

       } // end Plot_G

Use inline functions. You can improve the previous trick even more by using the inline directive to get rid of the function call completely. The inline directive instructs the compiler to make its best attempt to put the code for the function right where it’s called, rather than making the actual function call. Of course, this makes bigger programs, but speed is more important. Here’s an example:

       inline void Plot_I(int x, int y, int color)

       {

       // plots a pixel on the screen

       video_buffer[x + y*MEMORY_PITCH] = color;

       } // end Plot_I

Notice that I didn’t use globals because the compiler will in effect perform the same type of data aliasing. However, globals would come in handy if only one or two of the parameters were changing between calls because the old values could be used without reloading.

Always use 32-bit variables rather than 8- or 16-bit. The Pentium and later processors are totally 32-bit. This means that they don’t like 8- or 16-bit data words, and in fact, smaller data can slow them down due to caching and other related memory addressing anomalies. For example, you might create a structure that looks something like this:

       struct CPOINT

       {

       short x,y;

       unsigned char c;

       } // end CPOINT

Although this may seem like a good idea, it’s not! First, the structure itself is now five bytes long—`(2*sizeof(short) + sizeof(char)) =5`. This is really bad, and it’s going to wreak havoc on the memory addressing. A better approach is the following structure:

       struct CPOINT

       {

       int x,y;

       int c;

       } // end CPOINT

> Tip: STRUCTs in C++ are just like CLASSes, except that they have default PUBLIC visibility.

This new structure is much better. For one thing, all the elements are the same size—that is, `sizeof(int)` = 4 bytes. Therefore, a single pointer can be incremented on a DWORD boundary to access any member. Of course, the new structure is now `(3*sizeof(int))` = 12 bytes, but at least it’s a multiple of 4 or on a DWORD boundary. This is definitely going to improve performance.

In fact, if you really want to make things rock, you can pad all structures to make them multiples of 32 bytes. This is the optimal length due to standard on-chip cache line sizes in the Pentium class processors. You can pad manually by adding dummy variables, or you can use a compiler directive (the easy way). Of course, this may waste a lot of memory, but it may be worth it for the increase in speed.

Comment the heck out of your code. Game programmers are notorious for not commenting their code. Don’t make the same mistake. Clean, well-commented code is always worth the extra typing.

Program in a RISC-like (Reduced Instruction Set Computer) manner. In other words, make your code simple rather than complex. Pentium and Pentium II processors in particular like simple instructions rather than complex ones. And making your code longer, with simpler instructions, makes it easier for the compiler. For example, don’t do this:

       if ((x+=(2*buffer[index++])>10)

       {

       // do work

       } // end if

Do this:

       x+=(2*buffer[index]);

       index++;

       if (x > 10)

       {

       // do work

       } // end if

There are two reasons for coding like this. First, it allows a debugger to put break points between code sections. Second, it makes it easier for the compiler to send simplified code to the Pentium, which allows it to process more code in parallel using multiple execution units. Complex code is bad!

Use binary shifts for simple multiplication of integers by powers of 2. Since all data in a computer is stored in binary form, shifting the bit pattern to the left or right is equivalent to multiplication or division, respectively. For example:

       int y_pos = 10;

       // multiply y_pos by 64

       y_pos = (y_pos << 6);  // 2^6 = 64

       Similarly,

       // to divide y_pos by 8

       y_pos = (y_pos >> 3); // 1/2^3 = 1/8

You’ll see more tricks like this when you get to the optimization chapters. Cool, huh?

Write efficient algorithms. All the assembly language in the world isn’t going to make an n^2 algorithm go faster. It’s better to use clean, efficient algorithms rather than brute force.

Don’t optimize your code as you program. This is usually a waste of time. Before you start heavy optimization, wait until you’re done with a major code block or until you’re done with the whole game. This will save you time in the end because you won’t have to deal with cryptic code or optimizations that aren’t necessary. When the game is done, that’s when you should start profiling and finding problem areas to optimize. On the other hand, don’t program sloppily.

Don’t write a lot of complex data structures for simple objects. Just because linked lists are cool doesn’t mean you should use them for a fixed array that you know will always be around 256 items. Just allocate it statically and be done with it. Video game programming is 90 percent data manipulation. Keep your data as simple and visible as possible so you can access it quickly, do what you need to, and move on. Make sure the data structure fits the problem.

Use C++ sparingly. If you’re a seasoned professional, go ahead and do as you please, but don’t go class crazy or overload everything to death. In the end, simple, straightforward code is the best and easiest to debug. And I never want to see multiple inheritance!

If you see that you’re going down a rocky road, stop, back up, and take a detour. I have seen many game programmers start down a bad programming line and bury themselves. It’s better to realize you made a mistake and redo 500 lines of code than to have a generally undesirable code structure. So, if you see a problem with what you’re doing, reevaluate it and make sure that the time you’re saving is worth it.

Back up your work regularly. When you’re writing game code, you’re going to lock up the system fairly frequently. Redoing a sorting algorithm is one thing, but redoing the AI for a character and the collision detection is another.

Before you start on your game projects, be organized. Use reasonable filenames and directory names, come up with a consistent variable naming convention, and try to use separate directories for graphics and sound data rather than dumping everything in one directory.

Previous | Table of Contents | Next

Copyright © Macmillan Computer Publishing, Inc.

Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.