賽車方向盤和力回饋

此頁面將介紹基本概念,說明如何在通用 Windows 平台 (UWP) 使用 Windows.Gaming.Input.RacingWheel 和相關 API,設計適用於 Xbox One 的賽車方向盤程式。

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

  • 如何收集已連線的賽車方向盤及其使用者清單
  • 如何偵測賽車方向盤已新增或移除
  • 如何從一或多個賽車方向盤讀取輸入
  • 如何傳送力回饋命令
  • 如何將賽車方向盤當成 UI 瀏覽裝置使用

賽車方向盤概觀

賽車方向盤是輸入裝置,能模擬真實賽車駕駛艙的感覺。 賽車方向盤輸入裝置非常適合以汽車或卡車為主的電玩及模擬賽車遊戲。 Windows 10 或 Windows 11 和 Xbox One UWP 應用程式支援使用 Windows.Gaming.Input 命名空間的賽車方向盤。

賽車方向盤提供各種價位的產品,一般來說,價位越高,輸入及力回饋功能越多且越好。 所有賽車方向盤都配備一組模擬方向盤、模擬油門和煞車控制器,以及方向盤按鍵。 有些賽車方向盤會另外配備模擬離合器和手煞車控制器、排檔器和力回饋功能。 賽車方向盤配備的功能不一定相同,且可能因支援項目而異,例如:方向盤可能支援不同的旋轉範圍,排檔器可能支援不同的檔數。

裝置功能

不同的賽車方向盤提供不同的選配裝置功能,且有各種支援功能等級;在 Windows.Gaming.Input API 支援的裝置中,單一類型輸入裝置的這個變化版等級是唯一的。 此外,您看到的大部分裝置至少會支援部分選配功能或其他變化版。 因此,請務必個別判斷各個已連線賽車方向盤的功能,並根據您的遊戲支援適當的完整變化版功能。

如需詳細資訊,請參閱<判斷賽車方向盤功能>。

力回饋

有些賽車方向盤會提供真正的力回饋 (也就是說,玩家可在方向盤等控制軸上施力),而非只有簡單的震動感。 遊戲可利用這項功能來創造更強烈的沉浸感 (模擬碰撞毀損、以及「道路感」),增加駕駛技術挑戰。

如需詳細資訊,請參閱<力回饋概觀>。

UI 瀏覽

為減少支援不同輸入裝置使用者介面瀏覽的作業負擔,並提升遊戲和裝置之間的一致性,大部分實體輸入裝置運作時,都能分別模擬不同的邏輯輸入裝置 (稱為「UI 瀏覽控制器」)。 UI 瀏覽控制器提供不同輸入裝置通用的 UI 瀏覽命令詞彙。

由於裝置專用於模擬控制項和不同賽車方向盤的各種變化版,因此通常會配備數字方向鍵、View (檢視)Menu (功能表)ABXY 鍵 (仿遊戲台);這些按鍵的用途不是支援遊戲命令,且無法像賽車方向盤按鍵一樣能直接使用。

UI 瀏覽控制器,賽車方向盤將必要 的瀏覽命令組合對應至左搖桿、方向鍵、View (檢視)Menu (功能表)AB 鍵。

瀏覽命令 賽車方向盤輸入
向上 方向鍵上
向下 方向鍵下
Left 方向盤左
Right 方向鍵右
檢視 檢視按鍵
功能表 功能表按鈕
Accept A 按鍵
取消 B 按鍵

此外,有些賽車方向盤可能會將部分選用的瀏覽命令對應至其他支援的輸入裝置,命令對應可能因裝置而異。 另外,也請考慮支援這些命令,但要確保這些命令不是瀏覽遊戲介面的必備要素。

瀏覽命令 賽車方向盤輸入
Page Up 視情況而異
Page Down 視情況而異
Page Left 視情況而異
Page Right 視情況而異
Scroll Up 視情況而異
Scroll Down 視情況而異
Scroll Left 視情況而異
Scroll Right 視情況而異
Context 1 X 按鍵 (通常)
Context 2 Y 按鍵 (通常)
Context 3 視情況而異
Context 4 視情況而異

偵測和追蹤賽車方向盤

偵測和追蹤賽車方向盤的運作原理與遊戲台完全相同,只是使用的是 RacingWheel 類別,而不是 Gamepad 類別。 如需詳細資訊,請參閱<遊戲台和震動>。

讀取賽車方向盤

識別您有興趣的賽車方向盤後,即可收集該方向盤的輸入。 不過,賽車方向盤不會透過引發事件來傳達狀態變更,可能與您習慣的其他種輸入不同。 您可以用輪詢的方式,定期讀取方向盤的目前狀態。

輪詢賽車方向盤

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

您可以呼叫 GetCurrentReading 輪詢賽車方向盤。此函式會傳回內含賽車方向盤狀態的 RacingWheelReading

下列範例示範輪詢賽車方向盤的目前狀態。

auto racingwheel = myRacingWheels[0];

RacingWheelReading reading = racingwheel->GetCurrentReading();

除了賽車方向盤狀態,每次讀取都包含一個時間戳,指出擷取狀態的精確時間。 時間戳記很適合用於與先前的讀取時間或遊戲模擬時間建立關聯。

判斷賽車方向盤功能

許多賽車方向盤的控制項都是選用功能,即使是必要控制項,也可能支援不同的變化版。因此,您必須個別判斷每個賽車方向盤的功能,才能處理每次讀取賽車方向盤時收集到的輸入。

選用控制項為手煞車、離合器和排檔器。您可以分別讀取賽車方向盤的 HasHandbrakeHasClutchHasPatternShifter 屬性,判斷已連線的賽車方向盤是否支援這些控制項。 如果屬性值為 true,代表支援該控制項;反之則不支援。

if (racingwheel->HasHandbrake)
{
    // the handbrake is supported
}

if (racingwheel->HasClutch)
{
    // the clutch is supported
}

if (racingwheel->HasPatternShifter)
{
    // the pattern shifter is supported
}

此外,方向盤和排檔器控制項也可能有所不同。 方向盤可能因真實方向盤支援的實際旋轉角度而異,排檔器則可能因支援的前進檔數而有所不同。 您可以藉由讀取賽車方向盤的 MaxWheelAngle 屬性,判斷真實方向盤支援的最大旋轉角度;其值即支援的最大實際角度,包括順時針 (正值) 及逆時針 (負值) 方向的角度。 您可以藉由讀取賽車方向盤的 MaxPatternShifterGear 屬性,判斷排檔器支援的最大前進檔數;其值即支援的最大前進檔數 (含最大值),也就是如果值為 4,則排檔器支援倒車檔、N 檔、一檔、二檔、三檔和四檔。

auto maxWheelDegrees = racingwheel->MaxWheelAngle;
auto maxShifterGears = racingwheel->MaxPatternShifterGear;

最後,有些賽車方向盤支援透過方向盤給予力回饋。 您可以藉由讀取賽車方向盤的 WheelMotor 屬性,判斷連線的賽車方向盤是否支援力回饋。 如果 WheelMotor 不是 null 值,代表支援力回饋;反之則不支援。

if (racingwheel->WheelMotor != nullptr)
{
    // force feedback is supported
}

如需有關如何使用賽車方向盤支援的力回饋功能,請參閱<力回饋概觀>。

讀取按鈕

每個賽車方向盤的按鍵 (包括方向鍵的四個方向、[Previous Gear] (前一檔) 和 [Next Gear] (下一檔) 按鍵,以及 16 個額外的按鍵) 皆有數字,指出目前是按下 (向下) 或放開 (向上)。 為講求效率,按鍵讀取值不會以個別的布林值表示,而是全都封裝至單一位元欄位,以 RacingWheelButtons 列舉表示。

注意

賽車方向盤配備其他用於瀏覽 UI 的按鍵,例如 [View] (檢視) 和 [Menu] (功能表) 按鍵。 這些按鍵不屬於RacingWheelButtons列舉,且只在存取的賽車方向盤作為 UI 瀏覽裝置時才能讀取。 如需詳細資訊,請參閱<UI 瀏覽裝置>。

按鍵值讀取自 ButtonsRacingWheelReading 結構的屬性。 由於此屬性是位元欄位,因此會使用位元遮罩來隔離您有興趣的按鍵值。 如已設定對應位元,按鍵為按下 (向下);反之則為放開 (向上)。

下列範例示範 [Next Gear] (下一檔) 按鍵是否已按下。

if (RacingWheelButtons::NextGear == (reading.Buttons & RacingWheelButtons::NextGear))
{
    // Next Gear is pressed
}

下列範例示範 [Next Gear] (下一檔) 按鍵是否已放開。

if (RacingWheelButtons::None == (reading.Buttons & RacingWheelButtons::NextGear))
{
    // Next Gear is released (not pressed)
}

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

讀取方向盤

方向盤是必要控制項,提供的模擬讀取值介於 -1.0 到 +1.0 之間。 值 -1.0 對應至最左邊的方向盤位置;值 +1.0 則對應最右邊的位置。 方向盤的值讀取自 WheelRacingWheelReading 結構的屬性。

float wheel = reading.Wheel;  // returns a value between -1.0 and +1.0.

視實體賽車方向盤支援的旋轉範圍而定,方向盤讀取值會對應真實方向盤的不同實際旋轉角度。您通常不用調整方向盤讀取值,方向盤支援更大的旋轉角度,只是精準度較高。

讀取油門和煞車

油門和煞車是必要控制項,各提供介於 0.0 (全放開) 到 1.0 (全按下) 之間的模擬讀取值,以浮點值表示。 油門控制項的值讀取自 ThrottleRacingWheelReading 結構的屬性;煞車控制項的值讀取自 Brake 屬性。

float throttle = reading.Throttle;  // returns a value between 0.0 and 1.0
float brake    = reading.Brake;     // returns a value between 0.0 and 1.0

讀取手煞車和離合器

手煞車和離合器是選用控制項,各提供介於 0.0 (全放開) 到 1.0 (全按下) 之間的模擬讀取值,以浮點值表示。 手煞車控制項的值讀取自 HandbrakeRacingWheelReading 結構的屬性;離合器控制項的值讀取自 Clutch 屬性。

float handbrake = 0.0;
float clutch = 0.0;

if(racingwheel->HasHandbrake)
{
    handbrake = reading.Handbrake;  // returns a value between 0.0 and 1.0
}

if(racingwheel->HasClutch)
{
    clutch = reading.Clutch;        // returns a value between 0.0 and 1.0
}

讀取排檔器

排檔器是選用控制項,提供的數字介於 -1 和 MaxPatternShifterGear 之間,以符號整數值表示。 值 -1 或 0 分別對應至倒車檔N 檔;增加的正值對應至更大的前進檔,最多 MaxPatternShifterGear (含該值)。 排檔器的值讀取自 RacingWheelReading 結構的 PatternShifterGear 屬性。

if (racingwheel->HasPatternShifter)
{
    gear = reading.PatternShifterGear;
}

注意

如果排檔器是支援項目,則會有必要的 [Previous Gear] (前一檔) 和 [Next Gear] (下一檔) 按鍵,這些按鍵也會影響玩家車子目前的檔位。 如果兩項都有,則有一個簡單的策略可用來整合這些輸入:玩家為車子選擇自排時,忽略排檔器 (和離合器);只有當賽車方向盤配備排檔器控制項,且玩家為車子選擇手排時,則忽略 [Previous Gear] (前一檔) 和 [Next Gear] (下一檔) 按鍵。 如果這個方式不適合您的遊戲,您可以實作其他整合策略。

執行 InputInterfacing 範例

GitHub 上的 InputInterfacingUWP 範例應用程式示範如何同時使用賽車方向盤和不同的輸入裝置,以及這些輸入裝置如何當作 UI 瀏覽控制器使用。

力回饋概觀

許多賽車方向盤都有力回饋功能,可提供更身歷其境、更有挑戰性的駕駛體驗。 支援力回饋的賽車方向盤通常會配備單顆馬達,該馬達會沿著單軸 (車輪轉軸) 施力給方向盤。 Windows 10 或 Windows 11 和 Xbox One UWP 應用程式支援使用 Windows.Gaming.Input.ForceFeedback 命名空間的力回饋。

注意

力回饋 API 可支援多個力軸,但目前的賽車方向盤都不支援車輪轉軸以外的任何回饋軸。

使用力回饋

這些章節說明賽車方向盤力回饋效應的程式設計基本概念。 回饋會透過效果施加,先載入到力回饋裝置上,然後以類似音效的方式開始、暫停、繼續和停止。不過,您必須先判斷賽車方向盤的回饋功能。

判斷力回饋功能

您可以藉由讀取賽車方向盤的 WheelMotor 屬性,判斷連線的賽車方向盤是否支援力回饋。 如果WheelMotornull 值,代表不支援力回饋;反之則支援,且您可繼續判斷馬達的特定回饋功能,例如:馬達可影響的轉軸。

if (racingwheel->WheelMotor != nullptr)
{
    auto axes = racingwheel->WheelMotor->SupportedAxes;

    if(ForceFeedbackEffectAxes::X == (axes & ForceFeedbackEffectAxes::X))
    {
        // Force can be applied through the X axis
    }

    if(ForceFeedbackEffectAxes::Y == (axes & ForceFeedbackEffectAxes::Y))
    {
        // Force can be applied through the Y axis
    }

    if(ForceFeedbackEffectAxes::Z == (axes & ForceFeedbackEffectAxes::Z))
    {
        // Force can be applied through the Z axis
    }
}

載入力回饋效果

力回饋效果會載入到回饋裝置上,當遊戲發出命令,該裝置就會自發「播放」。 系統提供幾項基本效果;透過實作 IForceFeedbackEffect 介面的類別可建立自訂效果。

效果類別 效果描述
ConditionForceEffect 套用可變力的效果,用來回應裝置內目前的感應器。
ConstantForceEffect 沿特定向量套用恆力的效果。
PeriodicForceEffect 沿特定向量套用可變力 (由波形定義) 的效果。
RampForceEffect 沿特定向量套用線性增力/減力的效果。
using FFLoadEffectResult = ForceFeedback::ForceFeedbackLoadEffectResult;

auto effect = ref new Windows.Gaming::Input::ForceFeedback::ConstantForceEffect();
auto time = TimeSpan(10000);

effect->SetParameters(Windows::Foundation::Numerics::float3(1.0f, 0.0f, 0.0f), time);

// Here, we assume 'racingwheel' is valid and supports force feedback

IAsyncOperation<FFLoadEffectResult>^ request
    = racingwheel->WheelMotor->LoadEffectAsync(effect);

auto loadEffectTask = Concurrency::create_task(request);

loadEffectTask.then([this](FFLoadEffectResult result)
{
    if (FFLoadEffectResult::Succeeded == result)
    {
        // effect successfully loaded
    }
    else
    {
        // effect failed to load
    }
}).wait();

使用力回饋效果

載入後,對賽車方向盤的 WheelMotor 屬性呼叫函式,即可同步開始、暫停、繼續和停止所有效果,或者可個別對回饋效果呼叫函式。 一般來說,您應於遊戲開始前載入所有要在回饋裝置使用的效果,然後在遊戲進行期間,使用個別的 SetParameters 函式更新效果。

if (ForceFeedbackEffectState::Running == effect->State)
{
    effect->Stop();
}
else
{
    effect->Start();
}

最後,您可以視需要非同步啟用、停用或重設特定賽車方向盤上的整個力回饋系統。

另請參閱