レース ホイールとフォース フィードバック
このページでは、ユニバーサル Windows プラットフォーム (UWP) 用の Windows.Gaming.Input.RacingWheel API および関連 API を使った、Xbox One のレーシング ハンドルを対象にしたプログラミングの基礎について説明します。
ここでは、次の項目について紹介します。
- 接続されているレーシング ハンドルとそのユーザーの一覧を収集する方法
- レーシング ハンドルが追加または削除されたことを検出する方法
- 1 つ以上のレーシング ハンドルから入力を読み取る方法
- フォース フィードバック コマンドを送信する方法
- レーシング ハンドルが UI ナビゲーション デバイスとして動作する方法
レーシング ハンドルの概要
レーシング ハンドルとは、本物のレーシングカーの運転席の感触を再現する入力デバイスです。 レーシング ハンドルは、アーケードスタイルとシミュレーションスタイルの両方のレーシング ゲームに最適な入力デバイスです。 レーシング ハンドルは、Windows.Gaming.Input 名前空間によって、Windows 10 または Windows 11 および Xbox One UWP アプリでサポートされています。
レーシング ハンドルはさまざまな価格ポイントで提供されますが、一般に、価格ポイントが上がるにつれ、入力とフォース フィードバックの機能が増え性能も向上します。 すべてのレーシング ハンドルには、アナログのステアリング ハンドル、アナログのアクセルとブレーキのコントロール、いくつかのハンドル上のボタンが装備されています。 一部のレーシング ハンドルには、アナログのクラッチとハンドブレーキのコントロール、シフトレバー、フォース フィードバック機能が追加されています。 レーシング ハンドルの機能セットはどれも同じではなく、特定の機能のサポート状況も異なる可能性があります。たとえば、ステアリング ハンドルがサポートする回転の範囲や、シフト レバーがサポートするギア数は異なっている可能性があります。
デバイスの機能
レーシング ハンドルごとに提供されるオプション デバイス機能のセットは異なり、それらの機能のサポート レベルも多様です。同一種類の入力デバイス間でこのようなレベルの違いがあることは、Windows.Gaming.Input API がサポートするデバイスの中でも独特です。 また、扱うことになるほとんどのデバイスで、少なくともいくつかのオプション機能またはその他のバリエーションがサポートされます。 このため、接続しているレーシング ハンドルそれぞれの機能を個別に判別して、ゲームにとって意味がある機能のすべてのバリエーションをサポートすることが重要です。
詳細については、「レーシング ハンドルの機能の判別」を参照してください。
フォース フィードバック
一部の Xbox One レーシング ハンドルには、単なるバイブレーションではない本物のフォース フィードバックが備わっています。つまり、ステアリング ハンドルなどコントロールの軸に実際の力が加えられます。 ゲームはこの機能を利用して、没入感 ("衝突時の衝撃のシミュレーション" や "道路の感触") を高めたり、うまく運転するための難度を上げたりします。
詳細については、「フォース フィードバックの概要」を参照してください。
UI ナビゲーション
ユーザー インターフェイス ナビゲーションのためにさまざまな入力デバイスをサポートする負担を軽くし、ゲームとデバイスの間の一貫性を促進するために、ほとんどの物理入力デバイスは、UI ナビゲーション コントローラーと呼ばれる別の論理入力デバイスとして同時に機能します。 UI ナビゲーション コントローラーは、各種入力デバイスに共通の UI ナビゲーション コマンドのボキャブラリを提供します。
アナログ コントロールに重点が置かれている独自性と、さまざまなレーシング ハンドルのバリエーションのレベルのため、通常、デジタル方向パッド、ビュー、メニュー、A、B、X、Y の各ボタン (ゲームパッドのものと類似) が装備されています。これらは、ゲームプレイのコマンドをサポートするためのものではなく、レーシング ハンドルのボタンとしてすぐに利用することはできません。
UI ナビゲーション コントローラーとして、レーシング ハンドルは、ナビゲーション コマンドの必須セットを左のサムスティック、方向パッド、ビュー ボタン、メニュー ボタン、A ボタン、およびB ボタンにマップします。
ナビゲーション コマンド | レーシング ホイール入力 |
---|---|
上へ | 方向パッド上 |
[下へ] | 方向パッド下 |
Left | 方向パッド左 |
Right | 方向パッド右 |
ビュー | View ボタン |
メニュー | Menu ボタン |
同意する | A ボタン |
キャンセル | B ボタン |
さらに、一部のレーシング ハンドルでは、ナビゲーション コマンドのオプション セットの一部が、サポートしている他の入力にマップされることがあります。ただし、コマンド マッピングはデバイスによって異なる可能性があります。 これらのコマンドをサポートすることも検討してください。ただし、これらのコマンドはゲームのインターフェイス内の移動に不可欠ではありません。
ナビゲーション コマンド | レーシング ホイール入力 |
---|---|
Page Up | 状況に応じて異なる |
Page Down | 状況に応じて異なる |
左のページ | 状況に応じて異なる |
右のページ | 状況に応じて異なる |
上にスクロール | 状況に応じて異なる |
下にスクロール | 状況に応じて異なる |
左へスクロール | 状況に応じて異なる |
右へスクロール | 状況に応じて異なる |
コンテキスト 1 | X ボタン (一般的な場合) |
コンテキスト 2 | Y ボタン (一般的な場合) |
コンテキスト 3 | 状況に応じて異なる |
コンテキスト 4 | 状況に応じて異なる |
レーシング ハンドルを検出して追跡する
レーシング ハンドルの検出と追跡の方法はゲームパッドの場合とまったく同じですが、Gamepad クラスの代わりに RacingWheel クラスを使用する点だけが異なります。 詳細については、「ゲームパッドと振動」を参照してください。
レーシング ハンドルの読み取り
関心のあるレーシング ハンドルを特定したら、それらから入力を収集する準備が整いました。 ただし、慣れている他の種類の入力とは異なり、レーシング ハンドルはイベントを発生させることで状態変化を伝えるのではありません。 代わりに、現在の状態を定期的に読み取るために "ポーリング" します。
レーシング ハンドルのポーリング
ポーリングによって、レーシング ハンドルの正確な時点のスナップショットがキャプチャされます。 入力収集に対するこのアプローチは、ほとんどのゲームに適しています。通常、そのロジックがイベントドリブンではなく確定的なループで実行されるためです。また、一般にゲーム コマンドを解釈するのは、一度に収集された入力からの方が、時間の経過につれて収集される多数の単一の入力からよりも簡単です。
GetCurrentReading を呼び出してレーシング ハンドルをポーリングします。この関数が、レーシング ハンドルの状態を含む RacingWheelReading を返します。
次の例では、レーシング ハンドルの現在の状態をポーリングします。
auto racingwheel = myRacingWheels[0];
RacingWheelReading reading = racingwheel->GetCurrentReading();
レーシング ハンドルの状態に加えて、各読み取りには、状態がいつ取得されたかを正確に示すタイムスタンプが含まれます。 このタイムスタンプは、以前の読み取りのタイミングや、ゲームのシミュレーションのタイミングと関連付けに便利です。
レーシング ハンドルの機能の判別
レーシング ハンドル コントロールの多くがオプションであったり、必須コントロールでもサポートするバリエーションが異なったりするため、レーシング ハンドルの各読み取りで収集された入力を処理する前に、各レーシング ハンドルの機能を個別に判別する必要があります。
オプションのコントロールは、ハンドブレーキ、クラッチ、およびシフトレバーです。接続しているレーシング ハンドルがこれらのコントロールをサポートするかどうかを判別するには、レーシング ハンドルの HasHandbrake、HasClutch、および HasPatternShifter プロパティをそれぞれ読み取ります。 コントロールがサポートされているのはプロパティの値が 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 の場合、バック ギア、ニュートラル、第 1 ギア、第 2 ギア、第 3 ギア、および第 4 ギアまでサポートされます。
auto maxWheelDegrees = racingwheel->MaxWheelAngle;
auto maxShifterGears = racingwheel->MaxPatternShifterGear;
最後に、一部のレーシング ハンドルでは、ステアリング ハンドルを介したフォース フィードバックがサポートされます。 接続されたレーシング ハンドルがフォース フィードバックをサポートしているかどうかを判別するには、レーシング ハンドルの WheelMotor プロパティを読み取ります。 WheelMotor
プロパティの値が null ではない場合、フォース フィードバックはサポートされています。それ以外の場合は、サポートされません。
if (racingwheel->WheelMotor != nullptr)
{
// force feedback is supported
}
サポートされているレーシング ハンドルでフォース フィードバックを使用する方法の詳細については、「フォース フィードバックの概要」を参照してください。
ボタンの読み取り
レーシング ハンドルの各ボタン (方向パッドの 4 方向、前のギア ボタンと次のギア ボタン、その他 16 個のボタン) は、デジタルの読み取り値によって、押されている (ダウン) か離されている (アップ) かを示します。 効率を高めるため、ボタンの読み取り値は個別のブール値としては表されません。代わりに、読み取り値はすべて、RacingWheelButtons 列挙型で表される単一のビットフィールドにパックされます。
Note
レーシング ハンドルには、ビュー ボタンやメニュー ボタンなど、UI 操作に使用するその他のボタンが搭載されています。 これらのボタンは RacingWheelButtons
列挙体には含まれず、UI ナビゲーション デバイスとしてレーシング ハンドルにアクセスしないと読み取ることができません。 詳細については、「UI ナビゲーション デバイス」を参照してください。
ボタンの値は、RacingWheelReading 構造体の Buttons
プロパティから読み取ります。 このプロパティはビットフィールドであるため、ビット演算子マスクを使用して目的のボタンの値を分離します。 対応するビットが設定されているときはボタンが押されており (ダウン)、それ以外の場合はボタンが離されています (アップ)。
次の例では、[次のギア] ボタンが押されているかどうかを判別します。
if (RacingWheelButtons::NextGear == (reading.Buttons & RacingWheelButtons::NextGear))
{
// Next Gear is pressed
}
次の例では、[次のギア] ボタンが離されているかどうかを判別します。
if (RacingWheelButtons::None == (reading.Buttons & RacingWheelButtons::NextGear))
{
// Next Gear is released (not pressed)
}
場合によっては、ボタンが押された状態から離された状態への移行またはその逆方向への移行のタイミング、複数のボタンが押されているか離されているかの状態、または一連のボタンが特定のパターンの状態になっているかどうか (一部が押されていて、一部が押されていない) を特定する必要があります。 これらの状態を検出する方法の詳細については、「ボタン移行の検出」および「複雑なボタン配置の検出」を参照してください。
ハンドルの読み取り
ステアリング ハンドルは、-1.0 から +1.0 のアナログ読み取り値を提供する必須コントロールです。 値 -1.0 は最も左のハンドル位置に対応します。+1.0 の値は最も右の位置に対応します。 ステアリング ハンドルの値は、RacingWheelReading 構造体の Wheel
プロパティから読み取ります。
float wheel = reading.Wheel; // returns a value between -1.0 and +1.0.
実際のレーシング ハンドルでサポートされる角度の範囲によって、ハンドルの読み取りは、実際のハンドルのさまざまな物理角度に対応しますが、通常、ハンドルの読み取りをスケーリングする必要はありません。サポートする角度が広いハンドルの方が精度が高くなるだけです。
アクセルとブレーキの読み取り
アクセルとブレーキは、それぞれが浮動小数点値として表される、0.0 (完全に離れている) から 1.0 (最後まで踏んでいる) までのアナログ読み取り値を提供する必須コントロールです。 アクセル コントロールの値は、RacingWheelReading 構造体の Throttle
プロパティから、ブレーキ コントロールの値は 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 (完全に操作している) までのアナログ読み取り値を提供するオプション コントロールです。 ハンドブレーキ コントロールの値は、RacingWheelReading 構造体の Handbrake
プロパティから、クラッチ コントロールの値は 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 の値は、それぞれ "リバース" と "ニュートラル" のギアに対応します。最大 MaxPatternShifterGear (その値を含む) まで増える正の値に対応して、前進用のギアの段階が上がります。 シフトレバーの値は、RacingWheelReading 構造体の PatternShifterGear プロパティから読み取ります。
if (racingwheel->HasPatternShifter)
{
gear = reading.PatternShifterGear;
}
Note
シフトレバーがサポートされている場合、必須の [前のギア] ボタンと [次のギア] ボタンも一緒に存在し、これらもプレイヤーの車の現在のギアに影響します。 両方が存在する場合にギアの入力を統合する簡単な方法は、プレイヤーが車に AT (オートマチック トランスミッション) を選択しているときは、シフトレバー (とクラッチ) を無視することです。レーシング ハンドルにシフトレバー コントロールしか搭載されていなくて、プレイヤーが車に MT (マニュアル トランスミッション) を選択しているときは、[前のギア] ボタンと [次のギア] ボタンを無視します (レーシング ハンドルにシフトレバー コントロールが装備されている場合のみ)。 これがゲームに適していない場合は、統合するために別の戦略を実装してください。
InputInterfacing サンプルを実行する
GitHub の InputInterfacingUWP サンプル アプリでは、レーシング ハンドルやさまざまな種類の入力デバイスを使用する方法や、それらの入力デバイスが UI ナビゲーション コントローラーとして動作する方法が示されます。
フォース フィードバックの概要
多くのレーシング ハンドルにはフォース フィードバック機能が備えられており、没入感が高くやりがいがある運転体験が提供されます。 フォース フィードバックをサポートするレーシング ハンドルには、通常、1 つのモーターが装備されており、1 つの軸 (ハンドルの回転軸) に沿ってステアリング ハンドルに力を加えます。 フォース フィードバックは、Windows 10 または Windows 11 および Xbox One UWP アプリで Windows.Gaming.Input.ForceFeedback 名前空間によってサポートされています。
Note
フォース フィードバック API は、力を加える複数の軸をサポートできますが、現在のレーシング ハンドルでは、ハンドルの回転軸以外のフィードバック軸はサポートされていません。
フォース フィードバックの使用
これらのセクションでは、レーシング ハンドルのフォース フィードバック効果のプログラミングの基本について説明します。 フィードバックは効果を使用して適用されます。効果を最初にフォース フィードバック デバイスに読み込んでから、開始、一時停止、再開、停止を行うことができます (サウンド効果と同様の方法です)。ただし、まずレーシング ハンドルのフィードバック機能を判別する必要があります。
フォース フィードバック機能の判別
接続されたレーシング ハンドルがフォース フィードバックをサポートしているかどうかを判別するには、レーシング ハンドルの WheelMotor プロパティを読み取ります。 WheelMotor
が null の場合は、フォース フィードバックはサポートされません。そうでない場合は、フォース フィードバックはサポートされているので、続行して、フォース フィードバックが適用される軸など、モーターの特定のフィードバック機能を判別できます。
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();
}
また、必要に応じて、特定のレーシング ハンドルのフォース フィードバック システム全体を非同期的に有効化、無効化、またはリセットできます。