Condividi tramite


Introduzione a XInput nelle applicazioni Windows

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

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 l'auricolare collegato possono anche essere utilizzati per richiedere informazioni sui dispositivi audio di input e output che possono essere usati con l'auricolare per l'elaborazione vocale.

Disposizione del Controller

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 funzioneXInputSetStateper 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 legacy XInputGetDSoundAudioDeviceGuids per ottenere gli identificatori dei dispositivi che corrispondono ai dispositivi per il microfono e la cuffia. È quindi possibile usare le API audio core per ricevere l'input vocale e inviare l'output audio.

Uso di XInput

L'uso di XInput è semplice come chiamare le funzioni XInput quando necessario. 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 tutte un parametro dwUserIndex passato per identificare il controller da impostare o da interrogare. 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 in alto a sinistra; 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 le pressioni dei pulsanti o i delta del controller analogico, sono state apportate a tale frame. Nell'esempio precedente g_Controllers rappresenta una struttura di questo tipo.

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 dwPacketNumber membro della struttura XINPUT_STATE può essere utilizzato per verificare se lo stato del controller è stato modificato dall'ultima chiamata a XInputGetState. Se dwPacketNumber non cambia tra due chiamate sequenziali a XInputGetState, non è stato apportato alcun cambiamento di stato. Se è diverso, l'applicazione deve controllare il membro gamepad della struttura XINPUT_STATE per ottenere informazioni più dettagliate sullo stato attuale.

Per motivi di prestazioni, non chiamare XInputGetState per uno slot utente vuoto a ogni frame. Consigliamo invece di effettuare controlli per nuovi controller ogni pochi secondi.

Zona morta

Per consentire agli utenti di avere un'esperienza di gioco coerente, il gioco deve implementare correttamente la zona morta. La zona morta è i valori di "movimento" segnalati dal controller anche quando le levette analogiche sono centrate e non toccate. 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 morte e rispondere in modo appropriato, 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 quanto il controller è stato spostato lungo il vettore. In questo modo è possibile applicare una zona morta circolare semplicemente verificando se la grandezza del controller è maggiore del valore della zona morta. 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 può definire le proprie zone morte per le levette e i trigger (da 0 a 65534), oppure usare le zone morte specificate 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 scalare l'intervallo risultante [0.0..1.0] nel formato a virgola mobile (come nell'esempio precedente) e, facoltativamente, applicare una trasformazione non lineare.

Ad esempio, con i giochi di guida, può essere utile elevare al cubo il risultato per offrire maggiore precisione nella guida delle auto usando un gamepad, poiché elevare al cubo il risultato dà una maggiore precisione nelle gamme inferiori, che è auspicabile, poiché i giocatori in genere applicano una forza leggera per ottenere un movimento sottile o applicano una forza intensa in un'unica direzione per ottenere una risposta rapida.

Impostazione degli effetti vibrazioni

Oltre a ottenere lo stato del controller, è anche possibile inviare dati sulle vibrazioni al controller per modificare il feedback fornito all'utente. Il controller contiene due motori a vibrazione 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 che viene passata alla funzione XInputSetState:

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.

Ottenere 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 principale 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 utilizzare l'identificatore del dispositivo captureId, vedere Acquisizione di un flusso.

Recupero dei GUID di 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, è necessario ottenere i DirectSoundGUIDs per i dispositivi di acquisizione e rendering chiamando XInputGetDSoundAudioDeviceGuids.

Nota

L'uso della legacy DirectSound 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;

Riferimento alla programmazione