原始遊戲控制器

此頁面將介紹基本概念,說明如何以通用 Windows 平台 (UWP) 專用的 Windows.Gaming.Input.RawGameController 和相關 API,設計適用於幾乎所有類型的遊戲控制器。

閱讀此頁面後,您將瞭解:

  • 如何收集已連線的原始遊戲控制器及其使用者清單
  • 如何偵測原始遊戲控制器已新增或移除
  • 如何取得原始遊戲控制器的功能
  • 如何從原始遊戲控制器讀取輸入

概觀

「原始遊戲控制器」是遊戲控制器的通用型式,提供多種常見遊戲控制器上的輸入控制項。 這些輸入會公開顯示為簡單的未命名按鍵、切換鈕和軸陣列。 無論客戶使用哪類型的控制器,您都可以採用原始遊戲控制器,讓客戶建立自訂的輸入對應。

RawGameController 類別真正適用的時機,就是其他輸入類別 (ArcadeStickFlightStick 等等) 都不符合您的需求時;如果您需要更通用的版本,且預期客戶會使用多種不同的遊戲控制器,即可使用這個類別。

偵測及追蹤原始遊戲控制器

偵測和追蹤原始遊戲控制器的運作原理與遊戲台完全相同,只是使用的是 RawGameController 類別,而不是 Gamepad 類別。 如需詳細資訊,請參閱<遊戲台和震動>。

取得原始遊戲控制器的功能

識別您有興趣的原始遊戲控制器後,即可收集該控制器的功能資訊。 使用 RawGameController.ButtonCount 可取得原始遊戲控制器的按鍵數;使用 RawGameController.AxisCount 可取得模擬軸數;使用 RawGameController.SwitchCount 則可取得切換鈕數。 此外,您可以使用 RawGameController.GetSwitchKind 取得切換鈕類型。

下列範例示範取得原始遊戲控制器的輸入計數:

auto rawGameController = myRawGameControllers->GetAt(0);
int buttonCount = rawGameController->ButtonCount;
int axisCount = rawGameController->AxisCount;
int switchCount = rawGameController->SwitchCount;

下列範例示範如何判斷各個切換鈕的類型:

for (uint32_t i = 0; i < switchCount; i++)
{
    GameControllerSwitchKind mySwitch = rawGameController->GetSwitchKind(i);
}

讀取原始遊戲控制器

當您知道原始遊戲控制器的輸入數目後,即可收集該控制器的輸入。 不過,原始遊戲控制器不會透過引發事件來傳達狀態變更,可能與您習慣的其他種輸入不同。 您可以用輪詢的方式,定期讀取控制器的目前狀態。

輪詢原始遊戲控制器

輪詢時,系統會在精確的時間點擷取原始遊戲控制器快照。 這種收集輸入命令的方式很適合大部分遊戲,因為該方式的邏輯通常會以決定性迴圈執行,而非事件驅動。 而且,比起解讀一段期間收集到的許多單一輸入,針對一次收集到的所有輸入解讀遊戲命令,通常也比較簡單。

您可以呼叫 RawGameController.GetCurrentReading 來輪詢原始遊戲控制器。 此函式會在按鍵、切換鈕和軸陣列 (內含原始遊戲控制器的狀態) 中填入值。

下列範例示範輪詢原始遊戲控制器的目前狀態:

Platform::Array<bool>^ currentButtonReading =
    ref new Platform::Array<bool>(buttonCount);

Platform::Array<GameControllerSwitchPosition>^ currentSwitchReading =
    ref new Platform::Array<GameControllerSwitchPosition>(switchCount);

Platform::Array<double>^ currentAxisReading = ref new Platform::Array<double>(axisCount);

rawGameController->GetCurrentReading(
    currentButtonReading,
    currentSwitchReading,
    currentAxisReading);

針對不同類型的控制器,每個陣列內的特定位置不保證會填入特定輸入值。因此,您需要使用 RawGameController.GetButtonLabelRawGameController.GetSwitchKind 方法檢查對應的輸入。

GetButtonLabel 可指出實體按鍵上顯示的文字或符號,而非按鍵功能。因此,建議將該標籤當成 UI 輔助,用以提示玩家哪個按鍵會執行哪項功能。 GetSwitchKind 可指出切換鈕類型 (即有多少位置),而非名稱。

目前沒有取得軸或切換鈕標籤的標準方法,您必須自行測試並判斷輸入。

如果您有想支援的特定控制器,可呼叫 RawGameController.HardwareProductIdRawGameController.HardwareVendorId,並檢查是否與該控制器相符。 每個陣列中每筆輸入的位置,與具有同一個 HardwareProductIdHardwareVendorId 的所有控制器都相同,因此您不用擔心邏輯可能會與同類型的不同控制器不一致。

除了原始遊戲控制器的狀態,每筆讀取都會傳回時間戳記,指出狀態的擷取時間。 時間戳記很適合用於與先前的讀取時間或遊戲模擬時間建立關聯。

讀取按鍵和切換鈕

每個原始遊戲控制器的按鍵都有數字,指出目前是按下 (向下) 或放開 (向上)。 按鍵讀數以單一陣列中的個別布爾值表示。 使用 RawGameController.GetButtonLabel 及陣列中的布林值索引,即可找到每個按鍵的標籤。 每個值都以 GameControllerButtonLabel 表示。

下列範例說明如何判斷 XboxA 按鍵是否已按下:

for (uint32_t i = 0; i < buttonCount; i++)
{
    if (currentButtonReading[i])
    {
        GameControllerButtonLabel buttonLabel = rawGameController->GetButtonLabel(i);

        if (buttonLabel == GameControllerButtonLabel::XboxA)
        {
            // XboxA is pressed.
        }
    }
}

有時候,您可能會想判斷按鍵的轉換狀態:是從按下到放開、放開到按下,還是有多個已按下或放開的按鍵,或特定的按鍵組合 (有些按下、有些放開)。 如需有關如何偵測每個條件的資訊,請參閱<偵測按鍵轉換>和<偵測複雜的按鍵組合>。

切換鈕值以 GameControllerSwitchPosition 陣列形式提供。 由於此屬性是位元欄位,因此會使用位元遮罩來隔離切換鈕的方向。

下列範例說明如何判斷每個切換鈕是否處於向上位置:

for (uint32_t i = 0; i < switchCount; i++)
{
    if (GameControllerSwitchPosition::Up ==
        (currentSwitchReading[i] & GameControllerSwitchPosition::Up))
    {
        // The switch is in the up position.
    }
}

讀取模擬輸入 (搖桿、啟動器、油門等等)

模擬軸讀取值介於 0.0 和 1.0 之間。 這包括搖桿的每個維度,例如標準搖桿的 X 和 Y 軸,甚至是飛行搖桿的 X、Y 和 Z 軸 (分別是滾轉、俯仰和偏航)。

這些值可代表模擬啟動器、油門或任何其他類型的模擬輸入。 這些值未隨附標籤,建議您使用各種輸入裝置測試程式碼,確保這些輸入值與您的假設正確相符。

在所有軸中,搖桿置中時的值大約為 0.5,但確切值通常會稍微有點誤差,即使是連續讀數也可能會有此現象。本節稍後將討論減輕該差異問題的策略做法。

下列範例示範如何從 Xbox 控制器讀取模擬值:

// Xbox controllers have 6 axes: 2 for each stick and one for each trigger.
float leftStickX = currentAxisReading[0];
float leftStickY = currentAxisReading[1];
float rightStickX = currentAxisReading[2];
float rightStickY = currentAxisReading[3];
float leftTrigger = currentAxisReading[4];
float rightTrigger = currentAxisReading[5];

讀取搖桿值時,您會發現即使搖桿靜止於中心位置,讀數也不會總是顯示 0.5;搖桿每次移動後返回中心位置,就會出現不一樣的值,但都接近 0.5。 如要減輕這些差異問題,您可以實作小型 死區 (deadzone),也就是接近理想中心位置的特定範圍值,落在該範圍的值皆可忽略。

實作死區的方法之一,是判斷搖桿從中心移動多少距離,然後忽略接近您所選距離的讀數。 您可以使用畢氏定理計算大概距離,這不會是精確的值,因為搖桿讀數基本上是極座標值,而不是平面值。 這會產生徑向死區。

下列範例示範使用畢氏定理的基本徑向死區:

// Choose a deadzone. Readings inside this radius are ignored.
const float deadzoneRadius = 0.1f;
const float deadzoneSquared = deadzoneRadius * deadzoneRadius;

// Pythagorean theorem: For a right triangle, hypotenuse^2 = (opposite side)^2 + (adjacent side)^2
float oppositeSquared = leftStickY * leftStickY;
float adjacentSquared = leftStickX * leftStickX;

// Accept and process input if true; otherwise, reject and ignore it.
if ((oppositeSquared + adjacentSquared) < deadzoneSquared)
{
    // Input accepted, process it.
}

另請參閱