Windows 應用程式中的 XInput 用戶入門

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

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

XInput 簡介

當應用程式插入 Windows 計算機時,可以使用 XInput API 與遊戲控制器通訊(一次最多可以插入四個唯一控制器)。

使用此 API,可以查詢任何相容的連線控制器,以取得其狀態,也可以設定震動效果。 連接頭戴式裝置的控制器也可以查詢聲音輸入和輸出裝置,這些裝置可與耳機搭配語音處理使用。

控制器配置

相容的控制器有兩個類比方向桿,每個都有一個數位按鈕、兩個類比觸發程式、一個具有四個方向的數位方向板和八個數字按鈕。 呼叫 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 之後控制器的狀態是否已變更。 如果 dwPacketNumber 不會在對 XInputGetState 的兩個循序呼叫之間變更,則狀態沒有變更。 如果不同,則應用程式應該檢查 XINPUT_STATE 結構的 Gamepad 成員,以取得更詳細的狀態資訊。

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

死區

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

注意

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

應用程式應該在模擬輸入上使用「死區」(觸發程式、遊戲桿)來指出是否已在遊戲桿或觸發程式上進行足夠的動作,才能視為有效。

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

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 到 0-65534),或者您可以使用 XInput.h 中定義為XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE、XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE和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 );

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

取得音訊裝置標識碼

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

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

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

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 裝置標識碼的詳細資訊,請參閱 擷取數據流

取得 DirectSound GUID (僅限舊版 DirectX SDK)

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

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

注意

不建議使用舊版 DirectSound ,而且無法在 Windows 市集應用程式中使用。 本節中的資訊僅適用於 XInput 的 DirectX SDK 版本(XInput 1.3)。 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;

程式設計參考