Erste Schritte mit XInput in Windows-Anwendungen

XInput ermöglicht Windows Anwendungen, Xbox Controller-Interaktionen (einschließlich Controller-Rumbleeffekte und Spracheingabe und -ausgabe) zu verarbeiten.

Dieses Thema enthält eine kurze Übersicht über die Funktionen von XInput und die Einrichtung in einer Anwendung. Dies umfasst Folgendes:

Einführung in XInput

Die Xbox-Konsole verwendet einen Gaming-Controller, der mit Windows kompatibel ist. Anwendungen können die XInput-API verwenden, um mit diesen Controllern zu kommunizieren, wenn sie an einem Windows PC angeschlossen sind (bis zu vier eindeutige Controller können gleichzeitig angeschlossen werden).

Mithilfe dieser API können alle verbundenen Xbox Controller für ihren Zustand abgefragt werden, und Vibrationseffekte können festgelegt werden. Controller, die das Headset angeschlossen haben, können auch nach Soundeingabe- und Ausgabegeräten abgefragt werden, die mit dem Headset für die Sprachverarbeitung verwendet werden können.

Der Xbox-Controller

Der Xbox Controller verfügt über zwei analoge direktionale Sticks, jeweils mit einer digitalen Taste, zwei analoge Trigger, ein digitales Richtungskreuz mit vier Richtungen und acht digitale Tasten. Die Zustände jeder dieser Eingaben werden in der XINPUT_GAMEPAD Struktur zurückgegeben, wenn die XInputGetState-Funktion aufgerufen wird.

Der Controller verfügt auch über zwei Vibrationsmotoren, um dem Benutzer Kraftfeedbackeffekte zu liefern. Die Geschwindigkeiten dieser Motoren werden in der XINPUT_VIBRATION Struktur angegeben, die an die XInputSetState-Funktion übergeben wird, um Vibrationseffekte festzulegen.

Optional kann ein Headset mit dem Controller verbunden werden. Das Headset verfügt über ein Mikrofon für die Spracheingabe und einen Kopfhörer für die Soundausgabe. Sie können die XInputGetAudioDeviceIds oder legacy XInputGetDSoundAudioDeviceGuids-Funktion aufrufen, um die Gerätebezeichner abzurufen, die den Geräten für das Mikrofon und kopfhörer entsprechen. Anschließend können Sie die Core Audio-APIs verwenden, um Spracheingaben zu empfangen und Soundausgabe zu senden.

Verwenden von XInput

Die Verwendung von XInput ist so einfach wie das Aufrufen der XInput-Funktionen wie erforderlich. Mithilfe der XInput-Funktionen können Sie den Controllerstatus abrufen, Headset-Audio-IDs abrufen und Controller-Rumbleeffekte festlegen.

Mehrere Controller

Die XInput-API unterstützt bis zu vier Controller, die jederzeit verbunden sind. Die XInput-Funktionen erfordern alle einen dwUserIndex-Parameter , der übergeben wird, um den festgelegten oder abgefragten Controller zu identifizieren. Diese ID befindet sich im Bereich von 0-3 und wird automatisch von XInput festgelegt. Die Zahl entspricht dem Port, an den der Controller angeschlossen ist und nicht geändert werden kann.

Jeder Controller zeigt an, welche ID er verwendet, indem er einen Quadranten auf dem "Ring des Lichts" in der Mitte des Controllers anzeigt. Ein dwUserIndex-Wert von 0 entspricht dem oberen linken Quadranten; die Nummerierung geht um den Ring im Uhrzeigersinn fort.

Anwendungen sollten mehrere Controller unterstützen.

Abrufen des Controllerstatus

Während der gesamten Dauer einer Anwendung wird der Status eines Controllers wahrscheinlich am häufigsten ausgeführt. Von Frame zu Frame in einer Spielanwendung sollte der Zustand abgerufen und Spielinformationen aktualisiert werden, um die Controlleränderungen widerzuspiegeln.

Verwenden Sie zum Abrufen des Zustands die Funktion 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
    }
}

Beachten Sie, dass der Rückgabewert von XInputGetState verwendet werden kann, um festzustellen, ob der Controller angeschlossen ist. Anwendungen sollten eine Struktur definieren, die interne Controllerinformationen enthält; Diese Informationen sollten mit den Ergebnissen von XInputGetState verglichen werden, um zu bestimmen, welche Änderungen wie Tastendrücke oder analoge Controllerdeltas vorgenommen wurden. Im obigen Beispiel stellt g_Controllers eine solche Struktur dar.

Nachdem der Zustand in einer XINPUT_STATE Struktur abgerufen wurde, können Sie ihn nach Änderungen überprüfen und spezifische Informationen zum Controllerstatus abrufen.

Das dwPacketNumber-Element der XINPUT_STATE Struktur kann verwendet werden, um zu überprüfen, ob sich der Zustand des Controllers seit dem letzten Aufruf von XInputGetState geändert hat. Wenn dwPacketNumber zwischen zwei sequenziellen Aufrufen von XInputGetState nicht geändert wird, wurde der Zustand nicht geändert. Wenn es sich unterscheidet, sollte die Anwendung das Gamepad-Element der XINPUT_STATE Struktur überprüfen, um detailliertere Zustandsinformationen zu erhalten.

Aus Leistungsgründen rufen Sie XInputGetState nicht für einen leeren Benutzerplatz für jeden Frame auf. Es wird empfohlen, alle paar Sekunden nach neuen Controllern zu suchen.

Tote Zone

Damit Benutzer über eine einheitliche Spielerfahrung verfügen, muss Ihr Spiel die Dead Zone richtig implementieren. Die tote Zone ist "Bewegungswerte", die vom Controller gemeldet werden, auch wenn die analogen Daumensticks unberührt und zentriert sind. Es gibt auch eine tote Zone für die 2 analogen Trigger.

Hinweis

Spiele, die XInput verwenden, die überhaupt keine tote Zone filtern, werden schlechtes Spielerlebnis haben. Bitte beachten Sie, dass einige Controller sensibler sind als andere, sodass die tote Zone von Einheit zu Einheit variieren kann. Es wird empfohlen, Ihre Spiele mit mehreren Xbox-Controllern auf verschiedenen Systemen zu testen.

Anwendungen sollten "tote Zonen" für analoge Eingaben (Trigger, Sticks) verwenden, um anzugeben, wann eine Bewegung ausreichend auf dem Stick vorgenommen wurde oder als gültig angesehen wird.

Ihre Anwendung sollte nach toten Zonen suchen und appopriate reagieren, wie in diesem Beispiel:

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 diesem Beispiel wird der Richtungsvektor des Controllers berechnet und wie weit der Vektor des Controllers verschoben wurde. Dies ermöglicht die Erzwingung einer kreisförmigen Totzone, indem Sie einfach überprüfen, ob die Größe des Controllers größer als der Deadzone-Wert ist. Darüber hinaus normalisiert der Code die Größe des Controllers, die dann durch einen spielspezifischen Faktor multipliziert werden kann, um die Position des Controllers in Einheiten zu konvertieren, die für das Spiel relevant sind.

Beachten Sie, dass Sie ihre eigenen toten Zonen für die Sticks und Trigger (überall von 0-65534) definieren können, oder Sie können die bereitgestellten Totzonen verwenden, die als XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE und XINPUT_GAMEPAD_TRIGGER_THRESHOLD in XInput.h definiert sind:

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

Sobald die Totzone erzwungen wurde, können Sie den resultierenden Bereich [0.0...1.0] Gleitkommapunkt (wie im obigen Beispiel) skalieren und optional eine nicht lineare Transformation anwenden.

Mit Fahrspielen kann es beispielsweise hilfreich sein, das Ergebnis zu würfeln, um ein besseres Gefühl für das Fahren der Autos mit einem Gamepad zu bieten, da das Ergebnis ihnen mehr Präzision in den unteren Bereichen bietet, was wünschenswert ist, da Gamer normalerweise weiche Kraft anwenden, um subtile Bewegung zu erhalten oder harte Kraft in einer Richtung anzuwenden, um rd-Antwort zu erhalten.

Festlegen von Vibrationseffekten

Zusätzlich zum Abrufen des Zustands des Controllers können Sie auch Vibrationsdaten an den Controller senden, um das vom Benutzer des Controllers bereitgestellte Feedback zu ändern. Der Controller enthält zwei Rumblemotoren, die unabhängig gesteuert werden können, indem Werte an die XInputSetState-Funktion übergeben werden.

Die Geschwindigkeit jedes Motors kann mithilfe eines WORD-Werts in der XINPUT_VIBRATION Struktur angegeben werden, die wie folgt an die XInputSetState-Funktion übergeben wird:

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

Beachten Sie, dass der rechte Motor der Hochfrequenzmotor ist, der linke Motor ist der Niederfrequenzmotor. Sie müssen nicht immer auf dieselbe Menge festgelegt werden, wie sie verschiedene Effekte bieten.

Abrufen von Audiogerätebezeichnern

Das Headset für einen Xbox Controller verfügt über folgende Funktionen:

  • Aufzeichnen von Sound mithilfe eines Mikrofons
  • Wiedergeben von Sound mithilfe eines Kopfhörers

Verwenden Sie diesen Code, um die Gerätebezeichner für das Headset abzurufen:

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

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

Nachdem Sie die Gerätebezeichner erhalten haben, können Sie die entsprechenden Schnittstellen erstellen. Wenn Sie beispielsweise XAudio 2.8 verwenden, verwenden Sie diesen Code, um eine Masterstimme für dieses Gerät zu erstellen:

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;

Informationen zur Verwendung des CaptureId-Gerätebezeichners finden Sie unter Erfassen eines Datenstroms.

Abrufen von DirectSound-GUIDs (nur legacy DirectX SDK)

Das Headset, das mit einem Xbox-Controller verbunden werden kann, verfügt über zwei Funktionen: Er kann Sound mit einem Mikrofon aufzeichnen, und es kann mit einem Kopfhörer Zurück-Sound wiedergeben. In der XInput-API werden diese Funktionen über DirectSound erreicht, wobei die IDirectSound8 - und IDirectSoundCapture8-Schnittstellen verwendet werden.

Um das Headset-Mikrofon und den Kopfhörer mit ihren entsprechenden DirectSound-Schnittstellen zu verknüpfen, müssen Sie die DirectSoundGUIDs für die Aufnahme- und Rendergeräte abrufen, indem Sie XInputGetDSoundAudioDeviceGuids aufrufen.

Hinweis

Die Verwendung des Legacy-DirectSound wird nicht empfohlen und ist in Windows Store Apps nicht verfügbar. Die Informationen in diesem Abschnitt gelten nur für die DirectX SDK-Version von XInput (XInput 1.3). Die Windows 8 Version von XInput (XInput 1.4) verwendet ausschließlich Windows AUDIO Session API (WASAPI)-Gerätebezeichner, die über XInputGetAudioDeviceIds abgerufen werden.

XInputGetDSoundAudioDeviceGuids( i, &dsRenderGuid, &dsCaptureGuid );

Nachdem Sie die GUIDs abgerufen haben, können Sie die entsprechenden Schnittstellen erstellen, indem Sie DirectSoundCreate8 und DirectSoundCaptureCreate8 wie folgt aufrufen:

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

Programmierverzeichnis