Building the Fore 2 Program Example


If you recall from the previous hour , the Fore program example demonstrated how to use the Sprite class to create a few sprites and move them around on the game screen. You're now going to enhance that program example a little by reworking it to support the new sprite management features built in to the game engine, as well as double-buffer animation. The new version of the Fore program is called Fore 2, and it serves as a great test bed for exploring the sprite features you've now added to the game engine.

Writing the Program Code

As with most programs created using the game engine, the best place to start with the code is the GameStart() function, which is used to initialize global variables and get everything in place. Listing 11.3 shows the code for the GameStart() and GameEnd() functions in the Fore 2 program.

Listing 11.3 The GameStart() Function Initializes the Offscreen Buffer Variables and Adds Sprites to the Game Engine, Whereas the GameEnd() Function Cleans Up the Offscreen Buffer
 1: void GameStart(HWND hWindow)  2: {  3:   // Seed the random number generator  4:   srand(GetTickCount());  5:  6:   // Create the offscreen device context and bitmap  7:   _hOffscreenDC = CreateCompatibleDC(GetDC(hWindow));  8:   _hOffscreenBitmap = CreateCompatibleBitmap(GetDC(hWindow),  9:     _pGame->GetWidth(), _pGame->GetHeight()); 10:   SelectObject(_hOffscreenDC, _hOffscreenBitmap); 11: 12:   // Create and load the bitmaps 13:   HDC hDC = GetDC(hWindow); 14:   _pForestBitmap = new Bitmap(hDC, IDB_FOREST, _hInstance); 15:   _pGolfBallBitmap = new Bitmap(hDC, IDB_GOLFBALL, _hInstance); 16: 17:   // Create the golf ball sprites 18:   RECT    rcBounds = { 0, 0, 600, 400 }; 19:   Sprite* pSprite; 20:   pSprite = new Sprite(_pGolfBallBitmap, rcBounds, BA_WRAP); 21:   pSprite->SetVelocity(5, 3); 22:   _pGame->AddSprite(pSprite); 23:   pSprite = new Sprite(_pGolfBallBitmap, rcBounds, BA_WRAP); 24:   pSprite->SetVelocity(3, 2); 25:   _pGame->AddSprite(pSprite); 26:   rcBounds.left = 265; rcBounds.right = 500; rcBounds.bottom = 335; 27:   pSprite = new Sprite(_pGolfBallBitmap, rcBounds, BA_BOUNCE); 28:   pSprite->SetVelocity(-6, 5); 29:   _pGame->AddSprite(pSprite); 30:   rcBounds.right = 470; 31:   pSprite = new Sprite(_pGolfBallBitmap, rcBounds, BA_BOUNCE); 32:   pSprite->SetVelocity(7, -3); 33:   _pGame->AddSprite(pSprite); 34: 35:   // Set the initial drag info 36:   _pDragSprite = NULL; 37: } 38: 39: void GameEnd() 40: { 41:   // Cleanup the offscreen device context and bitmap 42:   DeleteObject(_hOffscreenBitmap); 43:   DeleteDC(_hOffscreenDC); 44: 45:   // Cleanup the bitmaps 46:   delete _pForestBitmap; 47:   delete _pGolfBallBitmap; 48: 49:   // Cleanup the sprites 50:   _pGame->CleanupSprites(); 51: 52:   // Cleanup the game engine 53:   delete _pGame; 54: } 

The first big change in the GameStart() function, as compared to its previous version, is the creation of the offscreen device context and bitmap (lines 7 “10). The other change has to do with how the golf ball sprites are created. Rather than use a global array of sprites to keep track of the sprites, they are now just created and added to the game engine via the AddSprite() method (lines 22, 25, 29, and 33). Actually, there is an extra golf ball sprite in the Fore 2 program example, which is helpful in demonstrating the collision detection features now built in to the game engine.

You might notice that two of the sprites have their bounding rectangles set differently than the others. More specifically , the third sprite has its bounding rectangle diminished in size, which limits the area in which the sprite can travel (line 26). Similarly, the last sprite's bounding rectangle is further reduced to limit its travel area even more (line 30). When you later test the program, you'll see that these diminished bounding rectangles give the balls the effect of bouncing between trees in the forest background image.

The GameEnd() function is similar to the previous version except that it now cleans up the offscreen bitmap and device context that are required for double-buffer animation (lines 42 and 43).

One of the areas where the new sprite manager really simplifies things is in the GamePaint() function, which is shown in Listing 11.4.

Listing 11.4 The GamePaint() Function Draws the Forest Background and All the Sprites in the Sprite List
 1: void GamePaint(HDC hDC)  2: {  3:   // Draw the background forest  4:   _pForestBitmap->Draw(hDC, 0, 0);  5:  6:   // Draw the sprites  7:   _pGame->DrawSprites(hDC);  8: } 

Notice in this code that the entire list of sprites is drawn using a single call to the DrawSprites() method in the game engine (line 7). This is a perfect example of how a little work in the game engine can really help make your game code easier to manage and understand.

Unlike the GamePaint() function, the GameCycle() function is a little more complex in the Fore 2 program than in its predecessor. However, as Listing 11.5 reveals, the new code consists solely of the familiar double-buffer code that you saw in the previous section.

Listing 11.5 The GameCycle() Function Updates the Sprites in the Sprite List, and Then Draws Them to an Offscreen Memory Buffer Before Updating the Game Screen
 1: void GameCycle()  2: {  3:   // Update the sprites  4:   _pGame->UpdateSprites();  5:  6:   // Obtain a device context for repainting the game  7:   HWND  hWindow = _pGame->GetWindow();  8:   HDC   hDC = GetDC(hWindow);  9: 10:   // Paint the game to the offscreen device context 11:   GamePaint(_hOffscreenDC); 12: 13:   // Blit the offscreen bitmap to the game screen 14:   BitBlt(hDC, 0, 0, _pGame->GetWidth(), _pGame->GetHeight(), 15:     _hOffscreenDC, 0, 0, SRCCOPY); 16: 17:   // Cleanup 18:   ReleaseDC(hWindow, hDC); 19: } 

The GameCycle() function first updates the sprites in the sprite list with a call to the game engine's UpdateSprites() method (line 4). The remainder of the code in the function should look familiar to you because it is identical to the code you saw earlier when you learned about double-buffer animation. The GamePaint() method is called to paint the game graphics to the offscreen device context (line 11). The offscreen image is then blitted to the game screen's device context to finish the painting (lines 14 and 15).

If you recall from the previous hour, the left mouse button can be used to click and drag a golf ball sprite around on the game screen. Listing 11.6 contains the code for the three mouse functions that make sprite dragging possible.

Listing 11.6 The MouseButtonDown() , MouseButtonUp() , and MouseMove() Functions Use New Game Engine Sprite Manager Features to Simplify the Process of Dragging a Sprite Around the Game Screen
 1: void MouseButtonDown(int x, int y, BOOL bLeft)  2: {  3:   // See if a ball was clicked with the left mouse button  4:   if (bLeft && (_pDragSprite == NULL))  5:   {  6:     if ((_pDragSprite = _pGame->IsPointInSprite(x, y)) != NULL)  7:     {  8:       // Capture the mouse  9:       SetCapture(_pGame->GetWindow()); 10: 11:       // Simulate a mouse move to get started 12:       MouseMove(x, y); 13:     } 14:   } 15: } 16: 17: void MouseButtonUp(int x, int y, BOOL bLeft) 18: { 19:   // Release the mouse 20:   ReleaseCapture(); 21: 22:   // Stop dragging 23:   _pDragSprite = NULL; 24: } 25: 26: void MouseMove(int x, int y) 27: { 28:   if (_pDragSprite != NULL) 29:   { 30:     // Move the sprite to the mouse cursor position 31:     _pDragSprite->SetPosition(x - (_pDragSprite->GetWidth() / 2), 32:       y - (_pDragSprite->GetHeight() / 2)); 33: 34:     // Force a repaint to redraw the sprites 35:     InvalidateRect(_pGame->GetWindow(), NULL, FALSE); 36:   } 37: } 

The mouse functions in Fore 2 are simplified from their previous versions thanks to the new and improved game engine. For example, the MouseButtonDown() function now relies on the IsPointInSprite() method in the game engine to check and see if the mouse position is located within a sprite (line 6). The other two mouse functions are very similar to their previous counterparts, except that they now rely on a sprite pointer to keep track of the drag sprite, as opposed to an index into an array of sprites. For example, notice that when the mouse button is released, the _pDragSprite pointer is set to NULL (line 23). Similarly, the same pointer is used to set the position of the drag sprite in the MouseButtonUp() function (lines 31 and 32).

The last function in the Fore 2 program example is the SpriteCollision() function, which is called whenever two sprites collide with each other. Listing 11.7 contains the code for this function.

Listing 11.7 The SpriteCollision() Function Swaps the Velocities of Sprites That Collide, Which Makes Them Appear to Bounce Off of Each Other
 1: BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee)  2: {  3:   // Swap the sprite velocities so that they appear to bounce  4:   POINT ptSwapVelocity = pSpriteHitter->GetVelocity();  5:   pSpriteHitter->SetVelocity(pSpriteHittee->GetVelocity());  6:   pSpriteHittee->SetVelocity(ptSwapVelocity);  7:   return TRUE;  8: } 

The SpriteCollision() function receives the two sprites that collided as its only arguments (line 1). The function handles the collision by swapping the velocities of the sprites (lines 4 “6). This has the effect of making the sprites appear to bounce off of each other and reverse direction. Notice that the SpriteCollision() function returns TRUE at the end to indicate that the sprites should be restored to their old positions prior to the collision (line 7).

Testing the Finished Product

The improvements you made in the Fore 2 program example are somewhat subtle, but they are significant in terms of adding functionality to the game engine that is required to create real games . For example, it is critical that you be able to detect collisions between sprites and react accordingly . The collision detection support in the game engine now makes it very easy to tell when two sprites have collided, and then take appropriate action. Although it's hard to show sprite collisions in a still image, Figure 11.2 shows the Fore 2 program example in action.

Figure 11.2. The golf ball sprites in the Fore 2 program move around and bounce off of each other thanks to the new and improved sprite management features in the game engine.

graphics/11fig02.gif

If you pay close attention to the sprites, you'll notice that two of them appear to bounce between trees in the background. These two sprites are the ones whose bounding rectangles were reduced to limit their movement. You can see that bounding rectangles provide a simple yet effective way to limit the movement of sprites. Keep in mind that you can still click and drag any of the sprites with the left mouse button. Now that the sprites are sensitive to collisions, dragging them around with the mouse is considerably more interesting.



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