共用方式為


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

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

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

XInput 簡介

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

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

控制器配置

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

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

選擇性地,耳機可以連接到控制器。 耳機具有語音輸入的麥克風,以及聲音輸出的耳機。 您可以呼叫 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 結構中擷取狀態之後,您可以檢查狀態是否有變更,並取得控制器狀態的特定資訊。

dwPacketNumberXINPUT_STATE 結構中的一個成員,其可用於檢查控制器自上次呼叫 XInputGetState以來的狀態是否有所變更。 如果 dwPacketNumber 不會在兩個循序呼叫之間變更為 XInputGetState,則狀態沒有變更。 如果不同,則應用程式應該檢查 GamepadXINPUT_STATE 結構的成員,以取得更詳細的狀態資訊。

基於效能考慮,請勿針對每個畫面的「空白」使用者位置呼叫 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),或者您可以使用 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](如上述範例所示),並選擇性地套用非線性轉換。

例如,在駕駛遊戲中,透過控制器駕駛汽車時,將結果取立方可能會有幫助,因為這樣能在較低範圍提供更高的精確度。這是理想的,因為玩家通常會輕按以獲得細微的移動,或是全力按向一個方向以獲得快速反應。

設定震動效果

除了取得控制器的狀態之外,您也可以將震動數據傳送至控制器,以改變提供給控制器使用者的意見反應。 控制器包含兩個可獨立控制的朗朗馬達,其方式是將值傳遞至 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 中,這些函式是透過 DirectSound來完成,使用 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;

程式設計參考