Bien démarrer avec XInput dans les applications Windows

XInput permet Windows applications de traiter les interactions du contrôleur Xbox (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. Les éléments suivants sont abordés :

Présentation de XInput

La console Xbox utilise une manette de jeu compatible avec Windows. Les applications peuvent utiliser l’API XInput pour communiquer avec ces contrôleurs lorsqu’elles sont connectées à un PC Windows (jusqu’à quatre contrôleurs uniques peuvent être branchés à la fois).

À l’aide de cette API, n’importe quelle manette Xbox connectée peut être interrogée 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 sonores qui peuvent être utilisés avec le casque pour le traitement vocal.

Manette Xbox

La manette Xbox a 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 transmise à la fonction XInputSetState pour définir des effets de vibration.

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

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 les ID audio du casque et définir les 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 passé pour identifier le contrôleur défini 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 branché 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 l’image à la trame 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 quelles 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 de XINPUT_STATE , vous pouvez le vérifier pour connaître 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 à XInputGetState, il n’y a pas eu de changement d’état. Si elle diffère, l’application doit vérifier le membre du boîtier de commande 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 image. Nous vous recommandons d’espacer les vérifications des nouveaux contrôleurs toutes les quelques secondes à la place.

Zone morte

Pour que les utilisateurs aient une expérience de jeu cohérente, votre jeu doit implémenter correctement la zone morte. La zone morte est des valeurs de « mouvement » signalées par le contrôleur, même lorsque les sticks analogiques sont intouchés et centrés. Il existe également une zone morte pour les 2 déclencheurs analogiques.

Notes

Les jeux qui utilisent XInput qui ne filtrent pas de zone morte du tout vont subir 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 Xbox 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é suffisamment effectué sur le stick ou le déclencheur pour être considéré comme valide.

Votre application doit vérifier les zones mortes et répondre appopriatement, 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 la distance entre le vecteur que le contrôleur a été poussé. Cela permet l’application d’une zone morte circulaire en vérifiant simplement si l’ampleur du contrôleur est supérieure à la valeur de 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 la zone morte appliquée, vous pouvez trouver utile de mettre à l’échelle la plage résultante [0.0..1.0] à virgule flottante (comme dans l’exemple ci-dessus), et éventuellement d’appliquer une transformation non linéaire.

Par exemple, avec les jeux de conduite, il peut être utile de cuber le résultat pour fournir une meilleure sensation de conduire les voitures à l’aide d’un boîtier de commande, car cubing le résultat vous donne plus de précision dans les plages inférieures, ce qui est souhaitable, car les joueurs appliquent généralement la force douce pour obtenir un mouvement subtil ou appliquer une force dure tout le chemin dans une direction pour obtenir la réponse rd.

Définition 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 des identificateurs d’appareil audio

Le casque d’une manette Xbox a les fonctions suivantes :

  • Enregistrer le son à l’aide d’un microphone
  • Lire le son à l’aide d’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 (SDK DirectX hérité uniquement)

Le casque qui peut être connecté à une manette Xbox 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 au casque à leurs interfaces DirectSound appropriées, vous devez obtenir les DirectSoundGUIDs pour les appareils de capture et de rendu en appelant XInputGetDSoundAudioDeviceGuids.

Notes

L’utilisation de DirectSound hérité n’est pas recommandée et n’est pas disponible dans Windows 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 Windows identificateurs d’appareil WASAPI (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;

Guide de référence de programmation