Getting the Device State

No matter what type of device you use—keyboard, mouse, joystick, and so on—you'll need to understand the techniques and subtleties of getting and controlling the state of your input devices. We'll start by working at the lowest level and then we'll work our way up the input device food chain. The interfaces to input devices are completely dependant on the platforms you use, and to some extent any middleware you might be using. Many 3D graphics engines also provide APIs to all the input hardware. Regardless of the API used or devices they control, there are two schemes for processing user input:

  • Polling: This method is a little old fashioned and requires an application to query each device for every trip around the main loop. When the device status changes, you call the appropriate handler. You might see this approach at work with handheld devices or non-Win32 platforms.

  • Callbacks or Messages: This method is much more common. Here you'll intercept calls or messages from the operating system and write handler functions.

If your game is developed for Win32, you either intercept messages like WM_MOUSEMOVE or you might consider using DirectX's DirectInput API. I'll discuss this API a little later in this chapter.

Some other platforms use a callback method. In this case your game registers a callback function for each device. Each time the status of the device changes, your function is called. Renderware, the 3D engine from Criterion, uses the callback approach as this simple example illustrates:

 // Attach input devices is called during game initialization RwBool AttachInputDevices(void) {    // These functions are Renderware APIs that register device callbacks    RsInputDeviceAttach(rsKEYBOARD, KeyboardHandler);    RsInputDeviceAttach(rsMOUSE, MouseHandler);    return TRUE; } 

The two functions, KeyboardHandler and MouseHandler parse the input parameters and translate them into actions that change the game state. Here's an example of the MouseHandler function:

 // NOTE: Don't bother searching MSDN for RsEventStatus or anything else in here. // This code is pulled straight from a 3D action/adventure game demo that used // Renderware from Criterion. Just look it over, and get a feel for the // structure. // // You'll be seeing this kind of thing in every mouse handler, regardless of // the platform. RsEventStatus MouseHandler(RsEvent event, void *param) {    RsMouseStatus *mouseStatus = (RsMouseStatus *)param;    switch( event )    {       case rsLEFTBUTTONDOWN:       {          LeftButtonDown(mouseStatus);          return rsEVENTPROCESSED;       }       case rsLEFTBUTTONUP:       {          LeftButtonUp(mouseStatus);          return rsEVENTPROCESSED;       }       case rsRIGHTBUTTONDOWN:       {          RightButtonDown(mouseStatus);          return rsEVENTPROCESSED;       }       case rsRIGHTBUTTONUP: ;       {          RightButtonUp(mouseStatus);          return rsEVENTPROCESSED;       }       case rsMOUSEMOVE:       {          MouseMove(mouseStatus);          return rsEVENTPROCESSED;       }       default:       {          return rsEVENTNOTPROCESSED;       }    } } 

Of course, every platform operates a little differently, but the code looks very similar; mouse buttons still go up and down and the entire device moves on a two dimensional plane. It's not crazy to assume that most device-handling code reflects the nature of the specific device:

  • Buttons: They will have up and down states. The down state might have an analog component. Most game controllers support button pressure as an 8-bit value.

  • One axis controllers: They will have a single analog state, with zero representing the unpressed state. Game controllers usually have analog triggers, for use in features such as accelerators in driving games.

  • Two axis controllers: A mouse and joystick are 2D controllers. Their status can be represented as integers or floating-point numbers. When using these devices, you shouldn't assume anything about their coordinate space: The coordinate (0,0) might represent the upper left-hand corner of the screen or it might reprsent the device center.

Game controllers, even complicated ones, are built from assemblies of these three component types. The tricked-out joysticks that the flight simulator fans go for are simply buttons and triggers attached to a 2D controller. To support such a device, you'll need to write a custom handler function for each component. Depending on the way your handler functions get the device status, you might have to factor the device status for each component out of a larger data structure. Eventually, you'll call your handler functions and change the game state.

Using DirectInput

DirectInput is the DirectX API for input devices such as the mouse, keyboard, joystick, game controllers, and force feedback devices. You might notice that this module of DirectX is a little different than most of the other APIs. There's no HAL, and that means there's nothing to hardware accelerate. DirectInput simply provides a way to interface with input devices.

Windows can certainly grab user input with DirectInput. Mouse and keyboard messages are well understood by a Win32 programmer the moment they create their first Win32 application. You might not be aware that the Win32 Multimedia: Platform SDK has everything you need to accept messages from your joystick. You don't even need DirectInput for that, so why bother? Straight Win32 code might not expose every feature of the weirder varieties of joysticks or PC game controller pads. That's where DirectInput will have to help you out. This is certainly true if you want an easy way to use the force-feedback devices.

Beyond this, another feature of DirectInput that's pretty useful is called action mapping. This is a concept that binds actions to virtual controls. Instead of looking at the X-axis of the joystick to find the direction of a car's steering wheel, DirectInput can map the action of steering the car to a virtual control. The actual controls can be mapped to the virtual controls at the whim of the player, and are the basis for providing a completely configurable control system. Hardcore gamers really love this. If you are making a hardcore game, you'll need configurable controls. DirectInput isn't the only way to make that work, however, but it does buy you a few other things like a standard way to tweak the force-feedback system.

Mass market games that don't use any advanced features of joysticks or don't have insanely configurable controls can work just fine with Windows messages and the Windows Multimedia Platform SDK. You don't have to learn to use DirectInput to make games, and Windows messages are easy and familiar. There are plenty of DirectInput samples in the DirectX SDK for you to look at, so I'm not going to waste your time or any trees on the subject. What I want to work on is the fact that there's plenty to talk about in terms of user interface code, regardless of the API you use.

Game Coding Complete
Game Coding Complete
ISBN: 1932111751
EAN: 2147483647
Year: 2003
Pages: 139 © 2008-2017.
If you may any questions please contact us: