消費者入門 在 Windows 應用程式中搭配 XInput

XInput 可讓Windows應用程式處理 Xbox 控制器互動, (包括控制器雜音效果和語音輸入和輸出) 。

本主題提供 XInput 功能的簡短概觀,以及如何在應用程式中設定。 其中包含下列專案:

XInput 簡介

Xbox 主控台使用與Windows相容的遊戲控制器。 當應用程式插入Windows電腦時,應用程式可以使用 XInput API 與這些控制器通訊, (最多可以一次插入四個唯一控制器) 。

使用此 API,可以查詢任何連線的 Xbox 控制器狀態,並設定震動效果。 附加頭戴式裝置的控制器也可以查詢聲音輸入和輸出裝置,這些裝置可以搭配頭戴式裝置進行語音處理。

Xbox 控制器

Xbox 控制器有兩個類比方向杆,分別具有數位按鈕、兩個類比觸發程式、具有四個方向的數位方向板,以及八個數字按鈕。 呼叫XInputGetState函式時,每個輸入的狀態都會在XINPUT_GAMEPAD結構中傳回。

控制器也有兩個震動馬達,可向使用者提供強制回饋效果。 這些馬達的速度是在傳遞至XInputSetState函式的XINPUT_VIBRATION結構中指定,以設定震動效果。

或者,頭戴式裝置可以連線到控制器。 頭戴式裝置具有語音輸入的麥克風,以及聲音輸出的耳機。 您可以呼叫 XInputGetAudioDeviceIds 或舊版 XInputGetDSoundAudioDeviceGuids 函式,以取得對應至麥克風和耳機裝置的裝置識別碼。 然後,您可以使用 核心音訊 API 來接收語音輸入和傳送聲音輸出。

使用 XInput

使用 XInput 就像視需要呼叫 XInput 函式一樣簡單。 使用 XInput 函式,您可以擷取控制器狀態、取得頭戴式裝置音訊識別碼,以及設定控制器無聲效果。

多個控制器

XInput API 隨時支援最多四個連線的控制器。 XInput 函式都需要傳入 的 dwUserIndex 參數,以識別要設定或查詢的控制器。 此識別碼會位於 0-3 的範圍內,且由 XInput 自動設定。 數位會對應到控制器插入的埠,而且無法修改。

每個控制器都會顯示它所使用的識別碼,方法是在控制器中央的「光環」上啟動象限。 dwUserIndex值為 0 對應至左上方象限;編號會依順時針順序在環形周圍繼續。

應用程式應該支援多個控制器。

取得控制器狀態

在應用程式的持續時間內,從控制器取得狀態可能最常完成。 從畫面到遊戲應用程式中的框架,應該擷取狀態,並更新遊戲資訊以反映控制器變更。

若要擷取狀態,請使用 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
    }
}

請注意, XInputGetState 的傳回值可用來判斷控制器是否已連線。 應用程式應該定義結構來保存內部控制器資訊;這項資訊應該與 XInputGetState 的結果進行比較,以判斷哪些變更,例如按鈕按下或類比控制器差異。 在上述範例中, g_Controllers 表示這類結構。

XINPUT_STATE 結構中擷取狀態之後,您可以檢查狀態是否有變更,並取得控制器狀態的特定資訊。

XINPUT_STATE結構的dwPacketNumber成員可用來檢查控制器的狀態是否自上次呼叫XInputGetState之後變更。 如果 dwPacketNumberXInputGetState的兩個循序呼叫之間沒有變更,則狀態沒有任何變更。 如果不同,則應用程式應該檢查XINPUT_STATE結構的Gamepad成員,以取得更詳細的狀態資訊。

基於效能考慮,請勿針對每個畫面的「空白」使用者位置呼叫 XInputGetState 。 建議您每隔幾秒就清除檢查新控制器的空間。

死區

為了讓使用者擁有一致的遊戲體驗,您的遊戲必須正確實作死區。 死區是控制器所報告的「移動」值,即使類比搖桿未觸控並置中也一樣。 另外還有 2 個類比觸發程式的死區。

注意

使用 XInput 的遊戲完全不會篩選死區,將會遇到不佳的遊戲。 請注意,某些控制器比其他控制器更敏感,因此死區可能會因單位而異。 建議您在不同的系統上,使用數個 Xbox 控制器測試遊戲。

應用程式應該在類比輸入上使用「死區」, (觸發程式、黏杆) ,以指出移動在杆或觸發程式上是否足以視為有效。

您的應用程式應該檢查死區,並立即回應,如下列範例所示:

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

本範例會計算控制器的方向向量,以及控制器所推送的向量距離。 這允許藉由檢查控制器的大小是否大於死區值,來強制執行迴圈死區。 此外,程式碼也會正規化控制器的大小,然後乘以遊戲特定因素,將控制器的位置轉換為與遊戲相關的單位。

請注意,您可能會為杆和觸發程式定義自己的死區, (從 0-65534) 到任何地方,或者您可以使用定義為XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE、XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE和 XInput.h 中XINPUT_GAMEPAD_TRIGGER_THRESHOLD提供的死區:

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

強制執行死區之後,您可能會發現調整產生的範圍 [0.0..1.0] 浮點 (,如上述範例所示) ,並選擇性地套用非線性轉換。

例如,使用駕駛遊戲時,結果可能會很有説明,讓結果更適合使用遊戲台來駕駛汽車,因為結果會在較低的範圍內提供更精確的精確度,因為玩家通常會套用軟力來取得細微的移動,或透過單向套用硬式強制以取得 rd 回應。

設定震動效果

除了取得控制器的狀態之外,您也可以將震動資料傳送至控制器,以改變提供給控制器使用者的意見反應。 控制器包含兩個可獨立控制的浮點馬達,方法是將值傳遞至 XInputSetState 函式。

每個馬達的速度都可以在傳遞至XInputSetState函式的XINPUT_VIBRATION結構中使用 WORD 值來指定,如下所示:

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

請注意,右馬達是高頻率馬達,左馬達是低頻率馬達。 它們不一定會設定為相同的數量,因為它們提供不同的效果。

取得音訊裝置識別碼

Xbox 控制器的頭戴式裝置具有下列功能:

  • 使用麥克風錄製音效
  • 使用耳機播放音效

使用此程式碼來取得頭戴式裝置的裝置識別碼:

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

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

取得裝置識別碼之後,您可以建立適當的介面。 例如,如果您使用 XAudio 2.8,請使用此程式碼來為此裝置建立主控語音:

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;

如需如何使用 captureId 裝置識別碼的詳細資訊,請參閱 擷取資料流程

僅取得舊版 DirectX SDK (DirectSound GUID)

可以連線到 Xbox 控制器的頭戴式裝置有兩個功能:它可以使用麥克風錄製音效,而且可以使用耳機播放音效。 在 XInput API 中,這些函式是透過 DirectSound來完成,使用 IDirectSound8IDirectSoundCapture8 介面。

若要將頭戴式裝置麥克風和耳機與其適當的 DirectSound 介面產生關聯,您必須藉由呼叫 XInputGetDSoundAudioDeviceGuids來取得擷取和轉譯裝置的 DirectSoundGUID。

注意

不建議使用舊版DirectSound,而且無法在Windows Microsoft Store應用程式中使用。 本節中的資訊僅適用于 XInput (XInput 1.3) 的 DirectX SDK 版本。 Windows 8版本的 XInput (XInput 1.4) 會獨佔使用透過XInputGetAudioDeviceIds取得的Windows音訊會話 API (WASAPI) 裝置識別碼。

XInputGetDSoundAudioDeviceGuids( i, &dsRenderGuid, &dsCaptureGuid );

擷取 GUID 之後,您可以呼叫 DirectSoundCreate8 和 DirectSoundCaptureCreate8 來建立適當的介面,如下所示:

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

程式設計參考