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:
·An array of axes that will be involved in the effect. For a joystick this array will normally consist of the identifiers for the x-axis and the y-axis.
·An array of values for setting the direction. The values will differ according to the number of axes, and according to whether you want to use polar, spherical, or Cartesian coordinates. For a full explanation of this rather complicated business, see Effect Direction.
·A structure of type-specific parameters. In the example, since you are creating a periodic effect, this will be of type DIPERIODIC.
·A DIENVELOPE structure for defining the envelope to be applied to the effect.
·Finally, a DIEFFECT structure to contain the basic parameters for the effect.
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.