Okay folks, no more fooling around…time to learn something. Have your compiler running with the BaseCode1 project loaded, and be sure that your DirectX 7.0 SDK documentation is easily accessible. If you’re missing some files,
try here.
The Rise and Fall of BaseCode1
Let’s turn our attention first to the overall layout of the program:
- Program execution starts at WinMain() (true for all Windows applications)
- The OS is instructed to create a full-screen window (black)
- Game_Initialize() is called
- DirectDraw is initialized
- DirectInput is initialized
- DirectSound is initialized
- The main message loop is entered (true for all Windows applications)
- Windows messages are handled in WindowProc()
- If there are no messages waiting, Game_Main() is called
- Check for input
- Clear the display
- Write a line of text
- Play a sound if the spacebar was hit
- Once the main message loop is exited (ESCAPE was hit), call Game_Terminate()
- DirectSound is shutdown
- DirectInput is shutdown
- DirectDraw is shutdown
- The program ends
Or, more simply stated (ignoring Windows-related procedures):
- Game_Initialize() is called
- Game_Main() is called (repeatedly)
- Game_Terminate() is called
It’s fairly common to see ‘entities’ such as our game code and the DirectX components asking for an opportunity to initialize before being used and a chance to take care of any shutdown tasks before they’re done. If our game requires memory for internal data, it would be a natural process to allocate the memory in
Game_Initialize() which is called once at the beginning of the game, and free that memory in
Game_Terminate() which is called before the game is completely terminated. Likewise, you’d expect that DirectX components don’t just work off-the-bat – we need to set them up first by calling their initialization functions and feeding them the correct information so that they work properly when they’re needed.
Overall Layout
The compiler doesn’t care if our template code is in one file or in fifty. It is only for readability that we ‘chop up’ the game’s functions into groups and assign them their own files. Face it – a game with 800 functions all in one file makes for messy game code. There will be an article discussing source files, header files and projects that will further explain the semantics to organizing code in files, so for now let’s just look at the contents of the files themselves:
- WinBase.cpp contains all of the code that is specific to Windows. We need to at least create a window and process messages coming in from the OS to be considered a true Windows program, and this file keeps them all in one place. It also contains the proper start of execution, WinMain() . Once we dissect this function, the entire flow of the program will make perfect sense.
- InitTerm.cpp contains, you’ve guessed it, all of the initialization and termination functions for our game template. Game_Initialize() calls other initialization functions, in a sense managing everything else in the game. In the same way, Game_Terminate() ensures that everything that would like a chance to shutdown gets called. This is our last chance to do things before our frame-by-frame code gets called repeatedly, so make sure everything’s prepared!
- GameMain.cpp is where all of the action takes place. This function is called repeatedly from the message loop in WinMain() , which means that whatever you put in this function is going to execute many times a second. It’s our job to make sure that the code is written to fit this style. If every call to Game_Main() produces a frame of animation on the display, we’ve got ourselves the chassis for a game.
- Utils.cpp is a convenient place to throw functions into that are used by other parts of the game. For instance, there are functions for loading bitmaps from disk in this file, which is used elsewhere.
And there’s also a couple of header (.H) files:
- Utils.h contains a ‘description’, called a prototype or declaration, for each of the functions implemented in Utils.cpp. As long as any other source file has access to this header file, it can use the functions contained inside.
- Globals.h is the master header file for our game template code. Every source file needs to know about Globals.h in order to function properly. If we place any information inside of this header file, it is immediately accessible from anywhere in our entire game. Sounds like a good place for global variables, huh? That’s one of the primary purposes of this file.
So, the rule of thumb is that every source file (.CPP) should include Globals.h so that we can use global variables properly, and any source file that needs to use a function from Utils.cpp should include Utils.h. As you can see at the top of each source file, including a header file is accomplished with the line
#include “filename .h” , where filename is either
Globals or
Utils .
Let’s look once more at our organizational ‘chart’ to really drive the point home:
- Create a window for our game: WinBase.cpp – WinMain()
- Initialize our game: InitTerm.cpp – Game_Initialize()
- Initialize DirectX components
- Draw animated frames: GameMain.cpp – Game_Main()
- Shutdown our game: InitTerm.cpp – Game_Terminate()
- Shutdown DirectX components
- End of game
Divide and Conquer
What we’re doing here is setting ourselves up to understand the game template code by attacking each component separately. Each of the following has initialization, runtime and shutdown duties:
- Our game code
- DirectDraw
- DirectInput
- DirectSound
Since we don’t have a game yet, the template code just fills in each section with something simple to serve as a placeholder. For instance, we use DirectDraw to set up our display, but all we’re doing is writing a line of text. Heck, it even goes through the trouble of loading RESOURCE.BMP onto an internal buffer, but we don’t (yet) do anything with it. For input, we have the code necessary to read the keyboard, and all that this program is interested in is the spacebar. Using DirectSound, we load a single sound into memory and play it whenever the spacebar is pressed. Nothing too complicated for any of these components, but just enough to say that we used them – they’re initialized, used and terminated properly. Definitely something good to build on!
Of the three DirectX components, only DirectDraw is truly essential. We don’t need fancy sounds this early in our studies, and even input isn’t absolutely necessary, but without DirectDraw we wouldn’t even be able to set our resolution, color depth or display anything on the screen.
Therefore, it should come as no surprise that we’ll concentrate our attack there first, but not before looking at DirectX as a whole. Part of the beauty of using DirectX is that the components are used rather consistently – once you get the hang of using one component, the rest aren’t so difficult to manipulate.
Questions? Comments? Please reply to this topic.
Edited by - teej on April 23, 2001 5:45:57 PM