Introduzione a XInput nelle applicazioni Windows

XInput consente alle applicazioni Windows di elaborare le interazioni del controller (inclusi gli effetti del controller e l'input vocale e l'output).

Questo argomento fornisce una breve panoramica delle funzionalità di XInput e di come configurarla in un'applicazione. Include quanto segue:

Introduzione a XInput

Le applicazioni possono usare l'API XInput per comunicare con i controller di gioco quando sono collegati a un PC Windows (fino a quattro controller univoci possono essere collegati alla volta).

Usando questa API, è possibile eseguire query su qualsiasi controller connesso compatibile per il relativo stato ed è possibile impostare effetti di vibrazione. I controller che hanno il visore VR collegato possono anche essere sottoposti a query per i dispositivi di input audio e output che possono essere usati con l'auricolare per l'elaborazione vocale.

Controller Layout

I controller compatibili hanno due levette direzionali analogiche, ognuna con un pulsante digitale, due trigger analogici, un riquadro direzionale digitale con quattro direzioni e otto pulsanti digitali. Gli stati di ognuno di questi input vengono restituiti nella struttura XINPUT_GAMEPAD quando viene chiamata la funzione XInputGetState.

Il controller ha anche due motori di vibrazione per fornire effetti di forza feedback all'utente. Le velocità di questi motori vengono specificate nella struttura XINPUT_VIBRATION passata alla funzione XInputSetState per impostare gli effetti di vibrazione.

Facoltativamente, un visore VR può essere collegato al controller. L'auricolare ha un microfono per l'input vocale e una cuffie per l'output audio. Puoi chiamare la funzione XInputGetAudioDeviceIds o XInputGetDSoundAudioGuids legacy per ottenere gli identificatori del dispositivo che corrispondono ai dispositivi per il microfono e le cuffie. È quindi possibile usare le API audio principali per ricevere l'input vocale e inviare l'output audio.

Uso di XInput

L'uso di XInput è semplice come chiamare le funzioni XInput come richiesto. Usando le funzioni XInput, è possibile recuperare lo stato del controller, ottenere gli ID audio del visore VR e impostare effetti rumble del controller.

Più controller

L'API XInput supporta fino a quattro controller connessi in qualsiasi momento. Le funzioni XInput richiedono tutti un parametro dwUserIndex passato per identificare il controller impostato o sottoposto a query. Questo ID sarà compreso nell'intervallo di 0-3 e viene impostato automaticamente da XInput. Il numero corrisponde alla porta collegata al controller e non modificabile.

Ogni controller visualizza l'ID usato illuminando un quadrante sull'anello di luce al centro del controller. Un valore dwUserIndex pari a 0 corrisponde al quadrante superiore sinistro. La numerazione procede intorno all'anello in senso orario.

Le applicazioni devono supportare più controller.

Recupero dello stato del controller

Per tutta la durata di un'applicazione, il recupero dello stato da un controller verrà probabilmente eseguito più spesso. Dal frame al frame in un'applicazione di gioco, lo stato deve essere recuperato e le informazioni sul gioco devono essere aggiornate per riflettere le modifiche del controller.

Per recuperare lo stato, usare la funzione 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
    }
}

Si noti che il valore restituito di XInputGetState può essere usato per determinare se il controller è connesso. Le applicazioni devono definire una struttura per contenere informazioni sul controller interno; queste informazioni devono essere confrontate con i risultati di XInputGetState per determinare quali modifiche, ad esempio la pressione dei pulsanti o i delta del controller analogico, sono stati apportati a tale frame. Nell'esempio precedente g_Controllers rappresenta tale struttura.

Dopo che lo stato è stato recuperato in una struttura di XINPUT_STATE , è possibile verificarne la presenza e ottenere informazioni specifiche sullo stato del controller.

Il membro dwPacketNumber della struttura XINPUT_STATE può essere utilizzato per verificare se lo stato del controller è cambiato dall'ultima chiamata a XInputGetState. Se dwPacketNumber non cambia tra due chiamate sequenziali a XInputGetState, non è stata apportata alcuna modifica allo stato. In caso contrario, l'applicazione deve controllare il membro Game pad della struttura XINPUT_STATE per ottenere informazioni più dettagliate sullo stato.

Per motivi di prestazioni, non chiamare XInputGetState per uno slot utente "vuoto" ogni fotogramma. È consigliabile spaziarne ogni pochi secondi per verificare la presenza di nuovi controller.

Zona morta

Per consentire agli utenti di avere un'esperienza di gioco coerente, il gioco deve implementare correttamente la zona morta. La zona morta è "movimento" valori segnalati dal controller anche quando le levette analogiche non sono toccate e centrate. C'è anche una zona morta per i 2 trigger analogici.

Nota

I giochi che usano XInput che non filtrano affatto la zona morta avranno un'esperienza di gioco scarsa. Si noti che alcuni controller sono più sensibili rispetto ad altri, pertanto la zona morta può variare da unità a unità. È consigliabile testare i giochi con diversi controller in sistemi diversi.

Le applicazioni devono usare "zone morte" sugli input analogici (trigger, levette) per indicare quando un movimento è stato fatto sufficientemente sul bastone o sul trigger da considerare valido.

L'applicazione deve verificare la presenza di zone non recapitabili e rispondere in modo appopriato, come in questo esempio:

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

In questo esempio viene calcolato il vettore di direzione del controller e la distanza del vettore di cui è stato eseguito il push del controller. In questo modo è possibile applicare una zona morta circolare controllando semplicemente se la grandezza del controller è maggiore del valore del deadzone. Inoltre, il codice normalizza la grandezza del controller, che può quindi essere moltiplicata per un fattore specifico del gioco per convertire la posizione del controller in unità rilevanti per il gioco.

Si noti che è possibile definire zone non recapitabili per le levette e i trigger (da 0 a 65534) oppure usare le zone morte specificate definite come XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE e XINPUT_GAMEPAD_TRIGGER_THRESHOLD in XInput.h:

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

Dopo aver applicato la zona morta, può risultare utile ridimensionare l'intervallo risultante [0.0...1.0] a virgola mobile (come nell'esempio precedente) e, facoltativamente, applicare una trasformazione non lineare.

Ad esempio, con i giochi di guida, può essere utile cubiare il risultato per offrire un'atmosfera migliore alla guida delle auto usando un game pad, poiché il risultato offre una maggiore precisione negli intervalli inferiori, che è auspicabile, poiché i giocatori in genere applicano forza morbida per ottenere un movimento sottile o applicare forza dura in un'unica direzione per ottenere la risposta rd.

Impostazione degli effetti vibrazioni

Oltre a ottenere lo stato del controller, è anche possibile inviare dati sulle vibrazioni al titolare del trattamento per modificare il feedback fornito all'utente del controller. Il controller contiene due motori rombo che possono essere controllati in modo indipendente passando valori alla funzione XInputSetState.

La velocità di ogni motore può essere specificata utilizzando un valore WORD nella struttura XINPUT_VIBRATION passata alla funzione XInputSetState come indicato di seguito:

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 );

Si noti che il motore destro è il motore ad alta frequenza, il motore sinistro è il motore a bassa frequenza. Non devono sempre essere impostati sulla stessa quantità, in quanto forniscono effetti diversi.

Recupero di identificatori di dispositivo audio

Il visore VR per un controller ha queste funzioni:

  • Registrare il suono usando un microfono
  • Riprodurre il suono usando una cuffie

Usare questo codice per ottenere gli identificatori del dispositivo per il visore VR:

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

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

Dopo aver ottenuto gli identificatori del dispositivo, è possibile creare le interfacce appropriate. Ad esempio, se si usa XAudio 2.8, usare questo codice per creare una voce mastering per questo dispositivo:

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;

Per informazioni su come usare l'identificatore del dispositivo captureId, vedi Acquisizione di un flusso.

Recupero di GUID DirectSound (solo DirectX SDK legacy)

Il visore VR che può essere collegato a un controller ha due funzioni: può registrare il suono usando un microfono e può riprodurre il suono usando una cuffie. Nell'API XInput queste funzioni vengono eseguite tramite DirectSound, usando le interfacce IDirectSound8 e IDirectSoundCapture8.

Per associare il microfono e le cuffie dell'auricolare alle interfacce DirectSound appropriate , devi ottenere i DirectSoundGUIDs per l'acquisizione e il rendering dei dispositivi chiamando XInputGetDSoundAudioDeviceGuids.

Nota

L'uso di DirectSound legacy non è consigliato e non è disponibile nelle app di Windows Store. Le informazioni contenute in questa sezione si applicano solo alla versione DirectX SDK di XInput (XInput 1.3). La versione windows 8 di XInput (XInput 1.4) usa esclusivamente identificatori di dispositivo WINDOWS Audio Session API (WASAPI) ottenuti tramite XInputGetAudioDeviceIds.

XInputGetDSoundAudioDeviceGuids( i, &dsRenderGuid, &dsCaptureGuid );

Dopo aver recuperato i GUID, è possibile creare le interfacce appropriate chiamando DirectSoundCreate8 e DirectSoundCaptureCreate8 come segue:

// 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;

Informazioni di riferimento sulla programmazione