Step 4: Creating an Effect

If the EffectFound flag is no longer FALSE after effect enumeration, you can safely assume that DirectInput has found support for at least one effect of the type you requested. (Of course, in real life you would probably not be content with finding just any periodic effect; you would want to use a particular kind such as a sine or sawtooth.) Armed with the effect GUID, you can now create the effect object.

Before calling the IDirectInputDevice2::CreateEffect method, you need to set up the following arrays and structures:

First, declare the arrays and structures. You can initialize the arrays at the same time:

DWORD      dwAxes[2] = { DIJOFS_X, DIJOFS_Y };
LONG       lDirection[2] = { 0, 0 };
 
DIPERIODIC diPeriodic;      // type-specific parameters
DIENVELOPE diEnvelope;      // envelope
DIEFFECT   diEffect;        // general parameters
 

Now initialize the type-specific parameters. If you use the values in the example, you will create a full-force periodic effect with a period of one-twentieth of a second.

diPeriodic.dwMagnitude = DI_FFNOMINALMAX; 
diPeriodic.lOffset = 0; 
diPeriodic.dwPhase = 0; 
diPeriodic.dwPeriod = (DWORD) (0.05 * DI_SECONDS); 
 

To get the effect of the chain-saw motor trying to start, briefly coughing into life, and then slowly dying, you will set an envelope with an attack time of half a second and a fade time of one second. You'll get to the sustain value in a moment.

diEnvelope.dwSize = sizeof(DIENVELOPE);
diEnvelope.dwAttackLevel = 0; 
diEnvelope.dwAttackTime = (DWORD) (0.5 * DI_SECONDS); 
diEnvelope.dwFadeLevel = 0; 
diEnvelope.dwFadeTime = (DWORD) (1.0 * DI_SECONDS); 
 

Now you set up the basic effect parameters. These include flags to determine how the directions and device objects (buttons and axes) are identified, the sample period and gain for the effect, and pointers to the other data that you have just prepared. You also associate the effect with the fire button of the joystick, so that it will automatically be played whenever that button is pressed.

diEffect.dwSize = sizeof(DIEFFECT); 
diEffect.dwFlags = DIEFF_POLAR | DIEFF_OBJECTOFFSETS; 
diEffect.dwDuration = (DWORD) (2 * DI_SECONDS);
 
diEffect.dwSamplePeriod = 0;               // = default 
diEffect.dwGain = DI_FFNOMINALMAX;         // no scaling
diEffect.dwTriggerButton = DIJOFS_BUTTON0;
diEffect.dwTriggerRepeatInterval = 0;      
diEffect.cAxes = 2; 
diEffect.rgdwAxes = dwAxes; 
diEffect.rglDirection = &lDirection[0]; 
diEffect.lpEnvelope = &diEnvelope; 
diEffect.cbTypeSpecificParams = sizeof(diPeriodic);
diEffect.lpvTypeSpecificParams = &diPeriodic;  
 

So much for the setup. At last you can create the effect:

LPDIEFFECT  g_lpdiEffect;  // global effect object
 
HRESULT hr = g_lpdid2Game->CreateEffect(
                     guidEffect,     // GUID from enumeration
                     &diEffect,      // where the data is
                     &g_lpdiEffect,  // where to put interface pointer
                     NULL);          // no aggregation
if (FAILED(hr)) 
  {
  OutputDebugString("Failed to create periodic effect");
  }
 

Remember that, by default, the effect is downloaded to the device as soon as it has been created, provided that the device is in an acquired state at the exclusive cooperative level. So if everything has gone according to plan, you should be able to compile, run, press the "fire" button, and feel the sputtering of a chain saw that's out of gas.