- Shopping Bag ( 0 items )
Using DirectX—the latest and greatest ...
Using DirectX—the latest and greatest technology for making games on PCs—Windows Game Programming For Dummies will help you write just about any 2D game you can conjure. Now updated to cover new DirectX and Windows releases, your friendly yellow-and-black companion will show you:
From graphics to sound to input and installation, legendary game developer and Xtreme Games CEO André LaMothe takes you right into the guts of the game—in an entertaining style that won’t send you retreating to the nearest joystick. André’s witty, he’s tons of fun, and before you know it he’ll have you up to speed on:
PART I: GETTING FAMILIAR WITH WINDOWS PROGRAMMING.
Chapter 1: Setting Up for Windows 9x/XP/2000 Programming.
Chapter 2: Exploring the Basics of Video Game Design.
Chapter 3: The Nuts and Bolts of Windows 9x/XP/2000 Programming.
Chapter 4: How to Handle Big Events.
Chapter 5: Putting Windows GDI to Work: Drawing Text and Graphics.
Chapter 6: Wrapping Up Windows 9x/XP/2000 Programming.
PART II: JACKING IN WITH DIRECTX.
Chapter 7: The Architecture of DirectX and the Dreaded COM.
Chapter 8: Getting to Know DirectDraw.
Chapter 9: Using the DirectDraw Crayons.
Chapter 10: Digging into DirectDraw: Animation and Bitmaps.
Chapter 11: Digging Deeper into Direct Draw: Advanced Features.
Chapter 12: The GPDUMB Game Engine, Part I.
PART III: The Rest of the Puzzle: SOUND, INPUT, AND SET UP.
Chapter 13: Making Noise with DirectSound.
Chapter 14: The Ins and Outs of DirectInput.
Chapter 15: Click OK to Install: Using DirectSetup & Autoplay.
Chapter 16: GPDUMB Part II-The Final Conflict.
Chapter 17: What goes Up Must Come Down: Physics Modeling.
Chapter 18: Putting the Game Engine to Work with Underworld.
Chapter 19: Marketing Your Madness.
PART V: THE PART OF TENS.
Chapter 20: Ten Basic Rules of Game Design.
Chapter 21: Ten Biggest Mistakes Game Programmers Make.
Chapter 22: Ten Best Game Programming Resources on the Web.
END-USER LICENCE AGREEMENT.
In This Chapter
If you've been reading this book from cover to cover and have been diligently following along with the text, then give yourself a hand because the hard part is over! If not, then get ready to enjoy this chapter's discussions on some Windows programming basics: timers, menus, menu messages, and sound. All these minor topics are important to creating complete Windows applications as well as to game programming.
At the end of this chapter, I also show you how to use what I cover in this chapter (and some bits from Chapters 1 through 5) to create the WinX game console. You use this console to write games much like you would DOS games, but with all the resources of 32-bit programming and the Windows operating system.
In game programming, you often want to perform a specific task at timed intervals. For example, you may want to play a heartbeat sound every second, or poll input devices every 100 milliseconds, or decrease the player's health every 5 seconds, and so on. Or perhaps you want to time how long something happens -- to optimize some code, perhaps, or to force a specific section of code to wait a specific amount of time. Hence, you need to be able to respond at timed intervals and to count how long something takes.
You can write low-level code and timing software to accomplish these types of functions. However, writing timing software is rather difficult, and making sure it works on every machine from a 386 to a Pentium II isn't that easy. The solution is to use the built-in timing and counting ability that Windows comes with. Here's what Windows provides:
WM_TIMERmessage to inform you that something must be done. This method works great, but the basic Windows timers aren't that accurate -- they can be plus or minus milliseconds.
WM_TIMERmessages, Windows calls a callback function that you supply to process your timing task.
If you're an assembly-language programmer, you can use a Pentium-only instruction RDTSC (Read Direct from Time Stamp Counter), which is a 64-bit CPU clock-cycle-accurate timer. Most assemblers don't support it, but you can create the instruction yourself by writing the op code bytes (0 x 0F31) directly into the code space. Refer to your favorite assembly-language manual or Intel Pentium documents for more information.
The following sections show you how to use the basic low-accuracy timer and how to set up a delay loop with the built-in clock.
You can create as many timers as you want, all with different time delays. To create a timer, you make a Windows API call with an id that you want the timer referred to as. Then Windows creates the timer and starts sending your event handler
WM_TIMER messages along with the timer id in
wParam when the timer delay is up; see Figure 6-1, which shows the timing loop. To create a timer, use the
UINT SetTimer(HWND hWnd, // handle of window
UINT nIDEvent,// timer ID
UINT uElapse, // time delay in microseconds
TIMERPROC lpTimerFunc); // address of callback
To create a timer, you send
However, you don't use the callback, so always set it to
NULL. If the function is successful, it returns the id that you sent; otherwise, it returns 0. You need this id to kill the timer after you're done with it. And of course, the id is needed to determine what timer sent the
WM_TIMER message in your event handler if you have more than one timer. Here's an example that creates two timers, one with a one-second delay and the other with a three-second delay:
// the timer IDs (totally arbitrary)
#define TIMER_ID1_SEC 1 // id of one-second timer
#define TIMER_ID3_SEC 2 // id of three-second timer
// create one-second timer
SetTimer(hwnd, TIMER_ID1_SEC, 1000,NULL);
// create three-second timer
SetTimer(hwnd, TIMER_ID3_SEC, 3000,NULL);
// exit event handler
Not much to it. Of course, you may want to check the return value to make sure that it's non-zero and that the timer was actually created.
Here's how you process the
// timer event is present; determine which timer fired
// more code for first timed item
// more code for second timed item
// test for other IDs; other code you require
} // end switch
Timers take up resources, so don't let them sit around and run -- kill the timers after you're done using them or kill them in the
WM_DESTROY message. To kill a timer, use the
KillTimer() function, which takes the window handle and the id of the timer to kill. Here's how you kill the timers in the preceding example:
PROG6_1.CPP on the CD is an example that creates two timers, just like the example here; it prints text in response to one timer and beeps in response to the other.
Creating a counter in Windows is much simpler than using the timers. All you have to do is query the current, millisecond-accurate time and perform a comparison with your start time. The function that retrieves a millisecond timer is
GetTickCount(). Here's an example that waits 100 ms:
// next line gets the current value
DWORD frame_start_time = GetTickCount();
//your code goes here
// now make sure that you have waited 100 milliseconds
while(GetTickCount() - frame_start_time < 100);
GetTickCount() is relative: You must compute differences in time, not absolute time, because
GetTickCount() can't be reset to 0.
Just about every window has a menu. In fact, an application may have a main menu along with a number of pop-up menus (press the right mouse button anywhere in Windows to see a pop-up menu). I just cover creating the main menu that most windows have, but these techniques are applicable to pop-up menus, floating menus, and so on.
Before I delve into the steps of creating a menu, here's a little background on resources, which you can think of as a database of media and information that you create and attach to your program. A Windows-based application can have a number of resources appended to the end of the
.EXE file that can be loaded at run-time. Resources are a nice way to add data to your application without having to write a lot of support code yourself.
Here's a small list of the kinds of resources that you can add to your application:
Moreover, most C/C++ compilers come with resource editors that help you create and add resources to your application in a drag-and-drop fashion. The resources are described in a resource file with an
.RC extension and then later compiled into a
.RES binary file with a resource compiler (
RCPP.EXE). However, this step is usually invisible if you're using a development studio such as Microsoft Visual C++ or Borland C++ Builder.
Of course, you don't have to use resources if you don't want to; you can load everything off disks in separate files or generate things on the fly, but using resources is a cool way to create your programs.
To create a menu for your window, you have a number of steps to follow:
.RC) with the menu definition in it.
.H) to include in your application and to contain the various constants defining your menu items.
WM_COMMANDmenu-selection messages, which are sent when a menu item is selected.
Figure 6-2 is a graphical representation of the process, to help show the interconnections. The following subsections take a look at the steps in more detail.
A menu is like a little program that describes the menu items along with the menu selections for each item. In addition, a menu can have submenus within it, making menus hierarchical by nature. The language in which you must describe Windows menus is based on C and Pascal and is very simple, with just a few commands to learn. You can use the built-in compiler tools to create menus, but look at the old-fashioned way first.
Every compiler creates menus differently (for that matter, nearly every version of every compiler does, too). I include the "old-fashioned way" because it's impossible to talk about a method for creating menus that is useful for every compiler.
To create an
.RC file, you must use a pure ASCII text editor, such as the editor you use to write your C/C++ code in. Within the file, you create the menu. Here is the format of a menu (code keywords are in bold):
MENU_NAME MENU DISCARDABLE
//menu definition goes in here
Although it's missing a great deal, that code is the shell of a menu. The first string is the name of the menu and is followed by
DISCARDABLE, which indicates to Windows that a menu definition follows and the data can be unloaded. Then within the
BEGIN..END pair, you create the menu.
The next example creates a menu that has two main selections:
Figure 6-3 shows the structure of the menu. You create an
.RC file with the following contents (call it
MYMENU.RC). All the keywords are in bold so that you can differentiate them from the stuff you control.
MYMENU MENU DISCARDABLE
MENUITEM "Open", 1000
MENUITEM "Close", 1001
MENUITEM "Exit", 1002
MENUITEM "About", 2000
The first thing to notice is the hierarchy. The name of the menu is MYMENU, and this menu consists of two pop-up menus named File and Help. Then within File, the menu items are defined with the
MENUITEM command along with an integer id associated with each. This integer id is the value that Windows will send to your application when the menu item is selected. The ids can be anything you want, but they must be less than 0 x 7FFFF.
The best strategy for selecting menu-item ids is to use multiples of 100 or 1,000 for each top-level menu and then increment by 1 for each menu item within each top-level menu. For example, your menu may have four items on it with ids 100, 101, 102, and 103.
Now of course, you can add as many items as you want to each menu selection, have as many top-level menus as you want (as long as they fit on the screen), and do plenty of other things to each menu item, such as gray it out or put in lines or breaks. (Check the Windows documentation for more on how to program those elements.) The only option I want to show you is the use of the ampersand symbol (
&), which you place inside a menu item's name, just before the character that you want to make an Alt shortcut for accessing that menu.
For example, if you want the x in Exit to be a hot key, define it like this:
MENUITEM "E&xit", 1002
When the menu item is displayed, you don't see the ampersand. Instead, you see an underline under the x, denoting that it's a hot key.
When you add a header file to your
.RC resource, your application doesn't have to use hard-coded numbers; in essence, you
#define all the ids in the
.RC file so that you can use them instead of numbers -- a very programmer-friendly approach. Hence, continuing with the example, you create an
.H header like this:
// MYMENU.H, header with menu ids
// Notice the clean naming I used for the ids; you
// can very quickly figure out the menu, submenu, and item
#define ID_MYMENU_FILE_OPEN 1000
#define ID_MYMENU_FILE_CLOSE 1001
#define ID_MYMENU_FILE_EXIT 1002
#define ID_MYMENU_HELP_ABOUT 2000
Then you include the header in your
.RC file and use names for your ids instead of hard-coded constants, like this:
MYMENU MENU DISCARDABLE
MENUITEM "Open", ID_MYMENU_FILE_OPEN
MENUITEM "Close", ID_MYMENU_FILE_CLOSE
MENUITEM "Exit", ID_MYMENU_FILE_EXIT
MENUITEM "About", ID_MYMENU_HELP_ABOUT
To load the
.RC file to your application, simply add the file into your project, and the compiler does the rest. Just make sure that the header file is within reach of your compiler. Of course, you can perform the entire process described in the preceding section totally with the compiler's resource editor, which creates the
.RC file along with the
.H file, and you can use the artificially generated files instead. However, the mechanics differ for each compiler, so you need to refer to your particular compiler's documentation for details.
After creating your menu resource file and header file, you're ready for business. Of course, you must include the menu header file in your C++ file so that it will have access to the id references; then you're all set. Now, to actually load the menu into your application at run-time, you have three choices, which are detailed in this subsection.
The first way is to define the menu when you create your Windows class:
// set menu name to the ASCII name of your menu
winclass.lpszMenuName = "MYMENU";
The second way is to leave this variable
NULL and simply load and attach your menu when you create the main window, by using the
LoadMenu() function, which is capable of loading a menu resource from your
.EXE. Here's how you use it when creating the window to load the menu (the code in bold is the only line change from the standard
CreateWindow() call used elsewhere in this section):
// create the window
CreateWindow(WINDOW_CLASS_NAME, // class
"Basic Menus", // title
WS_OVERLAPPEDWINDOW | WS_VISIBLE, // flags
0,0, // x,y
WINDOW_WIDTH, // width
WINDOW_HEIGHT, // height
NULL, // handle to parent
LoadMenu(hinstance, "MYMENU"), // handle to menu
hinstance, // instance
NULL); // creation parameters
LoadMenu would be
NULL, but you can attach a menu by sending
CreateWindow() a menu handle --
LoadMenu() does just this: It takes the instance of the application along with the menu name and returns a menu handle object
HMENU, which is then passed to
CreateWindow(). Presto -- you have a menu on your window.
The last way to attach a menu is to attach it after the initial window creation. In fact, with this technique, you can change the menu on the fly and change menus as much as you want as long as they're in the resource file. You do it first by getting the handle to your menu with
LoadMenu(), and then by attaching the menu to your window with
SetMenu(). Here's an example:
// load the menu from resource and get handle to it
HMENU hmymenu = LoadMenu(hinstance, "MYMENU");
// attach the menu to the window and save old
The ASCII name you use to refer to the menu that you create is contained in the
.RC file and can be anything you want. I've been using
MYMENU, but it could as well be
MAIN_MENU_1 or anything else. However, this name has nothing to do with the name Windows displays for the menu and menu items; that information is specified in the menu definition.