Force FeedbackMany input controllers come with some sort of force feedback these days, from rumble gamepads on game consoles to full force feedback joysticks. This technique is just another layer in our pursuit of immersion and realism. Force feedback hardware simulates vibration and other force-based effects by incorporating one or more motors that can be configured via programming. These motors vary the position or resistance of the game controller, and by programming fast movement sequences, the illusion of vibration and force are achieved. The number of motors and kind of controller determine the quality of the effect. On the lower end of the spectrum, all gamepads—since the original PlayStation controller—support some kind of force feedback, usually different kinds of vibration much like a cell phone. Joysticks can perform much more sophisticated routines. For example, it is possible to program the joystick to draw a circle with the stick, as well as many other interesting effects. Programming force feedback devices involves three steps. First, we need a way to create or describe the desired effect in terms of how it affects the controller's position, strength, and so on. Some tools are available for this purpose. Second, the effect must be loaded to the input API. Third, we need a way to reproduce the effect at runtime, either by manually triggering it or by assigning it to a specific event like a timer or specific button press. Sadly, these three steps are largely platform dependent, because developing applications that support force feedback depends greatly on the API. For the remainder of this section, we will focus on Microsoft's DirectInput solution to provide a complete example of how these devices work. DirectInput allows the creation of force feedback effects both from an external content-creation tool or by using a specific set of commands from within our application. The easiest way is to use the creation tool, called the Force Editor (see Figure 5.4). The editor has a look and feel similar to an audio application, but it works with force curves instead of sound. We can select different kinds of waveforms (square, sawtooth, sine, and so on) and modulate them, so the resulting effect is believable. We can even prototype the effect from the editor, getting a preview of how the end product will feel. When we are happy with the results, we can save them to .ffe files, which will then be read by DirectInput. Figure 5.4. Microsoft's Force Editor, built into the DirectX SDK.From the application's point of view, differences start at the device enumeration phase. We need to query for force feedback compatible devices only, as follows: HRESULT hr = g_pDI->EnumDevices( 0, EnumFFDevicesCallback, 0, DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK ); No changes to the EnumDevicesCallback function are required. A force feedback joystick is, as far as devices go, just a plain joystick that can be read as usual. The next relevant step is reading the effects from a .ffe file. One such file can contain several effects, so we will use an Enum call with a Callback function to retrieve those effects. Here is the Enum call: HRESULT hr = g_pFFDevice->EnumEffectsInFile( strFileName, EnumAndCreateEffectsCallback, NULL, DIFEF_MODIFYIFNEEDED ); Let's now examine the code for the callback. For simplicity, I will assume the .ffe file only contains one effect; so after retrieving it, no further calls will be pursued. Here is a suggested implementation of the routine, which stores the effect in persistent structure for later access. As usual, error checking is deleted for clarity: LPDIRECTINPUTEFFECT pDIEffect; BOOL CALLBACK EnumAndCreateEffectsCallback( LPCDIFILEEFFECT pDIFileEffect, VOID* pvRef ) { HRESULT hr = g_pFFDevice->CreateEffect( pDIFileEffect->GuidEffect, pDIFileEffect->lpDiEffect, &pDIEffect, NULL ); return DIENUM_STOP; } Then, all we have to do is trigger the effect as needed. This is a two-step process. First, we need to stop any previous forces, so both effects do not conflict. Second, we trigger the new effect. Here is a simple example—first, the stop for any other effects: HRESULT hr = g_pFFDevice->SendForceFeedbackCommand( DISFFC_STOPALL ); second, the execution of the effect: HRESULT hr = pDIEffect->Start(1, 0); The first parameter is the number of repetitions we want for the effect, and the second parameter is a flags parameter. Passing INFINITE as the first parameter causes the effect to loop forever, such as the engine vibration for a helicopter. These kinds of effects must be manually stopped with a call to: pDIEffect->Stop(); The force feedback API from DirectInput is very comprehensive. But we have only scratched the surface. For example, we can mix effects dynamically or even create them procedurally without using the Force Editor. With time and lots of experimentation, many interesting uses can be devised. |