Building the Trippy Example Program


At this point, you've seen bits and pieces of GDI graphics code, and you've learned how to carry out basic drawing operations with a variety of different graphics shapes. You've also learned how to tweak the appearance of those shapes by creating and using different pens and brushes. You're now ready to put what you've learned into a complete example program that demonstrates how to draw graphics in the context of a game. Okay, you're not really creating a game in this hour , but you are using the game engine to draw some pretty neat graphics. The example program I'm referring to is called Trippy, and its name comes from the fact that it displays a psychedelic series of rectangles in different sizes and colors.

The idea behind the Trippy program is to draw a random rectangle in each cycle of the game engine. Although the rectangles are drawn outside of the GamePaint() function in response to a game cycle, it is still important to demonstrate how to draw within GamePaint() so that the drawing isn't lost when the window is minimized or hidden. For this reason, Trippy draws a grid of lines in GamePaint() to demonstrate how graphics drawn in this function are retained even if the window must be repainted. The actual rectangles are drawn in GameCycle() , which means that they are lost if the window is repainted. Let's take a look at how the code actually works for this example program.

Writing the Program Code

The fun begins in the Trippy program example with the header file, Trippy.h, which is shown in Listing 4.1. All of the code for the Trippy program is available on the accompanying CD-ROM.

Listing 4.1 The Trippy.h Header File Imports a Few Header Files and Declares the Global Game Engine Pointer, as Well as the Previous Rectangle That Was Drawn
 1: #pragma once  2:  3: //-----------------------------------------------------------------  4: // Include Files  5: //-----------------------------------------------------------------  6: #include <windows.h>  7: #include "Resource.h"  8: #include "GameEngine.h"  9: 10: //----------------------------------------------------------------- 11: // Global Variables 12: //----------------------------------------------------------------- 13: GameEngine* _pGame; 14: RECT        _rcRectangle; 

This code isn't too mysterious . In fact, the only real difference between this header and the one you saw for the Game Engine example in the previous hour is the declaration of the _rcRectangle global variable (line 14). This rectangle stores the previously drawn rectangle, which allows you to alter its position randomly for the next rectangle. The end result is that the rectangles tend to randomly drift around the screen, as opposed to popping up in random locations all over the place.

Moving right along, remember that we're now taking advantage of the game engine to simplify a great deal of the work in putting together programs. In fact, all that is really required of the Trippy program is to provide implementations of the core game functions. Listing 4.2 contains the code for the first of these functions, GameInitialize() .

Listing 4.2 The GameInitialize() Function Creates the Game Engine and Sets the Frame Rate to 15 Cycles Per Second
 1: BOOL GameInitialize(HINSTANCE hInstance)  2: {  3:   // Create the game engine  4:   _pGame = new GameEngine(hInstance, TEXT("Trippy"),  5:     TEXT("Trippy"), IDI_TRIPPY, IDI_TRIPPY_SM);  6:   if (_pGame == NULL)  7:     return FALSE;  8:  9:   // Set the frame rate 10:   _pGame->SetFrameRate(15); 11: 12:   return TRUE; 13: } 

The GameInitialize() function is responsible for creating the game engine (lines 4 and 5) and setting the frame rate for it (line 10). In this case, the frame rate is set at 15 cycles per second ( frames per second), which is plenty to demonstrate the psychedelic nature of the rectangles.

Following up on GameInitialize() is GameStart() , which actually gets things going. Listing 4.3 shows the code for the GamStart() function.

Listing 4.3 The GameStart() Function Seeds the Random Number Generator and Establishes an Initial Rectangle
 1: void GameStart(HWND hWindow)  2: {  3:   // Seed the random number generator  4:   srand(GetTickCount());  5:  6:   // Set the position and size of the initial rectangle  7:   _rcRectangle.left = _pGame->GetWidth() * 2 / 5;  8:   _rcRectangle.top = _pGame->GetHeight() * 2 / 5;  9:   _rcRectangle.right = _rcRectangle.left + _pGame->GetWidth() / 5; 10:   _rcRectangle.bottom = _rcRectangle.top + _pGame->GetHeight() / 5; 11: } 

Any program that makes use of random numbers is responsible for seeding the built-in random number generator. This is accomplished in line 4 with the call to srand() . You'll see this line of code in virtually all the program examples throughout the book because most of them involve the use of random numbers; random numbers often play heavily into the development of games . The remainder of the GameStart() function is responsible for setting the position and size of the initial rectangle to be drawn (lines 7 “10). This rectangle is sized proportionally to the client area of the main program window, and positioned centered within the client area.

I mentioned earlier that part of the Trippy program was to demonstrate the difference between drawing graphics in the GamePaint() function, as opposed to drawing them in GameCycle() . Listing 4.4 shows the code for GamePaint() , which in this case is responsible for drawing a bunch of grid lines as a background for the rectangles.

Listing 4.4 The GamePaint() Function Draws a Grid of Lines to Fill the Entire Client Area
 1: void GamePaint(HDC hDC)  2: {  3:   // Draw grid lines as a background for the rectangles  4:   const int iGridLines = 50;  5:   for (int i = 1; i <= iGridLines; i++)  6:   {  7:     // Draw a horizontal grid line  8:     MoveToEx(hDC, _pGame->GetWidth() * i / iGridLines, 0, NULL);  9:     LineTo(hDC, _pGame->GetWidth() * i / iGridLines, _pGame->GetHeight()); 10: 11:     // Draw a vertical grid line 12:     MoveToEx(hDC, 0, _pGame->GetHeight() * i / iGridLines, NULL); 13:     LineTo(hDC, _pGame->GetWidth(), _pGame->GetHeight() * i / iGridLines); 14:   } 15: } 

The line drawing functions you learned about in this hour, MoveToEx() and LineTo() , are both used to draw a series of horizontal (lines 8 and 9) and vertical (lines 12 and 13) grid lines in the client area. Because these lines are being drawn in GamePaint() , they are not lost when the window is repainted. You can easily alter the number of grid lines by changing the value of the iGridLines variable (line 4).

The GameCycle() function is where the actual rectangles are drawn, as shown in Listing 4.5.

Listing 4.5 The GameCycle() Function Randomly Alters the Position of the Rectangle, and Then Draws It in a Random Color
 1: void GameCycle()  2: {  3:   HDC         hDC;  4:   HWND        hWindow = _pGame->GetWindow();  5:   HBRUSH      hBrush;  6:  7:   // Randomly alter the position and size of the rectangle  8:   int iInflation = (rand() % 21) - 10;  9:   InflateRect(&_rcRectangle, iInflation, iInflation); 10:   OffsetRect(&_rcRectangle, (rand() % 19) - 9, (rand() % 19) - 9); 11: 12:   // Draw the new rectangle in a random color 13:   hBrush = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256)); 14:   hDC = GetDC(hWindow); 15:   FillRect(hDC, &_rcRectangle, hBrush); 16:   ReleaseDC(hWindow, hDC); 17:   DeleteObject(hBrush); 18: } 

The GameCycle() function is interesting in that it does a few things you haven't seen before. First of all, it uses two new Win32 functions, InflateRect() and OffsetRect() , to randomly alter the size and position of the previous rectangle. A random inflation value is first calculated, which is in the range of -10 to 10 (line 8). This value is then used as the basis for shrinking or growing the rectangle using the InflateRect() function (line 9). The rectangle is then offset by a random amount between -9 and 9 using the OffsetRect() function (line 10).

With the new rectangle size and position figured out, the GameCycle() function moves on to determine a new fill color for it. This is accomplished by randomly selecting a color for a new solid brush (line 13). If you recall, earlier I mentioned that you had to select a graphics object into a device context in order to use it. In this case, however, it's possible to use a different rectangle function that allows you to directly provide the brush to be used. I'm referring to FillRect() , which accepts a handle to a device context, a rectangle, and a brush (line 15). After filling the rectangle with the solid brush, the device context is released (line 16) and the brush is deleted (line 17).

Testing the Finished Product

Now that you've worked through the code for the Trippy program example, I suspect that you're ready to see it in action. Figure 4.6 shows the program running in all of its psychedelic splendor.

Figure 4.6. The Trippy program example uses rapidly drawn rectangles to achieve a psychedelic effect.

graphics/04fig06.jpg

If you recall from the code, the Trippy program is smart enough to redraw the grid lines in the background if the window is minimized or resized, but it doesn't take into account redrawing the rectangles. You can test this out by covering part of the window with another window, and then revealing the window again. The portion of rectangles not covered will be erased because of the repaint, but the part of the window that was visible will remain untouched. This redraw problem is not too difficult to fix. In fact, you solve the problem in Hour 5 when you build a slideshow program example using bitmap images.



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