Improving Input with Joysticks - Building the UFO 2 Example
(Page 6 of 7 )
Chapter 5, "Controlling Games with the Keyboard and Mouse," guides you through the design and development of an interesting little program that allows you to control a flying saucer using the keyboard and mouse. The remainder of this chapter focuses on adding joystick support to the UFO example to create a new version of the program called UFO 2. In addition to providing joystick support, you also enhance the program a little by adding a thrust image to the flying saucer and a hyperspace feature. "Thrusting" in this case simply involves drawing a flying saucer with a flame shooting out of the bottom, whereas going into hyperspace involves repositioning the saucer at a random location on the game screen.
Writing the Program Code
The code for the UFO 2 program starts with the UFO.h header file, which includes a couple of changes from its previous version:
Bitmap* g_pSaucer[2];
BOOL g_bSaucerFlame;
The first change to this code from the previous version of the program is the modification of the g_pSaucer global variable to an array of two bitmaps, as opposed to one. This is necessary because the thrust bitmap has now been added to show the flying saucer with a flame shooting out. Figure 7.6 shows the new flying saucer image with the flame appearing out of the bottom; the original flying saucer image was also modified for this example so that its size matches the size of the saucer flame image. The other change to the code involves the addition of the g_bSaucerFlame variable, which keeps track of whether or not the saucer is to be displayed with a flame.

Figure 7.6 -- The UFO 2 example includes a new flying saucer image that has a flame shooting out of the bottom of the saucer to indicate thrust.
The GameInitialize() function is the first game function to revisit for the UFO 2 program. The only change to this code from the previous version is the addition of the call to the game engine's InitJoystick() method, which is necessary to get the joystick primed and ready for action:
g_pGame->InitJoystick();
The GameStart() function also has changed a little, as shown in Listing 7.5.
Listing 7.5 -- The GameStart() Function Initializes the Background and Flying Saucer Bitmaps
void GameStart(HWND hWindow)
{
// Seed the random number generator
srand(GetTickCount());
// Create and load the background and saucer bitmaps
HDC hDC = GetDC(hWindow);
g_pBackground = new Bitmap(hDC, IDB_BACKGROUND, g_hInstance);
g_pSaucer[0] = new Bitmap(hDC, IDB_SAUCER, g_hInstance);
g_pSaucer[1] = new Bitmap(hDC, IDB_SAUCERFLAME, g_hInstance);
// Set the initial saucer position and speed
g_iSaucerX = 250 - (g_pSaucer[0]->GetWidth() / 2);
g_iSaucerY = 200 - (g_pSaucer[0]->GetHeight() / 2);
g_iSpeedX = 0;
g_iSpeedY = 0;
}
Because the hyperspace feature of UFO 2 requires the calculation of a random location on the screen, it's necessary to seed the random number generator. This function also loads the new flaming saucer image.
As mentioned earlier, it's important for any program that supports joysticks to properly capture and release the joystick in response to the main program window being activated and deactivated. In the case of UFO 2, this takes place in the GameActivate() and GameDeactivate() functions, which are shown in Listing 7.6.
Listing 7.6 -- The GameActivate() and GameDeactivate() Functions Capture and Release the Joystick in Response to the Main Program Window Being Activated and Deactivated
void GameActivate(HWND hWindow)
// Capture the joystick
g_pGame->CaptureJoystick();
}
void GameDeactivate(HWND hWindow)
{
// Release the joystick
g_pGame->ReleaseJoystick();
}
The GameActivate() function simply calls the CaptureJoystick() method of the game engine to capture the joystick. Similarly, the joystick is released in GameDeactivate() with a quick call to the ReleaseJoystick() method.
If you're curious as to how the thrusting flying saucer is drawn, well wonder no more! The GamePaint() function handles drawing the appropriate flying saucer depending on the value of the g_bSaucerFlame global variable, as shown in Listing 7.7.
Listing 7.7 -- The GamePaint() Function Draws the Background and Flying Saucer Bitmaps, Making Sure to Determine Which Flying Saucer Bitmap to Draw
void GamePaint(HDC hDC)
{
// Draw the background and saucer bitmaps
g_pBackground->Draw(hDC, 0, 0);
g_pSaucer[g_bSaucerFlame ? 1:0]->Draw(hDC, g_iSaucerX, g_iSaucerY, TRUE);
}
As the listing reveals, the g_bSaucerFlame variable directly determines which flying saucer is drawn. Of course, you're probably still curious as to how this variable gets modified in the first place; that's where the joystick enters the picture.
If you recall from earlier, a program that uses our super slick game engine to process joystick input must provide its own HandleJoystick()function to perform its own processing of joystick input. In this case, the HandleJoystick() function is responsible for altering the speed of the flying saucer in response to directional joystick movements, as well as controlling the thrust and hyperspace features of the flying saucer when the two joystick buttons are pressed. Listing 7.8 shows how the HandleJoystick() function carries out these tasks.
Listing 7.8 -- The HandleJoystick() Function Takes Care of Processing Joystick Input and Altering the Flying Saucer Appropriately
void HandleJoystick(JOYSTATE jsJoystickState)
{
// Check horizontal movement
if (jsJoystickState & JOY_LEFT)
g_iSpeedX = max(-g_iMAXSPEED, g_iSpeedX - 2);
else if (jsJoystickState & JOY_RIGHT)
g_iSpeedX = min(g_iMAXSPEED, g_iSpeedX + 2);
// Check vertical movement
if (jsJoystickState & JOY_UP)
g_iSpeedY = max(-g_iMAXSPEED, g_iSpeedY - 2);
else if (jsJoystickState & JOY_DOWN)
g_iSpeedY = min(g_iMAXSPEED, g_iSpeedY + 2);
// Check primary joystick button
g_bSaucerFlame = (jsJoystickState & JOY_FIRE1);
// Check secondary joystick button
if (jsJoystickState & JOY_FIRE2)
{
// Force the flying saucer into hyperspace
g_iSaucerX = rand() % (500 - g_pSaucer[0]->GetWidth());
g_iSaucerY = rand() % 320;
}
}
Seeing how the HandleJoystick() function is your only real communication link to the joystick in the UFO 2 program, it's really quite simple. The first block of code in the function checks to see if a horizontal movement has occurred—in which case, the X component of the flying saucer's speed is modified. Similarly, the second block of code performs the same processing on vertical joystick movement. The g_bSaucerFlame global variable is then set using the state of the first joystick button. Finally, the hyperspace feature is carried out in response to the second joystick button being pressed.
This chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.
Buy this book now. |
Next: Testing the Finished Product >>
More PC Gaming Articles
More By Sams Publishing
| Recommended by Dev Hardware |
|---|
|