Revamping the Game Engine for Input


Because we've already built a game engine for use in carrying out the various tasks associated with game management, it only makes sense to incorporate user input handling into the game engine. Granted, a certain aspect of handling user input is game specific, and therefore must be handled in the code for each individual game. However, there are some general aspects of keyboard and mouse handling that you can incorporate into the game engine to simplify the work required of specific game code.

If you recall, the idea behind the game engine is to provide a certain degree of game functionality in a self-contained class, and then delegate game specific tasks to a series of functions that each game is responsible for providing. For example, the GamePaint() function must be provided in a game to draw the game screen. Although the game engine doesn't technically provide code for the GamePaint() function, it does make sure that it gets called whenever the game window needs to be painted ; it's up to each game to provide the actual code for GamePaint() , which makes sense because every game draws itself differently. Similar functions need to be added to the game engine to provide games with a means of handling user input.

The next couple of sections guide you through modifications to the game engine to add support for keyboard and mouse input handling.

Adding Keyboard Support

Earlier this hour , you found out that the standard Windows approach to keyboard handling with messages simply isn't good enough for games. A much better way to handle keyboard input is to repeatedly check the state of the keyboard for specific key presses, and then react accordingly . Using this strategy, much of the burden of keyboard input handling is passed on to the game code, which means that the game engine is primarily responsible only for calling a keyboard handler function to give the game a chance to respond to key presses. Following is what this function looks like:

 void HandleKeys(); 

The HandleKeys() function must be provided as part of the game code, and therefore isn't included in the game engine. If you don't want your game to support keyboard input, you can just leave the HandleKeys() function blank. Of course, the game engine must make sure that the HandleKeys() function gets called rapidly enough to give your games a responsive feel. This is accomplished in the WinMain() function within the game engine code. Following is the change made to this function:

 if (iTickCount > iTickTrigger) {   iTickTrigger = iTickCount +     GameEngine::GetEngine()->GetFrameDelay();   HandleKeys();   GameCycle(); } 

The only change to the WinMain() code is the new call to the HandleKeys() function. Notice that this call occurs just before the GameCycle() function, which means that a game gets a chance to respond to keyboard input before every game cycle. Don't forget; the specifics of handling keyboard input are carried out in each specific game when you create your own HandleKeys() function. You find out how to do this later in the hour.

Adding Mouse Support

Although keyboard input in games is admittedly non-standard in terms of deviating from how things are typically handled in Windows programming, mouse input is handled the old-fashioned waywith messages. It's not that mouse messages are more efficient than keyboard messages; it's just harder to notice sluggish mouse input. In other words, mouse messages appear to be fast enough to allow you to create a responsive mouse interface, whereas keyboard messages do not.

In order to support mouse input, games must support the following three functions, which are called by the game engine to pass along mouse events:

 void MouseButtonDown(int x, int y, BOOL bLeft); void MouseButtonUp(int x, int y, BOOL bLeft); void MouseMove(int x, int y); 

In order to connect mouse messages with these mouse handler functions, the game engine must look for the appropriate mouse messages and respond accordingly. The following code includes the new portion of the GameEngine::HandleEvent() method that is responsible for handling mouse messages delivered to the main game window.

 case LBUTTONDOWN:   // Handle left mouse button press   MouseButtonDown(LOWORD(lParam), HIWORD(lParam), TRUE);   return 0; case WM_LBUTTONUP:   // Handle left mouse button release   MouseButtonUp(LOWORD(lParam), HIWORD(lParam), TRUE);   return 0; case WM_RBUTTONDOWN:   // Handle right mouse button press   MouseButtonDown(LOWORD(lParam), HIWORD(lParam), FALSE);   return 0; case WM_RBUTTONUP:   // Handle right mouse button release   MouseButtonUp(LOWORD(lParam), HIWORD(lParam), FALSE);   return 0; case WM_MOUSEMOVE:   // Handle mouse movement   MouseMove(LOWORD(lParam), HIWORD(lParam));   return 0; 

This code handles the following mouse messages: WM_LBUTTONDOWN , WM_LBUTTONUP , WM_RBUTTONDOWN , WM_RBUTTONUP , and WM_MOUSEMOVE . Each piece of message handler code simply calls one of the mouse functions with the appropriate arguments. The first and second arguments to all the mouse functions include the X and Y position of the mouse cursor at the moment the message was delivered. The last argument to the mouse button functions is a Boolean value that identifies whether the left ( TRUE ) or right ( FALSE ) mouse button is involved in the event.

As you can hopefully tell from the code, the idea behind the mouse functions is to allow games to simply provide MouseButtonDown() , MouseButtonUp() , and MouseMove() functions, as opposed to getting involved with message handling. So, to support the mouse in your games, all you have to do is provide code for these three functions.

Sprucing Up the Bitmap Class

You've now made the necessary changes to the game engine to prepare it for keyboard and mouse input. However, there is another minor change you need to make that doesn't technically have anything to do with input. I'm referring to bitmap transparency , which allows bitmaps to not always appear as square graphical objects. Don't get me wrong; bitmaps definitely are square graphical objects, but they don't necessarily have to be drawn that way. The idea behind transparency is that you can identify a color as the transparent color , which is then used to indicate parts of a bitmap that are transparent. When the bitmap is drawn, pixels of the transparent color aren't drawn, and the background shows through.

graphics/book.gif

Why is there a discussion of bitmap transparency in an hour focused on keyboard and mouse input? The answer has to do with the fact that I want you to view the game engine as a work in progress that is constantly evolving and picking up new features. In this case, the program example at the end of this hour benefits greatly from bitmap transparency, so it only makes sense to add the feature here. You'll continue to make small improvements to the game engine throughout the book even if they don't tie in directly to the topic at hand. The end result will be a game engine with a lot of slick little features that will make your games all the more fun.


From a graphics creation perspective, you create bitmaps with transparency by selecting a color that isn't used in your graphics, such as "hot purple," which is also known as magenta. You then use magenta to fill areas of your bitmaps that need to appear transparent. It's then up to the revamped game engine to make sure that these transparent regions don't get drawn with the rest of the bitmap. You obviously don't want magenta borders around your images!

The trick to making bitmap transparency work in the game engine is to expand the existing Bitmap::Draw() method so that it supports transparency. This is accomplished by adding two new arguments:

  • bTrans a Boolean value that indicates whether the bitmap should be drawn with transparency

  • crTransColor the transparent color of the bitmap

It's important to try and make changes to the game engine that don't cause problems with programs that we've already written. So, rather than add these two arguments to the Draw() method and require them of all bitmaps, it's much better to add them and provide default values:

 void Draw(HDC hDC, int x, int y, BOOL bTrans = FALSE,   COLORREF crTransColor = RGB(255, 0, 255)); 

If you notice, the default value of bTrans is FALSE , which means that if you leave off the argument, transparency isn't used. This works great for existing code because it doesn't change the way the Draw() method already worked. In case you're curious , the default color specified in crTransColor ( RGB(255, 0, 255) ) is magenta, so if you stick with that color as your transparent color, you won't have to specify a transparent color in the Draw() method.

The only significant changes to the Draw() method involve checking the transparency argument, and then drawing the bitmap with transparency using the Win32 TransparentBlt() function if the argument is TRUE . Otherwise, it's business as usual with the BitBlt() function being used to draw bitmaps without transparency.

graphics/book.gif

Although the TransparentBlt() function is part of the Win32 API, it isn't as widely supported as the traditional BitBlt() function. More specifically , the function isn't supported in versions of Windows prior to Windows 98, such as Windows 95.


The TransparentBlt() function is part of the Win32 API, but it requires the inclusion of a special library called msimg32.lib in order for your games to compile properly. This is a standard library that should be included with your compiler, but you'll need to make sure that it is linked in with any programs that use the TransparentBlt() function. If you aren't familiar with altering linker settings for your compiler, just take a look at the compiler documentation and find out how to add additional libraries to a project; it typically involves simply entering the name of the library, msimg32.lib in this case, in a project settings window. Or, if you happen to be using Microsoft Visual Studio, you can follow these steps:

  1. Open the project in Visual Studio (Visual C++).

  2. Right-click on the project's folder in Solution Explorer, and click Properties in the pop-up menu.

  3. Click the Linker folder in the left pane of the Properties window, and then click Input.

  4. Click next to Additional Dependencies in the right pane, and type msimg32.lib .

  5. Click the OK button to accept the changes to the project.

After completing these steps, you can safely compile a program and know that the msimg32.lib library is being successfully linked into the executable program file.

graphics/book.gif

The source code for the examples in the book is located on the accompanying CD-ROM, and includes Visual C++ project files with the appropriate linker settings already made.




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