次の方法で共有


フライト スティック

このページでは、Windows.Gaming.Input.FlightStick とユニバーサル Windows プラットフォーム (UWP) の関連 API を使用した Xbox One 認定フライト スティックのプログラミングの基本について説明します。

このページを読むと、次の内容が学習されます。

  • 接続されているフライト スティックとそのユーザーの一覧を収集する方法
  • フライト スティックが追加または削除されたことを検出する方法
  • 1 つ以上のフライト スティックから入力を読み取る方法
  • フライト スティックが UI ナビゲーション デバイスとして動作する方法

概要

フライト スティックは、飛行機や宇宙船のコックピットで見つかるフライト スティックの感覚を再現するために評価されるゲーム入力デバイスです。 彼らは飛行の速く、正確な制御のための完全な入力装置である。 フライト スティックは、Windows 10 または Windows 11 および Xbox One アプリで Windows.Gaming.Input 名前空間を 介してサポートされています。

Xbox One 認定フライト スティックには、次のコントロールが装備されています。

  • ロール、ピッチ、ヨーが可能なツイスト可能なアナログジョイスティック
  • アナログスロットル
  • 2 つのファイア ボタン
  • 8方向デジタルハットスイッチ
  • ビュー ボタン および メニュー ボタン

ビューメニュー ボタンは、ゲームプレイ コマンドではなく UI ナビゲーションをサポートするために使用されるため、ジョイスティック ボタンとして簡単にアクセスすることはできません。

UI ナビゲーション

ユーザー インターフェイス ナビゲーション用にさまざまな入力デバイスをサポートする負担を軽減し、ゲームとデバイス間の整合性を促進するために、ほとんどの 物理 入力デバイスは、UI ナビゲーション コントローラーと呼ばれる個別の 論理 入力デバイスとして同時に機能します。 UI ナビゲーション コントローラーは、入力デバイス間の UI ナビゲーション コマンドに共通のボキャブラリを提供します。

UI ナビゲーション コントローラーとして、フライト スティックは、ナビゲーション コマンドの 必要なセット をジョイスティックにマップし、ビューメニュー、およびFirePrimaryFireSecondary ボタンをします。

ナビゲーション コマンド フライト スティック入力
上方向 ジョイスティックを上に
下方向 ジョイスティックを下に倒す
ジョイスティックを左に
はい ジョイスティックを右に動かす
表示 ビュー ボタン
メニュー メニュー ボタン
同意する FirePrimary ボタン
キャンセル FireSecondary ボタン

フライト スティックでは、ナビゲーション コマンドのオプション セット マップされません。

フライト スティックを検出して追跡する

フライト スティックの検出と追跡は、Gamepad クラスではなく、FlightStick クラスを除き、ゲームパッドの場合とまったく同じように機能します。 詳細については ゲームパッドと振動 を参照してください。

フライト スティックの読み取り

関心のあるフライト スティックを特定したら、そこから入力を収集する準備が整います。 ただし、他の種類の入力とは異なり、フライト スティックはイベントを発生させて状態変化を伝えません。 代わりに、ポーリングを行って、 の現在の状態を定期的に把握します。

フライトスティックの信号チェック

ポーリングでは、正確な時点でフライト スティックのスナップショットがキャプチャされます。 入力収集に対するこのアプローチは、ほとんどのゲームに適しています。そのロジックは通常、イベントドリブンではなく確定的なループで実行されるためです。 また、通常は、一度に収集された入力からゲーム コマンドを解釈する方が、時間の経過と同時に収集される多数の単一の入力からのコマンドよりも簡単です。

フライトスティックをポーリングするには、FlightStick.GetCurrentReadingを呼び出します。 この関数は、フライト スティックの状態を含む FlightStickReading を返します。

次の例では、フライトスティックの現在の状態を調べます。

auto flightStick = myFlightSticks->GetAt(0);
FlightStickReading reading = flightStick->GetCurrentReading();

フライト スティックの状態に加えて、各読み取りには、状態が取得された正確なタイミングを示すタイムスタンプが含まれます。 タイムスタンプは、以前の読み取りのタイミングやゲーム シミュレーションのタイミングに関連する場合に役立ちます。

ジョイスティックとスロットル入力の読み取り

ジョイスティックは、X 軸、Y 軸、Z 軸 (それぞれロール、ピッチ、ヨー) で -1.0 と 1.0 のアナログ読み取り値を提供します。 ロールの場合、-1.0 の値は左端のジョイスティック位置に対応し、値 1.0 は右端の位置に対応します。 ピッチの場合、値 -1.0 は最下位のジョイスティック位置に対応し、値 1.0 は最上位の位置に対応します。 ヨーの場合、値 -1.0 は最も反時計回りのねじれた位置に対応し、値 1.0 は最も時計回りの位置に対応します。

すべての軸で、ジョイスティックが中心位置にある場合、値は約 0.0 ですが、後続の読み取り値の間でも正確な値が変わるのは正常です。 このバリエーションを軽減するための戦略については、このセクションの後半で説明します。

ジョイスティックのロールの値は、FlightStickReading.Roll プロパティから読み取られ、ピッチの値は FlightStickReading.Pitch プロパティから読み取られ、yaw の値は FlightStickReading.Yaw プロパティから読み取られます。

// Each variable will contain a value between -1.0 and 1.0.
float roll = reading.Roll;
float pitch = reading.Pitch;
float yaw = reading.Yaw;

ジョイスティックの値を読むと、ジョイスティックが中央の位置に静止しているとき、0.0の中立的な読み取り値が確実に生成されないことがわかります。代わりに、ジョイスティックを移動して中央の位置に戻すたびに、0.0 付近に異なる値が生成されます。 これらのバリエーションを軽減するために、小さな デッドゾーンを実装できます。これは、無視される理想的な中心位置に近い値の範囲です。

デッドゾーンを実装する 1 つの方法は、ジョイスティックが中心からどのくらい離れているかを判断し、選択した距離よりも近い読み取り値を無視することです。 Pythagorean 定理を使用するだけで、距離をおおよそ計算できますが、ジョイスティックの読み取り値は基本的に平面値ではなく極座標値であるため正確ではありません。 これにより、放射状のデッドゾーンが生成されます。

次の例は、ピタゴラス定理を使用した基本的な放射状デッドゾーンを示しています。

// 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 = pitch * pitch;
float adjacentSquared = roll * roll;

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

ボタンと帽子スイッチの読み取り

フライトスティックの2つのファイアボタンは、それぞれ押された状態(オン)か、離された状態(オフ)かを示すデジタル信号を提供します。 効率を高める目的で、ボタンの読み取り値は個々のブール値として表されるのではなく、FlightStickButtons 列挙体によって表される単一のビットフィールドにパックされます。 さらに、8 方向のハットスイッチは、GameControllerSwitchPosition 列挙体によって、1 つのビットフィールド内に方向情報を収める機能を提供します。

フライト スティックには、ビューメニュー ボタンなど、UI ナビゲーションに使用される追加のボタンが装備されています。 これらのボタンは FlightStickButtons 列挙体の一部ではなく、UI ナビゲーション デバイスとしてフライト スティックにアクセスすることによってのみ読み取ることができます。 詳細については、「UI ナビゲーション コントローラーの」を参照してください。

ボタンの値は、FlightStickReading.Buttons プロパティから読み取られます。 このプロパティはビットフィールドであるため、関心のあるボタンの値を分離するためにビットごとのマスクが使用されます。 対応するビットが設定されている場合、ボタンは押されます(下)。それ以外の場合は、リリース (アップ) されます。

次の例では、FirePrimary ボタンが押されているかどうかを確認します。

if (FlightStickButtons::FirePrimary == (reading.Buttons & FlightStickButtons::FirePrimary))
{
    // FirePrimary is pressed.
}

次の例では、FirePrimary ボタンが離されているかどうかを確認します。

if (FlightStickButtons::None == (reading.Buttons & FlightStickButtons::FirePrimary))
{
    // FirePrimary is released (not pressed).
}

場合によっては、ボタンが押された状態から離された状態、または離された状態から押された状態に切り替わるタイミングや、複数のボタンが押されたり離されたりしているかどうか、または一連のボタンが特定の方法で配置されているか (一部が押されており、一部が押されていない) を判断する必要があります。 これらの各条件を検出する方法については、「ボタン遷移の検出 と、複雑なボタン配置の検出を参照してください。

ハット スイッチの値は、FlightStickReading.HatSwitch プロパティから読み取られます。 このプロパティもビットフィールドであるため、ビットごとのマスクを使用して、ハット スイッチの位置を分離します。

次の例では、ハット スイッチが上の位置にあるかどうかを判断します。

if (GameControllerSwitchPosition::Up == (reading.HatSwitch & GameControllerSwitchPosition::Up))
{
    // The hat switch is in the up position.
}

次の例では、帽子スイッチが中央の位置にあるかどうかを判断します。

if (GameControllerSwitchPosition::Center == (reading.HatSwitch & GameControllerSwitchPosition::Center))
{
    // The hat switch is in the center position.
}

こちらも参照ください