Building the Henway 2 Program Example


You've spent the better part of this hour adding MIDI music support to the game engine. By adding the code to the game engine, you've made it possible to put a very small burden on the code for a game. In other words, the new and improved game engine now makes it painfully easy to add MIDI music support to any game. The remainder of this hour proves my point by showing you how to add MIDI music to the Henway game that you developed back in Hour 12, "Example Game: Henway." In fact, the Henway 2 game that you're about to develop not only includes MIDI music, but it also uses wave sounds to incorporate some sound effects into the game. The MIDI music added to the game simply serves as background music to make the game a little more interesting. You'll no doubt find the game to be a major improvement over its earlier version.

Writing the Program Code

The code for the audio supercharged Henway 2 game begins with the GameStart() function, which now includes a single line of code near the end that plays the MIDI song stored in the file Music.mid:

 _pGame->PlayMIDISong(TEXT("Music.mid")); 

There isn't too much to say about this code because it simply passes the name of the MIDI song file to the PlayMIDISong() method in the game engine.

Just as the GameStart() function opens the MIDI player by starting the playback of a MIDI song, the GameEnd() function is responsible for cleaning up by closing the MIDI player. The following line of code appears near the beginning of the GameStart() function:

 _pGame->CloseMIDIPlayer(); 

The GameEnd() function performs the necessary MIDI music cleanup by calling the CloseMIDIPlayer() method to close the MIDI player.

Earlier in the hour, I mentioned how it wouldn't be good for a MIDI song to continue playing if the game window is deactivated. In order to keep this from happening, it's important to pause the MIDI song when a window deactivation occurs, and then play it again when the window regains activation. Listing 15.1 contains the code for the GameActivate() and GameDeactivate() functions, which are responsible for carrying out these tasks .

Listing 15.1 The GameActivate() and GameDeactivate() Functions Are Used to Pause and Play the MIDI Song Based on the State of the Game Window
 1: void GameActivate(HWND hWindow)  2: {  3:   // Capture the joystick  4:   _pGame->CaptureJoystick();  5:  6:   // Resume the background music  7:   _pGame->PlayMIDISong(TEXT(""), FALSE);  8: }  9: 10: void GameDeactivate(HWND hWindow) 11: { 12:   // Release the joystick 13:   _pGame->ReleaseJoystick(); 14: 15:   // Pause the background music 16:   _pGame->PauseMIDISong(); 17: } 

The GameActivate() function is responsible for continuing the playback of a paused MIDI song, and it does so by calling the PlayMIDISong() method and specifying FALSE as the second argument (line 7). If you recall from earlier in the hour, the second argument determines whether the MIDI song is rewound before it is played . So, passing FALSE indicates that the song shouldn't be rewound, which has the effect of continuing playback from the previously paused position. The GameDeactivate() function performs an opposite task by pausing the MIDI song with a call to the PauseMIDISong() method (line 16).

The GameCycle() function doesn't have any MIDI- related code, but it does include some new sound effects code. More specifically , the GameCycle() function now plays car horn sounds at random intervals to help add some realism to the game (see Listing 15.2).

Listing 15.2 The GameCycle() Function Randomly Plays Car Horn Sounds
 1: void GameCycle()  2: {  3:   if (!_bGameOver)  4:   {  5:     // Play a random car sound randomly  6:     if (rand() % 100 == 0)  7:       if (rand() % 2 == 0)  8:         PlaySound((LPCSTR)IDW_CARHORN1, _hInstance, SND_ASYNC   9:           SND_RESOURCE); 10:       else 11:         PlaySound((LPCSTR)IDW_CARHORN2, _hInstance, SND_ASYNC  12:           SND_RESOURCE); 13: 14:     // Update the sprites 15:     _pGame->UpdateSprites(); 16: 17:     // Obtain a device context for repainting the game 18:     HWND  hWindow = _pGame->GetWindow(); 19:     HDC   hDC = GetDC(hWindow); 20: 21:     // Paint the game to the offscreen device context 22:     GamePaint(_hOffscreenDC); 23: 24:     // Blit the offscreen bitmap to the game screen 25:     BitBlt(hDC, 0, 0, _pGame->GetWidth(), _pGame->GetHeight(), 26:       _hOffscreenDC, 0, 0, SRCCOPY); 27: 28:     // Cleanup 29:     ReleaseDC(hWindow, hDC); 30:   } 31: } 

The modified GameCycle() function establishes a 1 in 100 chance of playing a car horn in every game cycle (line 6). Because the game cycles are flying by at 30 per second, these really aren't as bad odds as they sound. Whenever the odds do work out and a car horn is played, another random number is selected to see which car horn is played (lines 7 “12). You could have just as easily played a single car horn, but having two horns with different sounds makes the game much more interesting. It's the little touches like this that make a game more intriguing to players.

If you recall from the earlier design of the Henway game, you can start a new game by clicking the mouse anywhere on the game screen after the game is over. Listing 15.3 contains code for a new MouseButtonDown() function that restarts the MIDI song as part of starting a new game.

Listing 15.3 The MouseButtonDown() Function Restarts the MIDI Song to Coincide with a New Game
 1: void MouseButtonDown(int x, int y, BOOL bLeft)  2: {  3:   // Start a new game, if necessary  4:   if (_bGameOver)  5:   {  6:     // Restart the background music  7:     _pGame->PlayMIDISong();  8:  9:     // Initialize the game variables 10:     _iNumLives = 3; 11:     _iScore = 0; 12:     _bGameOver = FALSE; 13:   } 14: } 

It obviously makes sense to start the background music over when a new game starts. Because the default action of the PlayMIDISong() method is to rewind a song before playing it, it isn't necessary to pass any arguments to the method in this particular case (line 7).

Speaking of restarting the MIDI song, Listing 15.4 contains the code for the HandleJoystick() function, which also restarts the song as part of starting a new game.

Listing 15.4 The HandleJoystick() Function Also Restarts the MIDI Song to Signal a New Game
 1: void HandleJoystick(JOYSTATE jsJoystickState)  2: {  3:   if (!_bGameOver && (++_iInputDelay > 2))  4:   {  5:     // Check horizontal movement  6:     if (jsJoystickState & JOY_LEFT)  7:         MoveChicken(-20, 0);  8:     else if (jsJoystickState & JOY_RIGHT)  9:         MoveChicken(20, 0); 10: 11:     // Check vertical movement 12:     if (jsJoystickState & JOY_UP) 13:         MoveChicken(0, -20); 14:     else if (jsJoystickState & JOY_DOWN) 15:         MoveChicken(0, 20); 16: 17:     // Reset the input delay 18:     _iInputDelay = 0; 19:   } 20: 21:   // Check the joystick button and start a new game, if necessary 22:   if (_bGameOver && (jsJoystickState & JOY_FIRE1)) 23:   { 24:     // Play the background music 25:     _pGame->PlayMIDISong(); 26: 27:     // Initialize the game variables 28:     _iNumLives = 3; 29:     _iScore = 0; 30:     _bGameOver = FALSE; 31:   } 32: } 

The call to the PlayMIDISong() method again occurs with no arguments, which results in the MIDI song being started over at the beginning (line 25).

You've now seen all the MIDI music-related code in the Henway 2 game, but there are still some wave sound effects left to be played. Two of these sound effects are played in the SpriteCollision() function, which is shown in Listing 15.5.

Listing 15.5 The SpriteCollision() Function Plays Sound Effects in Response to the Chicken Getting Run Over and the Game Ending
 1: BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee)  2: {  3:   // See if the chicken was hit  4:   if (pSpriteHittee == _pChickenSprite)  5:   {  6:     // Move the chicken back to the start  7:     _pChickenSprite->SetPosition(4, 175);  8:  9:     // See if the game is over 10:     if (--_iNumLives > 0) 11:       // Play a sound for the chicken getting hit 12:       PlaySound((LPCSTR)IDW_SQUISH, _hInstance, SND_ASYNC  13:         SND_RESOURCE); 14:     else 15:     { 16:       // Play a sound for the game ending 17:       PlaySound((LPCSTR)IDW_GAMEOVER, _hInstance, SND_ASYNC  18:         SND_RESOURCE); 19: 20:       // Display game over message 21:       TCHAR szText[64]; 22:       wsprintf(szText, "Game Over! You scored %d points.", _iScore); 23:       MessageBox(_pGame->GetWindow(), szText, TEXT("Henway 2"), MB_OK); 24:       _bGameOver = TRUE; 25: 26:       // Pause the background music 27:       _pGame->PauseMIDISong(); 28:     } 29: 30:     return FALSE; 31:   } 32: 33:   return TRUE; 34: } 

If you remember, the SpriteCollision() function is where you detect a collision between the chicken and a car. This makes it an ideal place to play a squish sound when the chicken is run over (lines 12 and 13). Similarly, the SpriteCollision() function also knows when the game ends, so it only makes sense to play a sound whenever the player runs out of chickens (lines 17 and 18).

The final sound effect in the Henway 2 game occurs in the MoveChicken() function, which is where the game determines when the chicken has made it across the road. Listing 15.6 shows how a celebration sound is played each time the chicken makes it across.

Listing 15.6 The MoveChicken() Helper Function Plays a Celebration Sound Whenever the Chicken Makes It Safely Across the Road
 1: void MoveChicken(int iXDistance, int iYDistance)  2: {  3:   // Move the chicken to its new position  4:   _pChickenSprite->OffsetPosition(iXDistance, iYDistance);  5:  6:   // See if the chicken made it across  7:   if (_pChickenSprite->GetPosition().left > 400)  8:   {  9:     // Play a sound for the chicken making it safely across 10:     PlaySound((LPCSTR)IDW_CELEBRATE, _hInstance, SND_ASYNC  SND_RESOURCE); 11: 12:     // Move the chicken back to the start and add to the score 13:     _pChickenSprite->SetPosition(4, 175); 14:     _iScore += 150; 15:   } 16: } 

This function makes sure that a celebration wave sound is played whenever the chicken successfully crosses the road (line 10). This might be a small concession for working so hard to get the chicken across, but keep in mind that the previous version of the game offered no audible reward at all!

Testing the Finished Product

You'll find that testing sounds and music in games is initially one of the most exciting test phases of a game, but eventually becomes quite monotonous. The music in the Henway 2 game will no doubt haunt me for years when you consider that I had to listen to it over and over as I developed and debugged the game. You will likely experience the same joy and frustration when developing and testing your own games that take advantage of wave sounds and music.

To test the Henway 2 game, just launch the game and listen. The music will immediately start playing, and you'll quickly begin hearing random car horns that help create the atmosphere of busy traffic buzzing by the chicken. You will notice the new sound effects as you safely navigate the chicken across the road, as well as when you get run over. More important from the perspective of the MIDI music is to test the game as it is deactivated and then reactivated. Just minimize the game or activate a different program to see how the game responds. The music should immediately stop playing. Reactivating the game results in the music continuing to play from where it left off.



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