Building the UFO Program Example


In order to really get a feel for how keyboard and mouse input works in games , it's helpful to work through a complete example. The remainder of this hour focuses on a program example called UFO. Although this program isn't technically a game, it's by far the closest thing you've seen to a game yet. It involves a flying saucer that you control with the keyboard and mouse. You're able to fly the flying saucer around a bitmap background image. Perhaps most important is the fact that the UFO program demonstrates how good of a feel you can create for game controls. More specifically , the arrow keys on the keyboard are surprisingly responsive in the UFO program.

Although you haven't really learned about animation yet, the UFO program example makes use of animation to allow you to fly the flying saucer. Fortunately, the program is simple enough that you can get by without knowing the specifics about animation. All you really need to know is that you can alter the position of a bitmap image to simulate movement on the screen. This occurs thanks to the game engine, which redraws the bitmap every game cycle. So, by altering the position of an image and redrawing it repeatedly, you create the effect of movement. The UFO program example reveals how this task is accomplished, as well as how the keyboard and mouse fit into the picture.

graphics/book.gif

You learn the details of how animation works in Hour 9, "A Crash Course in Game Animation."


Writing the Program Code

The header file for the UFO program example lays the groundwork for the meat of the program, which carries out the details of the flying saucer animation and user input. Listing 6.1 contains the code for the UFO.h header file, which declares global variables used to control the flying saucer.

Listing 6.1 The UFO.h Header File Declares Global Variables Used to Keep Track of the Flying Saucer
 1: #pragma once  2:  3: //-----------------------------------------------------------------  4: // Include Files  5: //-----------------------------------------------------------------  6: #include <windows.h>  7: #include "Resource.h"  8: #include "GameEngine.h"  9: #include "Bitmap.h" 10: 11: //----------------------------------------------------------------- 12: // Global Variables 13: //----------------------------------------------------------------- 14: HINSTANCE   _hInstance; 15: GameEngine* _pGame; 16: const int   _iMAXSPEED = 8; 17: Bitmap*     _pBackground; 18: Bitmap*     _pSaucer; 19: int         _iSaucerX, _iSaucerY; 20: int         _iSpeedX, _iSpeedY; 

The first thing of interest in this code is the _iMAXSPEED constant, which establishes the maximum speed of the flying saucer (line 16). The speed of the flying saucer is how many pixels it can travel in a given direction in each game cycle. So, the value of the _iMAXSPEED constant means that the flying saucer can never travel more than 8 pixels in the horizontal or vertical direction in a game cycle.

The _pBackground and _pSaucer global variables store the two bitmaps used in the program, which correspond to a night sky background image and the flying saucer image (lines 17 and 18). The remaining variables pertain to the flying saucer, and include its XY position (line 19) and XY speed (line 20). The XY position of the flying saucer is specified relative to the game screen. The XY speed, on the other hand, simply tells the program how many pixels the flying saucer should be moved per game cycle; negative values for the speed variables indicate that the flying saucer is moving in the opposite direction.

With the global variables in place, we can move on to the game functions. The first game function to consider is GameInitialize() , which creates the game engine and establishes the frame rate. The frame rate for the program is set to 30 frames per second. This is a relatively high frame rate for games, but it results in much smoother motion for the flying saucer, as you soon find out.

Next on the game function agenda is the GameStart() function, which creates and loads the flying saucer bitmaps, as well as sets the initial flying saucer position and speed (Listing 6.2).

Listing 6.2 The GameStart() Function Performs Startup Tasks for the UFO Program Example
 1: void GameStart(HWND hWindow)  2: {  3:   // Create and load the background and saucer bitmaps  4:   HDC hDC = GetDC(hWindow);  5:   _pBackground = new Bitmap(hDC, IDB_BACKGROUND, _hInstance);  6:   _pSaucer = new Bitmap(hDC, IDB_SAUCER, _hInstance);  7:  8:   // Set the initial saucer position and speed  9:   _iSaucerX = 250 - (_pSaucer->GetWidth() / 2); 10:   _iSaucerY = 200 - (_pSaucer->GetHeight() / 2); 11:   _iSpeedX = 0; 12:   _iSpeedY = 0; 13: } 

The GameStart() function is used to initialize data pertaining to the program such as the bitmaps and other global variables. The flying saucer position is initially set to the middle of the game screen (lines 9 and 10), and then the speed of the saucer is set to so that it isn't moving (lines 11 and 12).

You might think that a program with an animated flying saucer cruising over a background image would require a complex GamePaint() function. However, Listing 6.3 shows how this simply isn't the case.

Listing 6.3 The GamePaint() Function Draws the Background and Flying Saucer Bitmaps
 1: void GamePaint(HDC hDC)  2: {  3:   // Draw the background and saucer bitmaps  4:   _pBackground->Draw(hDC, 0, 0);  5:   _pSaucer->Draw(hDC, _iSaucerX, _iSaucerY, TRUE);  6: } 

As the code reveals, the GamePaint() function for the UFO program is painfully simple. Aside from the standard code that you're now getting accustomed to seeing in the GamePaint() function, all the function does is draw the background and flying saucer bitmaps. The background bitmap is drawn at the origin (0, 0) of the game screen (line 4), whereas the flying saucer is drawn at its current position (line 5). Notice that TRUE is passed as the last argument to the Draw() method when drawing the flying saucer, which indicates that the saucer is to be drawn with transparency using the default transparent color .

The GameCycle() function is a little more interesting than the others you've seen because it is actually responsible for updating the position of the flying saucer based on its speed. Listing 6.4 shows how this is accomplished in the code for the GameCycle() function.

Listing 6.4 The GameCycle() Function Updates the Saucer Position and Then Repaints the Game Screen
 1: void GameCycle()  2: {  3:   // Update the saucer position  4:   _iSaucerX = min(500 - _pSaucer->GetWidth(), max(0, _iSaucerX + _iSpeedX));  5:   _iSaucerY = min(320, max(0, _iSaucerY + _iSpeedY));  6:  7:   // Force a repaint to redraw the saucer  8:   InvalidateRect(_pGame->GetWindow(), NULL, FALSE);  9: } 

The GameCycle() function updates the position of the flying saucer by adding its speed to its position. If the speed is negative, the saucer will move to the left and/or up, whereas positive speed values move the saucer right and/or down. The seemingly tricky code for setting the position must also take into account the boundaries of the game screen so that the flying saucer can't be flown off into oblivion. Granted, the concept of flying off the screen might sound interesting, but it turns out being quite confusing! Another option would be to wrap the saucer around to the other side of the screen if it goes over the boundary, which is how games like Asteroids solved this problem, but I opted for the simpler solution of just stopping it at the edges. After updating the position of the flying saucer, the GameCycle() function forces a repaint of the game screen to reflect the new saucer position (line 8 ).

The flying saucer is now being drawn and updated properly, but you still don't have a way to change its speed so that it can fly. This is accomplished first by handling keyboard input in the HandleKeys() function, which is shown in Listing 6.5.

Listing 6.5 The HandleKeys() Function Checks the Status of the Arrow Keys, Which Are Used to Control the Flying Saucer
 1: void HandleKeys()  2: {  3:   // Change the speed of the saucer in response to arrow key presses  4:   if (GetAsyncKeyState(VK_LEFT) < 0)  5:     _iSpeedX = max(-_iMAXSPEED, --_iSpeedX);  6:   else if (GetAsyncKeyState(VK_RIGHT) < 0)  7:     _iSpeedX = min(_iMAXSPEED, ++_iSpeedX);  8:   if (GetAsyncKeyState(VK_UP) < 0)  9:     _iSpeedY = max(-_iMAXSPEED, --_iSpeedY); 10:   else if (GetAsyncKeyState(VK_DOWN) < 0) 11:     _iSpeedY = min(_iMAXSPEED, ++_iSpeedY); 12: } 

The HandleKeys() function uses the Win32 GetAsyncKeyState() function to check the status of the arrow keys ( VK_LEFT , VK_RIGHT , VK_UP , and VK_DOWN ) and see if any of them are being pressed. If so, the speed of the flying saucer is adjusted appropriately. Notice that the newly calculated speed is always checked against the _iMAXSPEED global constant to make sure that a speed limit is enforced. Even flying saucers are required to stay within the speed limit!

graphics/book.gif

The GetAsyncKeyState() function is part of the Win32 API, and provides a means of obtaining the state of any key on the keyboard at any time. You specify which key you're looking for by using its virtual key code ; Windows defines virtual key codes for all the keys on a standard keyboard. Common key codes for games include VK_LEFT , VK_RIGHT , VK_UP , VK_DOWN , VK_CONTROL , VK_SHIFT , and VK_RETURN .


If you thought handling keyboard input in the UFO program was easy, wait until you see how the mouse is handled. To make things a little more interesting, both mouse buttons are used in this program. The left mouse button sets the flying saucer position to the current mouse cursor position, whereas the right mouse button sets the speed of the flying saucer to . So, you can use the mouse to quickly get control of the flying saucer; just right-click to stop it and then left-click to position it wherever you want. Listing 6.6 shows the code for the MouseButtonDown() function, which makes this mouse magic possible.

Listing 6.6 The MouseButtonDown() Function Uses the Left and Right Mouse Buttons to Move the Flying Saucer to the Current Mouse Position and Stop the Flying Saucer, Respectively
 1: void MouseButtonDown(int x, int y, BOOL bLeft)  2: {  3:   if (bLeft)  4:   {  5:     // Set the saucer position to the mouse position  6:     _iSaucerX = x - (_pSaucer->GetWidth() / 2);  7:     _iSaucerY = y - (_pSaucer->GetHeight() / 2);  8:   }  9:   else 10:   { 11:     // Stop the saucer 12:     _iSpeedX = 0; 13:     _iSpeedY = 0; 14:   } 15: } 

The first step in this code is to check and see which one of the mouse buttons was pressed ”left or right (line 3). I know, most PC mice these days have three buttons, but I wanted to keep the game engine relatively simple, so I just focused on the two most important buttons. If the left mouse button was pressed, the function calculates the position of the flying saucer so that it is centered on the current mouse cursor position (lines 6 and 7). If the right button was pressed, the speed of the flying saucer is set to (lines 12 and 13).

graphics/book.gif

If you determine that supporting two mouse buttons is simply too much of a weakness for the game engine, feel free to modify it on your own. It primarily involves changing the third argument of the MouseButtonDown() function so that it can convey more than two values ”one for each of the three mouse buttons.


Testing the Finished Product

The UFO program example is the closest thing you've seen to a game thus far, and is quite interesting in terms of allowing you to fly around an animated graphical object. Hopefully, you'll be pleasantly surprised by the responsiveness of the program's keyboard controls. Figure 6.1 shows the UFO program in action as the flying saucer does a flyby of some desert cacti.

Figure 6.1. The UFO program example demonstrates how to control an animated graphical object with the keyboard and mouse.

graphics/06fig01.gif

If you guide the flying saucer to the edge of the game screen, it will stop, which is to be expected given the program code you just worked through. There are a variety of different ways to tweak this program and make it more intriguing, such as wrapping the flying saucer from one side of the screen to the other, which is why I hope you spend some time tinkering with the code. You'll find some ideas for modifying the program in the Exercises for the lesson, which are coming up.



Sams Teach Yourself Game Programming in 24 Hours
Sams Teach Yourself Game Programming in 24 Hours
ISBN: 067232461X
EAN: 2147483647
Year: 2002
Pages: 271

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