Game Programming Using CDX - With Source Code of Space Invaderz
Disclaimer:
This software is delivered as
is. The author takes no responsibility of any kind either implied
or expressed. You agree to hold the author harmless of any
charge.
You can distribute this code (SI.h
and SI.cpp)
as
long as it is NOT modified. You are allowed to modify this code
as much as you want, but you can NOT claim it as yours or make a profit out
of it. All you find here is
free as long as it is used for educational purposes.
Table of Contents:
Space Invaderz's code is far from being optimized and finished and some things could be done much better, but since this was an experiment, I will leave it as it is. If you have suggestions or questions, please feel free to send me an email. I will try to answer any questions you might have the best I can, but I am not an expert in game programming by any means and people on the CDX mailing list are probably your best bet.
Before I start going through the design of Space Invaderz and explaining what and how I did it, I would like to outline my goals and requirements. Look at it as this was a design outline for a project Space Invaderz. This means I will not go into C++ details and I will assume you already have minimum Windows programming knowledge and you understand what the CDX library is all about. I will try to touch base on the major game programming issues, but I will not go into details, instead I will try to point you in the right direction and show you the big picture. As you can see, this would be something like "How to…" quick guide to programming Space Invaderz using CDX. If you still find this interesting and you think you can learn something from this then get a mug of coffee, get yourself comfortable and enjoy.
Windows is event based OS and typical windows based application has small functions that handle events like keyboard key press, mouse movement, timers, etc… Everything boils down to one main loop that checks if there are any messages that have to be handled (did user press fire button?), if there is - dispatch the message to the right handler, otherwise process the stuff we want to get done (shoot the aliens). This is all done in WinMain function and graphically it would look like this:

After initialization, the main loop (shown above in red) will run until we explicitly terminate the application. Here we first check if there are any windows messages to process, and then we check if our application is still active (game windows has focus). If we don't have the focus, we yield to other threads, but if we have the focus we then check timing. This is very important since all the movements in this game are timed. This brings up the very interesting subject of game timing which I will be touching on a little later. The problem is that each computer runs on different hardware and software configuration and execution of the same piece of code will take different amounts of time on different platforms. So, your game will run fine on 166MHz machine but it will run super fast on 1GHz machine. If well designed, your game should run with the same speed no matter what machine you run it on as long as the machine meets minimum requirements. To achieve this we use timing. There are different techniques used to implement timing but the two most common are Constant Frame Rates and Dynamic Frame Rates. Space Invaderz implements both techniques and an example of Constant Frame Rates is shown in the main loop. This technique assures that a constant number of frames gets rendered each second. This is probably the simplest way of timing your game but it wastes additional power your machine might be capable of. In this particular case, I limited my frame rate to 1000FPS (Frames Per Second) or in other words one frame per one millisecond. Now, this might look ridiculous to you since it takes 12 milliseconds to render Space Invaderz frame, but my game movements are all based on elapsed time between two frames that is greater or equal to 1millisecond. At the present time, with current CPU speed this really doesn't matter, but in the distant future when CPU will be able to render a frame in less than a millisecond, it could cause a game to function improperly. Getting back to the main loop, if timing is right we are going to process our game stuff (move invaderz, shoot at them, get UFO moving, … ) until WM_QUIT message is received. Also, since all the moves are timed, in order to pause the game we just have to pause all the timers, isn't that simple?
Game loop is actually a simple state machine consisting of eight states. To better understand how the machine works, take a look at the diagram below.

So, if everything works fine with the main loop and we have everything initialized properly (sprites, timers and all other CDX objects), function cdx_DoFrame() should get called each time loop executes. This function decides which state is going to be executed depending on state machine variable GameState. Naturally, we start in the state 0, which is in this case splash screen. Since I haven't implemented splash screen (left as TODO) machine is going to transition to the next state. This state (Intro 1) displays introduction screen with game name and number of points each alien is worth. By the way, this is also termination point of the game. You can terminate the game if you hit Esc or choose Exit from the menu. So, there are three ways we can take from here: Exit, Menu and the Game. If you chose to go to menu, you will stay in that state until you hit Escape key. If you choose to play, you will go through Get Ready state into the game. As I already mentioned, state machine is controlled with state variable GameState and you can change the behavior of the game by simply changing this variable.
Let me now briefly go over CDX stuff. Space Invaders was programmed using MS Visual C++ and CDX library, which is DirectX wrapper that simplifies graphics programming significantly. All CDX objects used in this game are declared in .h file and initialized in cdx_Init() function. If you look at the cdx_Init() function you will see how easy it is to initialize all your objects. You just have to load up your bitmaps and sound files, start your timers and you are all set to go. When I first started this project, I used CDX project wizard for Visual C++ to create bare application and after that I just kept expanding it. The best way to get familiar with CDX is to go over all the examples from the CDX web site, understand how it works and just keep experimenting.
I decided to handle user input two different ways. Using windows message handling and using CDXInput object. This makes things a little bit inconsistent, but as I already mentioned, this project was an experiment and some things had to be sacrificed. CDXInput was used only in one place, PlayGame() function where I would check if user wants to move ship or fire a bullet. The rest of the input is handled in WinProc function under WM_KEYDOWN case. Some of the state machine transitions are also defined in WinProc. For example, if user is in Game Over state and presses a key, the game will transition to Intro screen.
Here it is, probably the most interesting part of this paper. Game itself is handled in function PlayGame(), which looks like this:

CheckInput() function actually does more than check for the user input. First thing it does is to record elapsed time since the last time function was executed and store it in variable DurationOfTheFrame. We need this info in order to calculate ship movements. This technique is called Dynamic Frame Rate. Initially, in the early phase of SI development, ship was moved constant number of pixels every time key press was detected. Moving the ship along the horizontal line in this fashion caused jerky moves, which wasn't pretty. There is a lot of different solutions to this problem but I decided to define the path the ship has to travel in the current frame as a function of time it took to render the previous frame:
double MoveShipNoPixelsThisFrame = ShipVelocityX * (double)DurationOfTheFrame; //ship velocity is given in pixels per second
//
error correction
ShipdX += MoveShipNoPixelsThisFrame
- (int)MoveShipNoPixelsThisFrame;
if( ShipdX >= 1.0 )
{
MoveShipNoPixelsThisFrame++;
ShipdX -= 1.0;
}
Since the number of pixels can only be an integer, we need some error correction because even though the error appears to be small, over a longer move it will accumulate and it might cause a jerky movement. I implemented simple error correction where I add up values that we normally lose while converting type double to int and add it to the number of pixels the ship will move in the current frame.
This function will also fire the bullets if the space bar is pressed and activate UFO if certain number of bullets have been fired.
MoveObjects() function will take care of moving Invaderz, bullets, UFO and bombs. This is pretty much straight forward and it is the same technique used to move the ship. In the case of bullets, we will search the array of bullets looking for the active bullet and update its position using DurationOfTheFrame variable and predefined velocity of the bullet. Invaders have their own timer, and they will decide to move if the timer is expired. Unlike the ship, Invaders will move constant number of pixels each time. UFO will move depending on the defined UFO velocity and elapsed time of the previous frame – just like the ship.
DrawObjects() function will draw all the game objects, ship, shields, invaderz, UFO, bombs and bullets. This is a very good example of how CDX makes things simple, you just have to call Draw(,,) function and that is it. Depending on the state of the object (sprite) we will draw appropriate bitmap to the background buffer. This applies to all the sprites except shields. Shields are done differently. In the .h file I defined 2-D array of 0's and 1's, which represents the shield. When drawing the shield we will go through the array and if the position within the array is set to one – the corresponding pixel on the screen will be colored green. This enables us to draw shield corrosion when the bullet or a bomb hits the shield, but more on this later.
CollisionDetection() is called after all the moving and drawing is done. It could be called before or after flipping the back buffer, it really doesn't matter. It will search for all possible collisions and update the state of the sprites for the next frame. Flowchart of collision detection for a bullet would look like this:

The first assumption is that when we draw sprites to the back buffer, we draw bullet first and then all other sprites. The second assumption is that if there was an overlap and actually one of the sprites was drawn over a part of the bullet, some bullet pixels will be repainted in the back buffer with the color of the sprite that gets drawn over the bullet. So, if we know what color bullet pixels are, and we find that some of them are colored different we know that there was a collision with some other sprite. Then, knowing other sprite colors we can test which sprite has been hit and perform a rectangle collision test to further verify it. The simplest way to test for collision is to use rectangle test. For example, bullet would be one rectangle and Invader would be another and if they overlap we have a collision. Even though it sounds simple, it might be a little bit inaccurate for your needs. It is your job as a game designer to figure out which technique would be the best and most accurate one for the game you are working on. I toyed with different ways of collision detection with my game and I found that pixel test combined with rectangle test works very well for this game - it is fast enough and accurate enough. There are a lot of different techniques ranging from the simple ones I implemented to very complex ones used in 3-D games that involve space vectors and a little bit more math.
Well, before I wrap this up I would like to show you the big picture just one more time and comment on it:

This diagram shows game structure and how it functions. Flowchart in red represents main loop I discussed in the beginning. Application will first initialize, which partially involves loading bitmaps, options, scores and exit code. Upon successful initialization, main loop will get started, which will start our state machine. State machine is nothing but the game loop and it is represented in yellow. As you can see, one of the states will get executed, but which one it depends on GameState variable. On this diagram GameState variable is represented with the switch. In this case, GAME is the state that is going to be executed and it is shown in green. If user chooses to terminate the game, objects will be deleted and options, score and exit code written to a file.
I hope that now you have a good understanding how this game works. Now all you need to do is to create a project for yourself, apply these same principles, and don't give up. You can read all the tutorials, but nothing will get you there like getting your hands on your own project.
I would like to list my references below, which I highly recommend you read if you haven't yet done so.
1 - Game programming under Windows using CDX
2 - CDX Tutorials and Examples
Please email your questions and suggestion to sinvaderz@hotmail.com.
Copyright © Mirza King, 2001