and responding to user input. If the function doesn’t return JOYERR_UNPLUGGED, you’re in good shape. When working with joysticks in the Win32 API, you always reference a joystick using a unique ID. This ID must be one of the built-in joystick IDs (JOYSTICKID1, JOYSTICKID2, and so on). In the case of the previous sample code, the JOYSTICKID1 ID checked out okay, so it’s the ID you must use to continue interacting with the same joystick. The joyGetPos() function is the function you call to check the status of the joystick, which is how you determine if the user has interacted with your game via the joystick. The status of the joystick is stored in the JOYINFO structure, which is defined as follows: typedef struct { UINT wXpos; UINT wYpos; UINT wZpos; UINT wButtons; } JOYINFO; You’ll notice that the JOYINFO structure is designed to accommodate joysticks with up to three axes of movement: x, y, and z. The first three members of the structure (wXpos, wYpos, and wZpos) indicate the position of the joystick with respect to each of these axes. The final member, wButtons, indicates the state of the joystick buttons. The wButtons member supports up to four buttons, as indicated by the following constants: JOY_BUTTON1, JOY_BUTTON2, JOY_BUTTON3, and JOY_BUTTON4. Earlier, I mentioned that Windows supports up to six axes and 32 buttons on joysticks, but the JOYINFO structure is obviously more limited than that. The reason for this disparity is because the full range of joystick features are only available through the DirectX game API, which is significantly more complex to use than the built-in Win32 joystick support. If you’re planning on supporting a gyroscopic nuclear-powered virtual reality game helmet in your games that takes advantage of all the axes and buttons possible, you definitely need to get to work learning DirectX. Otherwise, I think you’ll find the Win32 approach to joystick handling to be sufficient. In order to examine a JOYINFO structure to see what’s happened to a joystick, you must first obtain the joystick state using the joyGetPos() function, which is the same function you used to see if a joystick was plugged in. Following is a code example that examines a JOYINFO structure to see if the first joystick button is being pressed: JOYINFO jiInfo; if (joyGetPos(JOYSTICKID1, &jiInfo) == JOYERR_NOERROR) if (jiInfo.wButtons & JOY_BUTTON1) // Button 1 was pressed! This code calls the joyGetPos() function to fill a JOYINFO structure with information about the current joystick state. If the return value of the function is JOYERR_NOERROR, there was no problem retrieving the information and we can continue. The bitwise AND operator (&) is then used with the wButtons member of the JOYINFO structure and the JOY_BUTTON1 constant to see if button 1 on the joystick is being pressed. You can use the same approach to look at other joystick buttons. You’re probably wondering why I’ve avoided talking about the other members of the JOYINFO structure, and how they are used to determine the position of the joystick handle. The reason for this has to do with the fact that you need to understand the range of values possible for these members before you can make sense of them. More specifically, you need to find out the minimum and maximum values for each axis of movement that you’re interested in checking. For example, if you want to see if the joystick has been moved left, you first need to find out the range of values for the x axis of the joystick. You can then use this range to see how far left the joystick handle is being pushed, if at all. You can determine the ranges of joystick axes by calling the Win32 joyGetDevCaps() function. This function fills a JOYCAPS structure with more information about a joystick than you’ll probably ever want to know. For that reason, I won’t go into all the details of the JOYCAPS structure. Instead, I’d like to focus on how to use it to determine the range of the two primary axes, x and y. Following is a code snippet that determines the center point of the x and y axis, which reveals the ranges of each: JOYCAPS jcCaps; joyGetDevCaps(JOYSTICKID1, &jcCaps, sizeof(JOYCAPS)); DWORD dwXCenter = ((DWORD)jcCaps.wXmin + jcCaps.wXmax) / 2; DWORD dwYCenter = ((DWORD)jcCaps.wYmin + jcCaps.wYmax) / 2; The minimum and maximum values for the x axis are wXmin and wXmax, whereas the y axis is
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Clan Web Hosting services
the joystick by moving the handle or control pad in all of its directions, followed by pressing another button (see Figure 7.4). Figure 7.4. The Device Calibration Wizard determines the range of each joystick axis by asking you to move the handle or control pad in each direction. The final step is to leave the handle (control pad) alone once more and press a button. After performing these steps, you can click the Finish button to wrap up the calibration process. Although the steps to calibrating a joystick or game pad might seem kind of pointless, the idea is that your computer is analyzing the range of movement along each axis and properly determining the center point of each. Once the center point and maximum extents are determined, the joystick can then be accurately centered. Again, it’s very much like aligning a car so that it steers straight when you aren’t touching the steering wheel. I encourage you to calibrate your joystick any time it starts acting strange because it is possible for a joystick to lose calibration over a period of time as it begins to wear mechanically. Your joystick’s Properties window usually provides a test option for testing the joystick after you’ve calibrated it; this helps to make sure that the calibration worked properly. Figure 7.5 shows an example of a test window for the Microsoft SideWinder game pad. Figure 7.5. The test window for the Microsoft SideWinder game pad allows you to test out the control pad as well as the game pad’s buttons. The test window for a joystick or game pad is great because it quickly allows you to see if the device is working properly. If you get no response when testing a device, you know that something is wrong with the installation. You can also use the test window to see if a joystick or game pad is properly calibrated. Tracking Joystick Movements As you might have guessed, joysticks are a little more complicated to deal with than other input devices such as the keyboard and mouse. This primarily has to do with the fact that joysticks aren’t exactly considered standard devices, as well as the fact that there is a fair amount of variance when it comes to joystick features. The added complexity doesn’t have so much to do with handling specific joystick events as it does with determining if a joystick is connected and available for use. You also have to concern yourself with the concept of capturing a joystick, which gives your program exclusive control over the joystick. The first step in handling joystick input is checking to see if a joystick driver is installed and available on the computer system. Without the proper hardware driver in place, a physical joystick device is no good. Fortunately, Windows includes built-in drivers for most popular joysticks. However, it’s good to perform the check anyway. This is made possible by a call to a Win32 API function called joyGetNumDevs(). The joyGetNumDevs() function tells you how many joysticks are capable of being used on the computer system. Following is an example of how you might call the joyGetNumDevs() function to determine the number of joysticks available for use on the system: UINT uiNumJoysticks = joyGetNumDevs(); You now know how many joysticks can be used on the system, but that doesn’t tell you much about how many joysticks are actually present. To see if a real joystick is actually plugged in and ready to use, you call the joyGetPos() function, which provides a lot of information about a joystick. You must pass this function an ID that identifies the joystick you’re interested in; standard joystick IDs include JOYSTICKID1, JOYSTICKID2, and so on. So, to check for the presence of a single joystick, you can use code like this: JOYINFO jiInfo; if (joyGetPos(JOYSTICKID1, &jiInfo) != JOYERR_UNPLUGGED) // the joystick is plugged in and ready to go! In this code, the joyGetPos() function is called to retrieve joystick information for a single joystick in the form of a JOYINFO structure. You learn how to use the JOYINFO structure to analyze the state of the joystick in just a moment. For now, you’re simply providing it because the joyGetPos() function requires it. All you’re looking for in this code is the return value of joyGetPos(), which indicates whether the joystick is plugged in
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Clan Web Hosting services
by allowing the joystick handle to be pushed and pulled vertically. A fourth axis can be the twisting of the joystick handle. The fifth and sixth axes apply to more advanced input devices, and are typically used for keeping track of moving the entire joystick in space, like an input glove. Because a traditional joystick has only two axes of motion, you can think of the joystick in much the same way as you think of the mouse. Although a mouse can be moved in any direction, its movement is limited to a single plane. In other words, you can always resolve mouse movement into an XY value. Joysticks are similar in this manner because you can identify their movement according to how far the handle is being pushed along each axis. If side to side movement is along the x axis and forward and back movement is along the y axis, a joystick can be tracked in a manner similar to the mouse by using an XY value. Also similar to the mouse are the buttons on a joystick. Just as mice are capable of supporting multiple buttons (typically three), joysticks are also capable of having several buttons. In fact, joysticks are much more flexible than mice in terms of how many buttons they can have; joysticks in Windows are allowed to have up to 32 buttons. I personally wouldn’t want to try and figure out how to use a joystick with that many buttons, but the option is there if someone wants to make a joystick for super humans. A more realistic number for joystick buttons is six, which is still a lot to keep track of for the average game player. Similar to mouse button presses and keyboard key presses, handling joystick button presses is relatively straightforward, as you learn a little later in the hour. Calibrating Joysticks Before getting into the details of how to handle joystick input from a programming perspective, it’s worth addressing an important issue related to joysticks: calibration. Calibration is the process of fine-tuning the settings for a joystick so that the handle is properly centered. Joystick calibration is kind of like having your car aligned; when a joystick isn’t properly calibrated, it has a tendency to pull to one side. Fortunately, joystick calibration is easy to perform, and is readily available from the Windows Control Panel. To access joystick settings, just follow these steps in Windows XP: 1. Click the Start button, and select Control Panel. 2. Click Printers and Other Hardware. 3. Click Game Controllers. 4. Select the joystick (game controller) to calibrate, and then click the Properties button. Even if you aren’t using Windows XP, you’ll still find some kind of joystick or controller icon in the Control Panel for calibrating and testing your game controllers. The specifics of calibrating your joystick might be a little different from the steps I’ve listed, but the general idea is the same. After following these steps, you’ll be presented with a window that is specific to your particular joystick. In my case, I’m using a Microsoft SideWinder game pad, so the window I see is shown in Figure 7.1. Figure 7.1. The Properties window for your joystick should provide a means of calibrating the joystick. To calibrate my joystick (game pad), I just click the Calibrate button. This starts the Device Calibration Wizard, which is shown in Figure 7.2. Figure 7.2. The Device Calibration Wizard provides a means of calibrating joysticks in Windows XP. Click the Next button to get started calibrating the joystick (see Figure 7.3). Figure 7.3. Calibrating a joystick first involves pressing a button on the joystick without touching the joystick handle (or control pad for game pads). The first step in calibrating a joystick is to leave the handle centered and press a button, as shown in Figure 7.3. In the case of a game pad, you simply don’t touch the control pad (D-Pad) and then press a button. You are then prompted to calibrate the axes of
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Cheap Web Hosting services
Workshop The Workshop is designed to help you anticipate possible questions, review what you’ve learned, and begin learning how to put your knowledge into practice. The answers to the quiz can be found in Appendix A, “Quiz Answers.” Quiz 1: If you had to choose between supporting the keyboard or the mouse, which would you choose? 2: What do you have to do differently in a game to support trackballs? 3: Why is it important to extract information from the lParam argument when responding to a mouse message? Exercises 1. Experiment with different values for the _iMAXSPEED global variable in the UFO program example to see how it affects the flying saucer’s speed. 2. Modify the UFO program example so that you can click and drag the mouse to move the flying saucer around. 3. Modify the UFO program example so that the flying saucer wraps off the screen and appears on the other side when it reaches a side edge. Hour 7. Improving Input with Joysticks From its inception, the joystick has been used chiefly as an input device for game systems. Admittedly, its name alone limits its usage to the entertainment industry, as I doubt too many accountants would purchase a “joystick” for crunching numbers in a spreadsheet. At any rate, joysticks and game pads both play an important role in modern video games of all kinds, including computer games. For this reason, it’s important for you to have an understanding of how to interpret and respond to joystick input in your own games. This hour introduces you to joysticks and what makes them tick, along with providing you with the knowledge and source code to handle joystick input in games. In this hour, you’ll learn: The basics of responding to joystick input in games How to properly calibrate a joystick in Windows XP How to add joystick support to the game engine How to use the new and improved game engine to create interesting programs that respond to a joystick Joystick Basics The concept of a joystick is straightforward, although you might be surprised by how loosely a joystick is defined in terms of Windows programming. In Windows, a joystick is a physical input device that allows variable movement along different axes with multiple pushbuttons. That’s the geeky description of a joystick. What it means is that a joystick is an input device that can move in several different directions. Notice that I said several directions, not just two. Although a traditional joystick is thought of in terms of two axes (X and Y), a joystick in Windows can actually go up to six axes. Fortunately, we aren’t going to worry about more than two joystick axes in this hour, which helps simplify things considerably. The six possible joystick axes supported by Windows can be arranged in many different ways. A traditional joystick has two axes that correspond to moving the joystick handle side to side (one axis) and forward and back (another axis). A third axis of movement can be added
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Cheap Web Hosting services
new saucer position (line 8 ). The flying saucer is now being drawn and updated properly, but you still don’t have a way to change its speed so that it can fly. This is accomplished first by handling keyboard input in the HandleKeys() function, which is shown in Listing 6.5. Listing 6.5 The HandleKeys() Function Checks the Status of the Arrow Keys, Which Are Used to Control the Flying Saucer 1: void HandleKeys() 2: { 3: // Change the speed of the saucer in response to arrow key presses 4: if (GetAsyncKeyState(VK_LEFT) < 0) 5: _iSpeedX = max(-_iMAXSPEED, --_iSpeedX); 6: else if (GetAsyncKeyState(VK_RIGHT) < 0) 7: _iSpeedX = min(_iMAXSPEED, ++_iSpeedX); 8: if (GetAsyncKeyState(VK_UP) < 0) 9: _iSpeedY = max(-_iMAXSPEED, --_iSpeedY); 10: else if (GetAsyncKeyState(VK_DOWN) < 0) 11: _iSpeedY = min(_iMAXSPEED, ++_iSpeedY); 12: } The HandleKeys() function uses the Win32 GetAsyncKeyState() function to check the status of the arrow keys (VK_LEFT, VK_RIGHT, VK_UP, and VK_DOWN) and see if any of them are being pressed. If so, the speed of the flying saucer is adjusted appropriately. Notice that the newly calculated speed is always checked against the _iMAXSPEED global constant to make sure that a speed limit is enforced. Even flying saucers are required to stay within the speed limit! The GetAsyncKeyState() function is part of the Win32 API, and provides a means of obtaining the state of any key on the keyboard at any time. You specify which key you're looking for by using its virtual key code; Windows defines virtual key codes for all the keys on a standard keyboard. Common key codes for games include VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_CONTROL, VK_SHIFT, and VK_RETURN. If you thought handling keyboard input in the UFO program was easy, wait until you see how the mouse is handled. To make things a little more interesting, both mouse buttons are used in this program. The left mouse button sets the flying saucer position to the current mouse cursor position, whereas the right mouse button sets the speed of the flying saucer to 0. So, you can use the mouse to quickly get control of the flying saucer; just right-click to stop it and then left-click to position it wherever you want. Listing 6.6 shows the code for the MouseButtonDown() function, which makes this mouse magic possible. Listing 6.6 The MouseButtonDown() Function Uses the Left and Right Mouse Buttons to Move the Flying Saucer to the Current Mouse Position and Stop the Flying Saucer, Respectively 1: void MouseButtonDown(int x, int y, BOOL bLeft) 2: { 3: if (bLeft) 4: { 5: // Set the saucer position to the mouse position 6: _iSaucerX = x - (_pSaucer->GetWidth() / 2); 7: _iSaucerY = y - (_pSaucer->GetHeight() / 2); 8: } 9: else 10: { 11: // Stop the saucer 12: _iSpeedX = 0; 13: _iSpeedY = 0; 14: } 15: } The first step in this code is to check and see which one of the mouse buttons was pressed left or right (line 3). I know, most PC mice these days have three buttons, but I wanted to keep the game engine relatively simple, so I just focused on the two most important buttons. If the left mouse button was pressed, the function calculates the position of the flying saucer so that it is centered on the current mouse cursor position (lines 6 and 7). If the right button was pressed, the speed of the flying saucer is set to 0 (lines 12 and 13). If you determine that supporting two mouse buttons is simply too much of a weakness for the game engine, feel free to modify it on your own. It primarily involves changing the third
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Clan Web Hosting services
argument of the MouseButtonDown() function so that it can convey more than two values one for each of the three mouse buttons. Testing the Finished Product The UFO program example is the closest thing you’ve seen to a game thus far, and is quite interesting in terms of allowing you to fly around an animated graphical object. Hopefully, you’ll be pleasantly surprised by the responsiveness of the program’s keyboard controls. Figure 6.1 shows the UFO program in action as the flying saucer does a flyby of some desert cacti. Figure 6.1. The UFO program example demonstrates how to control an animated graphical object with the keyboard and mouse. If you guide the flying saucer to the edge of the game screen, it will stop, which is to be expected given the program code you just worked through. There are a variety of different ways to tweak this program and make it more intriguing, such as wrapping the flying saucer from one side of the screen to the other, which is why I hope you spend some time tinkering with the code. You’ll find some ideas for modifying the program in the Exercises for the lesson, which are coming up. Summary The ability to effectively communicate with the people that play your games is a critical factor of game design and development. In one direction a game communicates by displaying graphics and playing sounds and music, but in the other direction the user responds by interacting with a physical input device of some sort. It’s very important for game developers to master the fine art of responding to user input through a variety of different user input devices. The keyboard and mouse are the two fundamental user input devices that you can count on virtually all people having. This hour showed you how to handle and respond to keyboard and mouse input in an efficient manner specifically suited to games. Beyond the keyboard and mouse, it’s up to your resources and the specific needs of each game to determine whether you should support additional input devices. Hour 7 tackles the subject of joystick input, which is the next most important input device for games. Q&A Q1: Are there any games that wouldn’t require keyboard support? A1: Sure. Any game that requires extensive point-and-click style input, such as a card game, would probably be fine without any keyboard support. In fact, you create a memory game in Hour 8, “Example Game: Brainiac,” that is perfectly suited for mouse input alone. However, if you can figure out a way to add keyboard controls to mouse-centric games, by all means go for it. Q2: Is it possible to mix keyboard and mouse controls? A2: Of course! This actually hits on an interesting point not directly covered in this hour: Many games work great by combining mouse and keyboard controls together. A good example is the strafing feature in Doom, which also works with the mouse; it is activated by holding down the Shift key while moving the mouse left or right.
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Clan Web Hosting services
saucer, as you soon find out. Next on the game function agenda is the GameStart() function, which creates and loads the flying saucer bitmaps, as well as sets the initial flying saucer position and speed (Listing 6.2). Listing 6.2 The GameStart() Function Performs Startup Tasks for the UFO Program Example 1: void GameStart(HWND hWindow) 2: { 3: // Create and load the background and saucer bitmaps 4: HDC hDC = GetDC(hWindow); 5: _pBackground = new Bitmap(hDC, IDB_BACKGROUND, _hInstance); 6: _pSaucer = new Bitmap(hDC, IDB_SAUCER, _hInstance); 7: 8: // Set the initial saucer position and speed 9: _iSaucerX = 250 - (_pSaucer->GetWidth() / 2); 10: _iSaucerY = 200 - (_pSaucer->GetHeight() / 2); 11: _iSpeedX = 0; 12: _iSpeedY = 0; 13: } The GameStart() function is used to initialize data pertaining to the program such as the bitmaps and other global variables. The flying saucer position is initially set to the middle of the game screen (lines 9 and 10), and then the speed of the saucer is set to 0 so that it isn’t moving (lines 11 and 12). You might think that a program with an animated flying saucer cruising over a background image would require a complex GamePaint() function. However, Listing 6.3 shows how this simply isn’t the case. Listing 6.3 The GamePaint() Function Draws the Background and Flying Saucer Bitmaps 1: void GamePaint(HDC hDC) 2: { 3: // Draw the background and saucer bitmaps 4: _pBackground->Draw(hDC, 0, 0); 5: _pSaucer->Draw(hDC, _iSaucerX, _iSaucerY, TRUE); 6: } As the code reveals, the GamePaint() function for the UFO program is painfully simple. Aside from the standard code that you’re now getting accustomed to seeing in the GamePaint() function, all the function does is draw the background and flying saucer bitmaps. The background bitmap is drawn at the origin (0, 0) of the game screen (line 4), whereas the flying saucer is drawn at its current position (line 5). Notice that TRUE is passed as the last argument to the Draw() method when drawing the flying saucer, which indicates that the saucer is to be drawn with transparency using the default transparent color. The GameCycle() function is a little more interesting than the others you’ve seen because it is actually responsible for updating the position of the flying saucer based on its speed. Listing 6.4 shows how this is accomplished in the code for the GameCycle() function. Listing 6.4 The GameCycle() Function Updates the Saucer Position and Then Repaints the Game Screen 1: void GameCycle() 2: { 3: // Update the saucer position 4: _iSaucerX = min(500 - _pSaucer->GetWidth(), max(0, _iSaucerX + _iSpeedX)); 5: _iSaucerY = min(320, max(0, _iSaucerY + _iSpeedY)); 6: 7: // Force a repaint to redraw the saucer 8: InvalidateRect(_pGame->GetWindow(), NULL, FALSE); 9: } The GameCycle() function updates the position of the flying saucer by adding its speed to its position. If the speed is negative, the saucer will move to the left and/or up, whereas positive speed values move the saucer right and/or down. The seemingly tricky code for setting the position must also take into account the boundaries of the game screen so that the flying saucer can’t be flown off into oblivion. Granted, the concept of flying off the screen might sound interesting, but it turns out being quite confusing! Another option would be to wrap the saucer around to the other side of the screen if it goes over the boundary, which is how games like Asteroids solved this problem, but I opted for the simpler solution of just stopping it at the edges. After updating the position of the flying saucer, the GameCycle() function forces a repaint of the game screen to reflect the
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Clan Web Hosting services
Building the UFO Program Example In order to really get a feel for how keyboard and mouse input works in games, it’s helpful to work through a complete example. The remainder of this hour focuses on a program example called UFO. Although this program isn’t technically a game, it’s by far the closest thing you’ve seen to a game yet. It involves a flying saucer that you control with the keyboard and mouse. You’re able to fly the flying saucer around a bitmap background image. Perhaps most important is the fact that the UFO program demonstrates how good of a feel you can create for game controls. More specifically, the arrow keys on the keyboard are surprisingly responsive in the UFO program. Although you haven’t really learned about animation yet, the UFO program example makes use of animation to allow you to fly the flying saucer. Fortunately, the program is simple enough that you can get by without knowing the specifics about animation. All you really need to know is that you can alter the position of a bitmap image to simulate movement on the screen. This occurs thanks to the game engine, which redraws the bitmap every game cycle. So, by altering the position of an image and redrawing it repeatedly, you create the effect of movement. The UFO program example reveals how this task is accomplished, as well as how the keyboard and mouse fit into the picture. You learn the details of how animation works in Hour 9, “A Crash Course in Game Animation.” Writing the Program Code The header file for the UFO program example lays the groundwork for the meat of the program, which carries out the details of the flying saucer animation and user input. Listing 6.1 contains the code for the UFO.h header file, which declares global variables used to control the flying saucer. Listing 6.1 The UFO.h Header File Declares Global Variables Used to Keep Track of the Flying Saucer 1: #pragma once 2: 3: //—————————————————————-4: // Include Files 5: //—————————————————————-6: #include 7: #include “Resource.h” 8: #include “GameEngine.h” 9: #include “Bitmap.h” 10: 11: //—————————————————————-12: // Global Variables 13: //—————————————————————-14: HINSTANCE _hInstance; 15: GameEngine* _pGame; 16: const int _iMAXSPEED = 8; 17: Bitmap* _pBackground; 18: Bitmap* _pSaucer; 19: int _iSaucerX, _iSaucerY; 20: int _iSpeedX, _iSpeedY; The first thing of interest in this code is the _iMAXSPEED constant, which establishes the maximum speed of the flying saucer (line 16). The speed of the flying saucer is how many pixels it can travel in a given direction in each game cycle. So, the value of the _iMAXSPEED constant means that the flying saucer can never travel more than 8 pixels in the horizontal or vertical direction in a game cycle. The _pBackground and _pSaucer global variables store the two bitmaps used in the program, which correspond to a night sky background image and the flying saucer image (lines 17 and 18). The remaining variables pertain to the flying saucer, and include its XY position (line 19) and XY speed (line 20). The XY position of the flying saucer is specified relative to the game screen. The XY speed, on the other hand, simply tells the program how many pixels the flying saucer should be moved per game cycle; negative values for the speed variables indicate that the flying saucer is moving in the opposite direction. With the global variables in place, we can move on to the game functions. The first game function to consider is GameInitialize(), which creates the game engine and establishes the frame rate. The frame rate for the program is set to 30 frames per second. This is a relatively high frame rate for games, but it results in much smoother motion for the flying
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Clan Web Hosting services
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 way with 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. 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
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Clan Web Hosting services
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. 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. 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.
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Clan Web Hosting services