Partager via


Prise en main de XInput dans les applications Windows

XInput permet aux applications Windows de traiter les interactions du contrôleur (y compris les effets de rumble du contrôleur et les entrées et sorties vocales).

Cette rubrique fournit une brève vue d’ensemble des fonctionnalités de XInput et explique comment la configurer dans une application. Il inclut les éléments suivants :

Présentation de XInput

Les applications peuvent utiliser l’API XInput pour communiquer avec les contrôleurs de jeu lorsqu’ils sont connectés à un PC Windows (jusqu’à quatre contrôleurs uniques peuvent être branchés à la fois).

À l’aide de cette API, tout contrôleur connecté compatible peut être interrogé pour son état et les effets de vibration peuvent être définis. Les contrôleurs qui ont le casque attaché peuvent également être interrogés pour les périphériques d’entrée et de sortie audio qui peuvent être utilisés avec le casque pour le traitement vocal.

Configuration du contrôleur

Les contrôleurs compatibles ont deux bâtons directionnels analogiques, chacun avec un bouton numérique, deux déclencheurs analogiques, un pavé directionnel numérique avec quatre directions et huit boutons numériques. Les états de chacune de ces entrées sont retournés dans la structure XINPUT_GAMEPAD lorsque la fonction XInputGetState est appelée.

Le contrôleur a également deux moteurs de vibration pour fournir des effets de retour de force à l’utilisateur. Les vitesses de ces moteurs sont spécifiées dans la structure XINPUT_VIBRATION passée à la fonction XInputSetState pour définir les effets de vibration.

Si vous le souhaitez, un casque peut être connecté au contrôleur. Le casque a un microphone pour l’entrée vocale et un casque pour la sortie sonore. Vous pouvez appeler la fonction XInputGetAudioDeviceIds ou XInputGetDSoundAudioDeviceGuids héritée pour obtenir les identificateurs d’appareil qui correspondent aux appareils pour le microphone et le casque. Vous pouvez ensuite utiliser les API Core Audio pour recevoir une entrée vocale et envoyer une sortie audio.

Utilisation de XInput

L’utilisation de XInput est aussi simple que l’appel des fonctions XInput selon les besoins. À l’aide des fonctions XInput, vous pouvez récupérer l’état du contrôleur, obtenir des ID audio du casque et définir des effets de rumble du contrôleur.

Plusieurs contrôleurs

L’API XInput prend en charge jusqu’à quatre contrôleurs connectés à tout moment. Les fonctions XInput nécessitent tous un paramètre dwUserIndex transmis pour identifier le contrôleur en cours de définition ou interrogé. Cet ID se trouve dans la plage de 0 à 3 et est défini automatiquement par XInput. Le nombre correspond au port auquel le contrôleur est connecté et n’est pas modifiable.

Chaque contrôleur affiche l’ID qu’il utilise en éclairant un quadrant sur l'« anneau de lumière » au centre du contrôleur. Une valeur dwUserIndex de 0 correspond au quadrant supérieur gauche ; la numérotation se poursuit autour de l’anneau dans l’ordre des aiguilles d’une montre.

Les applications doivent prendre en charge plusieurs contrôleurs.

Obtention de l’état du contrôleur

Pendant toute la durée d’une application, l’obtention de l’état à partir d’un contrôleur sera probablement effectuée le plus souvent. De frame à frame dans une application de jeu, l’état doit être récupéré et les informations de jeu mises à jour pour refléter les modifications du contrôleur.

Pour récupérer l’état, utilisez la fonction XInputGetState :

DWORD dwResult;    
for (DWORD i=0; i< XUSER_MAX_COUNT; i++ )
{
    XINPUT_STATE state;
    ZeroMemory( &state, sizeof(XINPUT_STATE) );

    // Simply get the state of the controller from XInput.
    dwResult = XInputGetState( i, &state );

    if( dwResult == ERROR_SUCCESS )
    {
        // Controller is connected
    }
    else
    {
        // Controller is not connected
    }
}

Notez que la valeur de retour de XInputGetState peut être utilisée pour déterminer si le contrôleur est connecté. Les applications doivent définir une structure pour contenir les informations du contrôleur interne ; ces informations doivent être comparées aux résultats de XInputGetState pour déterminer les modifications, telles que les pressions sur les boutons ou les deltas de contrôleur analogique, ont été apportées à ce cadre. Dans l’exemple ci-dessus, g_Controllers représente une telle structure.

Une fois que l’état a été récupéré dans une structure XINPUT_STATE , vous pouvez vérifier les modifications et obtenir des informations spécifiques sur l’état du contrôleur.

Le membre dwPacketNumber de la structure XINPUT_STATE peut être utilisé pour vérifier si l’état du contrôleur a changé depuis le dernier appel à XInputGetState. Si dwPacketNumber ne change pas entre deux appels séquentiels vers XInputGetState, il n’y a pas eu de modification de l’état. Si elle diffère, l’application doit vérifier le membre Gamepad de la structure XINPUT_STATE pour obtenir des informations d’état plus détaillées.

Pour des raisons de performances, n'appelez pas XInputGetState pour un emplacement utilisateur « vide » à chaque frame. Nous vous recommandons d’espacer les contrôles des nouveaux contrôleurs toutes les quelques secondes à la place.

Zone morte

Pour permettre aux utilisateurs d’avoir une expérience de jeu cohérente, votre jeu doit implémenter correctement la zone morte. La zone morte correspond aux valeurs de « mouvement » signalées par le contrôleur, même lorsque les joysticks analogiques ne sont pas touchés et sont parfaitement centrés. Il existe également une zone morte pour les 2 déclencheurs analogiques.

Remarque

Les jeux qui utilisent XInput qui ne filtrent pas la zone morte du tout connaîtront un jeu médiocre. Notez que certains contrôleurs sont plus sensibles que d’autres, la zone morte peut donc varier d’une unité à l’autre. Il est recommandé de tester vos jeux avec plusieurs contrôleurs différents sur différents systèmes.

Les applications doivent utiliser des « zones mortes » sur des entrées analogiques (déclencheurs, bâtons) pour indiquer quand un mouvement a été effectué suffisamment sur le stick ou le déclencheur pour être considéré comme valide.

Votre application doit rechercher les zones mortes et répondre de manière appropriée, comme dans cet exemple :

XINPUT_STATE state = g_Controllers[i].state;

float LX = state.Gamepad.sThumbLX;
float LY = state.Gamepad.sThumbLY;

//determine how far the controller is pushed
float magnitude = sqrt(LX*LX + LY*LY);

//determine the direction the controller is pushed
float normalizedLX = LX / magnitude;
float normalizedLY = LY / magnitude;

float normalizedMagnitude = 0;

//check if the controller is outside a circular dead zone
if (magnitude > INPUT_DEADZONE)
{
    //clip the magnitude at its expected maximum value
    if (magnitude > 32767) magnitude = 32767;

    //adjust magnitude relative to the end of the dead zone
    magnitude -= INPUT_DEADZONE;

    //optionally normalize the magnitude with respect to its expected range
    //giving a magnitude value of 0.0 to 1.0
    normalizedMagnitude = magnitude / (32767 - INPUT_DEADZONE);
}
else //if the controller is in the deadzone zero out the magnitude
{
    magnitude = 0.0;
    normalizedMagnitude = 0.0;
}

//repeat for right thumb stick

Cet exemple calcule le vecteur de direction du contrôleur et jusqu’à quel point le long du vecteur le contrôleur a été poussé. Cela permet de créer une zone morte circulaire en vérifiant simplement si l'intensité du contrôleur est supérieure à la valeur de la zone morte. En outre, le code normalise l’ampleur du contrôleur, qui peut ensuite être multipliée par un facteur spécifique au jeu pour convertir la position du contrôleur en unités pertinentes pour le jeu.

Notez que vous pouvez définir vos propres zones mortes pour les sticks et les déclencheurs (n’importe où entre 0 et 65534), ou vous pouvez utiliser les zones mortes fournies définies comme XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE et XINPUT_GAMEPAD_TRIGGER_THRESHOLD dans XInput.h :

#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE  7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD    30

Une fois que la zone morte est appliquée, vous pouvez trouver utile de mettre à l’échelle la plage à virgule flottante obtenue [0.0..1.0] (comme dans l’exemple ci-dessus), et éventuellement d’appliquer une transformation qui n'est pas linéaire.

Par exemple, avec des jeux de conduite, il peut être utile d'élever le résultat au cube pour améliorer la sensation de conduite des voitures à l'aide d'une manette de jeu. Élever le résultat au cube vous donne plus de précision dans les plages inférieures, ce qui est souhaitable. Les joueurs appliquent généralement une force douce pour obtenir un mouvement subtil ou une force dure dans une seule direction pour obtenir une réponse rapide.

Paramétrage des effets de vibration

En plus d’obtenir l’état du contrôleur, vous pouvez également envoyer des données de vibration au contrôleur pour modifier les commentaires fournis à l’utilisateur du contrôleur. Le contrôleur contient deux moteurs de rumble qui peuvent être contrôlés indépendamment en passant des valeurs à la fonction XInputSetState .

La vitesse de chaque moteur peut être spécifiée à l’aide d’une valeur WORD dans la structure XINPUT_VIBRATION passée à la fonction XInputSetState comme suit :

XINPUT_VIBRATION vibration;
ZeroMemory( &vibration, sizeof(XINPUT_VIBRATION) );
vibration.wLeftMotorSpeed = 32000; // use any value between 0-65535 here
vibration.wRightMotorSpeed = 16000; // use any value between 0-65535 here
XInputSetState( i, &vibration );

Notez que le moteur droit est le moteur à haute fréquence, le moteur gauche est le moteur à basse fréquence. Ils n’ont pas toujours besoin d’être définis sur la même quantité, car ils fournissent des effets différents.

Obtention d’identificateurs d’appareil audio

Le casque d’un contrôleur a les fonctions suivantes :

  • Enregistrer le son à l’aide d’un microphone
  • Écouter du son avec un casque

Utilisez ce code pour obtenir les identificateurs d’appareil pour le casque :

WCHAR renderId[ 256 ] = {0};
WCHAR captureId[ 256 ] = {0};
UINT rcount = 256;
UINT ccount = 256;

XInputGetAudioDeviceIds( i, renderId, &rcount, captureId, &ccount );

Après avoir obtenu les identificateurs d’appareil, vous pouvez créer les interfaces appropriées. Par exemple, si vous utilisez XAudio 2.8, utilisez ce code pour créer une voix de mastering pour cet appareil :

IXAudio2* pXAudio2 = NULL;
HRESULT hr;
if ( FAILED(hr = XAudio2Create( &pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR ) ) )
    return hr;

IXAudio2MasteringVoice* pMasterVoice = NULL;
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, renderId, NULL, AudioCategory_Communications ) ) )
    return hr;

Pour plus d’informations sur l’utilisation de l’identificateur d’appareil captureId, consultez Capture d’un flux.

Obtention des GUID DirectSound (ancien SDK DirectX uniquement)

Le casque qui peut être connecté à un contrôleur a deux fonctions : il peut enregistrer le son à l’aide d’un microphone, et il peut lire le son à l’aide d’un casque. Dans l’API XInput, ces fonctions sont effectuées via DirectSound, à l’aide des interfaces IDirectSound8 et IDirectSoundCapture8 .

Pour associer le microphone et le casque à leurs interfaces DirectSound appropriées, vous devez obtenir les DirectSoundGUIDs pour les appareils de capture et de rendu en appelant XInputGetDSoundAudioDeviceGuids.

Remarque

L’utilisation de DirectSound hérité n’est pas recommandée et n’est pas disponible dans les applications du Windows Store. Les informations de cette section s’appliquent uniquement à la version du Kit de développement logiciel (SDK) DirectX de XInput (XInput 1.3). La version Windows 8 de XInput (XInput 1.4) utilise exclusivement des identificateurs d’appareil WASAPI (Windows Audio Session API) obtenus via XInputGetAudioDeviceIds.

XInputGetDSoundAudioDeviceGuids( i, &dsRenderGuid, &dsCaptureGuid );

Une fois que vous avez récupéré les GUID, vous pouvez créer les interfaces appropriées en appelant DirectSoundCreate8 et DirectSoundCaptureCreate8 comme suit :

// Create IDirectSound8 using the controller's render device
if( FAILED( hr = DirectSoundCreate8( &dsRenderGuid, &pDS, NULL ) ) )
   return hr;

// Set coop level to DSSCL_PRIORITY
if( FAILED( hr = pDS->SetCooperativeLevel( hWnd, DSSCL_NORMAL ) ) )
   return hr;

// Create IDirectSoundCapture using the controller's capture device
if( FAILED( hr = DirectSoundCaptureCreate8( &dsCaptureGuid, &pDSCapture, NULL ) ) )
   return hr;

de référence de programmation