ユーザー入力#

概要#

MGLはゲームプログラミングに必須な各種入力デバイスの読み取りに対応しています。これらは全てのプラットフォームで透過的に扱えるインターフェースを用意しており、必要に応じて基底部分をカスタムすることも可能です。

MGLが現在対応している入力デバイスは次の通りです。

  • キーボード

  • マウス

  • ゲームパッド

  • タッチデバイス

本項では、アプリケーション側からユーザー入力を得る基本的な方法について解説します。

注釈

ゲームパッドには様々な呼び方がありますが、MGLではAPIの命名規則や用語の統一性のために「ゲームパッド」(Gamepad) で統一しています。このため、製品によっては適切でない呼称となる場合があります。なお、ドキュメント内における製品名・商標名に関わる部分については、例外的に異なる呼称を用いる場合もあります。

キーボードの読み取り#

キーボードクラス#

キーボードの入力状態を読み取るにはMGL::Input::Keyboardクラスを用います。

キーボードクラスの宣言
MGL::Input::Keyboard keyboard;

利用可否のチェック#

実行中の環境でキーボードが利用可能であるかを判別するには、MGL::Input::Keyboard::IsAvailableまたはMGL::Input::Keyboard::IsConnectingを利用します。両者の違いは、前者はキーボードが利用可能な環境であれば常にtrueを返すのに対し、後者は利用可能かつ接続されている状態でtrueとなります。キーボードが利用できない環境ではMGL::Input::Keyboard::IsConnectingは常にfalseを返すため、アプリケーション側からはこちらの利用を推奨します。

注釈

標準構成におけるWindowsとmacOSにおいては、キーボードが接続されていなくてもMGL::Input::Keyboard::IsConnectingtrueを返します。

現在の実行環境において、キーボードの利用可否をチェックする例を次に示します。

キーボードの利用可否をチェックする例
MGL::Input::Keyboard keyboard;

if (keyboard.IsConnecting())
{
    MGL_TRACE("キーボードが接続されていて利用可能");
}
else
{
    if (keyboard.IsAvailable())
    {
        MGL_TRACE("キーボードは利用可能だけど今は接続されていない");
    }
    else
    {
        MGL_TRACE("この環境では利用不可能");
    }
}

キーの入力取得#

特定のキーの入力状態を得るには、次のいずれかのAPIを利用します。

各々の引数には、MGL::Input::Keycodeで定義されたキーコードを利用します。

これらの利用例を次に示します。

キーボードからキー入力を取得する例
MGL::Input::Keyboard keyboard;

// キーの押下状態を取得
if (keyboard.IsPressing(MGL::Input::Keycode::KeyA))
{
    MGL_TRACE("Aキーが押されている");
}

// キーが押された瞬間を取得
if (keyboard.IsTriggered(MGL::Input::Keycode::KeyA))
{
    MGL_TRACE("Aキーが押された");
}

// キーが離された瞬間を取得
if (keyboard.IsReleased(MGL::Input::Keycode::KeyA))
{
    MGL_TRACE("Aキーが離された");
}

// キーのリピート入力を取得
if (keyboard.IsARepeat(MGL::Input::Keycode::KeyA))
{
    MGL_TRACE("Aキーのリピート入力");
}

これらの関数は、キーボードに対応していない環境で呼び出した場合は常にfalseが返ります。

リピート入力のリピート間隔はMGL::Input::Keyboard::SetRepeatSettingで変更可能です。詳しくは後述のリピート間隔の変更を参照してください。

マウスの読み取り#

マウスクラス#

マウスの入力状態を読み取るにはMGL::Input::Mouseクラスを用います。

マウスクラスの宣言
MGL::Input::Mouse mouse;

利用可否のチェック#

マウスもキーボードと同様に、MGL::Input::Mouse::IsAvailableまたはMGL::Input::Mouse::IsConnectingで利用の可否を判別できます。現在の実行環境において、マウスの利用可否をチェックする例を次に示します。

マウスの利用可否をチェックする例
MGL::Input::Mouse mouse;

if (mouse.IsConnecting())
{
    MGL_TRACE("マウスが接続されていて利用可能");
}
else
{
    if (mouse.IsAvailable())
    {
        MGL_TRACE("マウスは利用可能だけど今は接続されていない");
    }
    else
    {
        MGL_TRACE("この環境では利用不可能");
    }
}

ボタンの入力取得#

ボタンの入力状態を得るには、次のいずれかのAPIを利用します。

各々の引数には、MGL::Input::MouseButtonまたはMGL::Input::MouseButtonFlagsを指定します。MGL::Input::MouseButtonFlagsは複数のボタンを組み合わせたビットフラグで、 MGL::Input::MouseButton|(or)演算子を用いることで変換されます。

これらの利用例を次に示します。

マウスからボタン入力を取得する例
MGL::Input::Mouse mouse;

// ボタンの押下状態を取得
if (mouse.IsPressing(MGL::Input::MouseButton::Left))
{
    MGL_TRACE("左ボタンが押されている");
}

// 指定されたいずれかのボタンの押下状態を取得
if (mouse.IsPressingAny(MGL::Input::MouseButton::Left | MGL::Input::MouseButton::Right))
{
    MGL_TRACE("左ボタンまたは右ボタンが押されている");
}

// ボタンが押された瞬間を取得
if (mouse.IsTriggered(MGL::Input::MouseButton::Left))
{
    MGL_TRACE("左ボタンが押された");
}

// ボタンが離された瞬間を取得
if (mouse.IsTriggered(MGL::Input::MouseButton::Left))
{
    MGL_TRACE("左ボタンが離された");
}

これらの関数は、マウスに対応していない環境で呼び出した場合は常にfalseが返ります。

カーソル位置の取得#

マウスカーソルの位置を得るにはMGL::Input::Mouse::GetPositionを利用します。

マウスカーソルの位置を取得する例
MGL::Input::Mouse mouse;

// 左ボタンを押している間だけチェック
if (mouse.IsTriggered(MGL::Input::MouseButton::Left))
{
    // カーソル位置を取得
    auto cursorPosition = mouse.GetPosition();

    MGL_TRACE("マウスカーソルの位置: %f, %f", cursorPosition.x, cursorPosition.y);
}

カーソル位置の座標系は、標準では画面表示の物理解像度と一致しています。この動作を変更したい場合はMGL::Input::Mouse::SetBoundsで範囲の変更と論理解像度の適用を行ってください。詳細は取得座標への論理解像度の適用にて後述します。

移動量の取得#

マウスの移動量を得るにはMGL::Input::Mouse::GetDeltaMoveを利用します。

マウスカーソルの位置を取得する例
MGL::Input::Mouse mouse;

// 左ボタンを押している間だけチェック
if (mouse.IsPressing(MGL::Input::MouseButton::Left))
{
    // 移動量を取得
    auto deltaMove = mouse.GetDeltaMove();

    MGL_TRACE("マウスの移動量: %f, %f", deltaMove.x, deltaMove.y);
}

取得できる移動量は、前回のフレームのカーソル位置から現在のカーソル位置までの差分となります。

重要

マウスの移動量は実行環境の解像度やリフレッシュレートなどに依存します。ゲーム側で移動量を扱う場合、ゲームオプションに感度設定を設けて調整可能にすることを強く推奨します。

FPSやTPSにおける視点移動など、カーソルを使用せず移動量のみを取得したい場合はMGL::Input::Mouse::SetCursorModeでカーソルモードを変更してください。引数にMGL::Input::CursorModeで定義されているDeltaMoveを指定することで、カーソルを非表示かつ固定化して移動量のみを得られるようになります。標準的なカーソルを使用するモードに戻す場合は引数にPointerを指定してください。

マウスのカーソルモードを切り替える例
MGL::Input::Keyboard keyboard;
MGL::Input::Mouse mouse;

// キーボードで Fキーを押したら切り替え
if (keyboard.IsTriggered(MGL::Input::Keycode::KeyF))
{
    // ポインタモードであれば移動量モードに変更
    if (mouse.GetCursorMode() == MGL::Input::CursorMode::Pointer)
    {
        mouse.SetCursorMode(MGL::Input::CursorMode::DeltaMove);
    }
    // それ以外はポインタモードに戻す
    else
    {
        mouse.SetCursorMode(MGL::Input::CursorMode::Pointer);
    }
}

この例では、キーボードでFキーを押すことで移動量モードとポインタモードを切り替えています。ゲーム側ではシーンに応じて切り替えるよう実装してください。

カーソル表示モード#

MGL::Input::Mouse::SetCursorVisibleModeを利用することで、アプリケーション動作中のカーソルの表示設定を変更可能です。引数にはMGL::Input::CursorVisibleModeで定義された次のいずれかを指定します。

  • Visible:表示(デフォルト)

  • Invisible:非表示

  • AutoInvisible:自動で表示・非表示を切り替え

デフォルトではVisibleが設定されており、マウスカーソルは常に表示されています。

Invisibleを指定した場合、マウスカーソルがウィンドウ内に存在している間は非表示となります。

AutoInvisibleは表示と非表示を自動で切り替えます。マウスカーソルがウィンドウ内に存在しており、移動のない状態が一定時間続いた場合に自動で非表示となります。移動を検知すると時間がリセットされてカーソルが表示され、再び一定時間移動がなければ非表示となります。

なお、カーソルモードを移動量モードに設定している場合、この設定に関わらず非表示となります。

ゲームパッドの読み取り#

ゲームパッドクラス#

ゲームパッドの入力状態を読み取るにはMGL::Input::Gamepadクラスを用います。

ゲームパッドはキーボードやマウスとは異なり、同時に複数扱えるデバイスです。ゲームパッドクラスは単一のゲームパッドデバイスを扱い、デバイスの指定はコンストラクタに与える引数によって行います。

デバイスの指定方法は複数あります。まずは簡単な指定の例として、認識している全てのデバイスのうち、入力のあるいずれかのデバイスで初期化する例を次に示します。

入力のあるゲームパッドを取得して初期化
// 入力のあるいずれかのゲームパッドを取得
MGL::Input::Gamepad gamepad(MGL::Input::PadEntry::Any);

if (gamepad.IsEnabled())    // if (gamepad) のようにbool型へのキャストでもOK
{
    // 取得に成功していたらここに到達
}

デバイスの指定にはエントリーという概念が関わっているため、詳細は次のゲームパッドのエントリーにて解説します。

ゲームパッドのエントリー#

MGLはゲームパッドの識別のためにエントリーという機能が備わっています。エントリーを行ったデバイスには1〜8の番号が割り当てられ、以降はこの番号を用いてデバイスの特定を可能にします。エントリー番号はアプリケーションから明示的にエントリー解除を行うか、デバイスが物理的に切断されるまで有効です。

エントリー番号を表す値はMGL::Input::PadEntryで定義されており、この値をコンストラクタに指定することで特定のデバイスと関連付けられたゲームパッドクラスを生成できます。

MGL::Input::PadEntryのうち、いくつかの値は特殊な指定として機能します。ゲームパッドクラスのコンストラクタに指定可能な特殊な値と、その効果は次のようになります。

NoEntry

エントリーしていないゲームパッドのうち、ボタン入力のあるいずれかのゲームパッドで初期化します。

Any

エントリーの有無に関わらず、ボタン入力のあるいずれかのゲームパッドで初期化します。

Auto

エントリーしているゲームパッドが存在する場合、最も番号の小さいゲームパッドで初期化します。エントリーしているゲームパッドが存在しない場合はNoEntryと同じ効果を持ちます。主にシングルプレイ用のゲームで使用し、コンストラクタを省略した場合はこの指定として扱われます。

条件に合致するゲームパッドが存在しない場合、ゲームパッドクラスは無効なオブジェクトとして生成されます。無効なオブジェクトはMGL::Input::Gamepad::IsEnabledfalseを返し、いずれの状態も初期値のまま変動しません。

アプリケーション起動直後の状態ではどのゲームパッドも未エントリーとなっているため、最初に上記のいずれかの指定でゲームパッドを取得する必要があります。その後、必要に応じてそのゲームパッドをエントリーし、それ以降に使用するゲームパッドを確定させます。例えば、タイトル画面で「PRESS ANY BUTTON」と表示してボタン入力を待ち、その時使用されたゲームパッドをエントリーするといった実装例などが挙げられます。

では、実際のエントリー処理の実装例を次に示します。次の例は、未エントリーのゲームパッドのうちL1ボタンとR1ボタンを同時押ししているものを検出し、それをエントリーする例です。

L1+R1でエントリーする処理の例
// 未エントリーのゲームパッドを取得
MGL::Input::Gamepad gamepad(MGL::Input::PadEntry::NoEntry);

// L1+R1が押されていればエントリーする (IsPressing()については後述する)
if (gamepad.IsPressing(MGL::Input::PadButton::L1 | MGL::Input::PadButton::R1))
{
    gamepad.Entry(MGL::Input::PadEntry::Player1);

    // gamepad.Entry();
    // のように引数を省略した場合、番号は自動で割り当てられる
}

MGL::Input::Gamepad::Entryがエントリーするための関数です。引数は省略可能で、その場合は未使用のエントリー番号が自動で割り当てられます。エントリーに成功した場合、実際に割り当てられたエントリー番号が返ります。

エントリーを解除したい場合はMGL::Input::Gamepad::Leaveを利用します。

ボタンの入力取得#

ボタンの入力状態を得るには、次のいずれかのAPIを利用します。

各々の引数には、MGL::Input::PadButtonまたはMGL::Input::PadButtonFlagsを指定します。MGL::Input::PadButtonFlagsは複数のボタンを組み合わせたビットフラグで、 MGL::Input::PadButton|(or)演算子を用いることで変換されます。

これらの利用例を次に示します。

ゲームパッドからボタン入力を取得する例
MGL::Input::Gamepad gamepad;

// ボタンの押下状態を取得
if (gamepad.IsPressing(MGL::Input::PadButton::Button01))
{
    MGL_TRACE("ボタン01が押されている");
}

// 指定されたいずれかのボタンの押下状態を取得
if (gamepad.IsPressingAny(MGL::Input::PadButton::Button01 | MGL::Input::PadButton::Button02))
{
    MGL_TRACE("ボタン01またはボタン02が押されている");
}

// ボタンが押された瞬間を取得
if (gamepad.IsTriggered(MGL::Input::PadButton::Button01))
{
    MGL_TRACE("ボタン01が押された");
}

// ボタンが離された瞬間を取得
if (gamepad.IsReleased(MGL::Input::PadButton::Button01))
{
    MGL_TRACE("ボタン01が離された");
}

// ボタンのリピート入力を取得
if (gamepad.IsARepeat(MGL::Input::PadButton::Button01))
{
    MGL_TRACE("ボタン01のリピート入力");
}

リピート入力のリピート間隔はMGL::Input::GamepadSettings::SetRepeatSettingで変更可能です。詳しくは後述のリピート間隔の変更を参照してください。

サムスティックの取得#

サムスティックのあるデバイスにおいて、左右のサムスティックの値を得るにはMGL::Input::Gamepad::GetLeftStickMGL::Input::Gamepad::GetRightStickを利用します。

サムスティックの値を取得する例
MGL::Input::Gamepad gamepad;

if (gamepad)
{
    // 左スティックの値を取得
    auto leftStickValue = gamepad.GetLeftStick();
    if (!leftStickValue.IsZero())
    {
        MGL_TRACE("左スティック: %f, %f", leftStickValue.x, leftStickValue.y);
    }

    // 右スティックの値を取得
    auto rightStickValue = gamepad.GetRightStick();
    if (!rightStickValue.IsZero())
    {
        MGL_TRACE("右スティック: %f, %f", rightStickValue.x, rightStickValue.y);
    }
}

サムスティックの値は(0, 0)が中央を表し、水平方向をxが、垂直方向をyが、それぞれ1.0から-1.0の範囲で表現しています。

サムスティックの座標系
../_images/thumbstick.svg

左サムスティックは擬似的なD-Padとして扱うことも可能です。その値に応じて、ボタンのAnalogUpAnalogDownAnalogLeftAnalogRightの内容が更新されます。

ゲームパッドの認識タイプ#

ゲームパッドのボタンの配置や名称は製品によって異なっており、統一された規格はありません。MGLではこの点をある程度カバーするために、そのゲームパッドがどのタイプのデバイスであるかを取得するための仕組みがあります。

ゲームパッドの認識タイプはMGL::Input::Gamepad::GetTypeにて取得可能です。戻り値の内容はMGL::Input::PadTypeの説明を参照してください。

特定のデバイスがどのタイプとして認識するかは実行環境によって異なります。詳細はプラットフォーム別情報を参照してください。

注釈

ゲームロジック内で認識タイプやボタンの別名を直接利用することは推奨されません。これらはUI表示や設定画面での利用にとどめ、機能にボタンを割り当てる仕組みを実装することを推奨します。

決定ボタンとキャンセルボタン#

ゲームパッドにおける決定・キャンセルの標準的な配置は、プラットフォームや地域によって異なっています。MGLではこの問題に柔軟に対応するための仕組みを備えています。

MGL::Input::PadButtonのうち、PadButton::DecidePadButton::Cancelは他のいずれかのボタンと連動してオンになります。この割り当てはゲームパッドの認識タイプによって異なっており、必要に応じて後から変更することも可能です。

デフォルトの割り当ては次の通りです。

認識タイプ

決定ボタン

キャンセルボタン

Disable

MFiExtended

MFiA

MFiB

MFiMicro

MFiA

NintendoSwitch

NintendoA

NintendoB

DualShock4

DSCross

DSCircle

DualSense

DSCross

DSCircle

XboxOne

XInputA

XInputB

GenericHID

Button01

Button02

XInput

XInputA

XInputB

DirectInput

Button01

Button02

Other

割り当てるボタンはMGL::Input::GamepadSettings::SetDecideCancelButtonにて変更可能です。

タッチデバイスの読み取り#

タッチクラス#

タッチデバイスの入力状態を読み取るにはMGL::Input::Touchクラスを用います。

MGLではマルチタッチに対応しており、タッチクラスはそのうちの1つのタッチ状態を読み取るクラスです。取得方法は複数あり、コンストラクタに渡す引数の型によって取得方法を選択できます。

インデックス指定による取得#

タッチ状態は内部でMGL::Input::kMultiTouchCount個の要素数を持つ配列で保持されており、コンストラクタにインデックスを指定することでタッチ状態を取得して初期化します。

タッチ状態はインデックスの小さい未使用の要素に優先的に格納されます。したがって、マルチタッチを使用しないアプリケーションにおいては、読み取る必要があるのはインデックス0の最初の要素のみとなります。

インデックスを指定したタッチクラスの初期化の例を次に示します。

インデックス指定によるタッチクラスの初期化
MGL::Input::Touch touch(0);     // 引数を省略しても 0 を指定したことになる

if (touch.IsEnabled())          // if (touch) のようにbool型へのキャストでもOK
{
    MGL_TRACE("タッチされている");
}

MGL::Input::Touch::IsEnabledはそのクラスが有効であるかを取得する関数であり、実際にタッチされている状態であればこの戻り値がtrueになります。この判定はbool型へのキャストでも同じ結果が得られます。

範囲指定による取得#

タッチクラスは一定範囲内のタッチを取得することも可能です。コンストラクタの引数にMGL::Rectangleで矩形を指定することで、その矩形内がタッチされている場合にその状態を取得します。

範囲指定によるタッチクラスの初期化
// (100, 100) からサイズ (300, 300) の範囲内のタッチを取得
MGL::Input::Touch touch(MGL::Rectangle(100.0f, 100.0f, 300.0f, 300.0f));

if (touch.IsEnabled())
{
    MGL_TRACE("タッチされている");
}

指定した範囲内に複数のタッチが存在している場合、インデックスの最も小さいタッチが優先して取得されます。

識別子による取得#

有効なタッチクラスからは一意の識別子を取得可能で、この値を用いることで継続したタッチの追跡が可能です。識別子はMGL::Input::Touch::GetTouchIDで取得し、この戻り値をコンストラクタに指定することでそのタッチ状態を再取得できます。この識別子はタッチが無効化されるまで、すなわち指が離されるまで有効です。

MGL::Input::TouchIDを用いたタッチの追跡の例を次に示します。最初のタッチの識別子を保存し、そのタッチの開始と終了の際にそれぞれトレースを表示する例は次の通りです。

TouchIDを用いたタッチの追跡
// 識別子を保存するための変数
static auto touchID = MGL::Input::TouchID::Invalid;

// touchID が無効値であれば最初のタッチを認識するまで待つ
if (touchID == MGL::Input::TouchID::Invalid)
{
    MGL::Input::Touch touch;

    // タッチされたらそのタッチの識別子を取得
    if (touch)
    {
        touchID = touch.GetTouchID();
        MGL_TRACE("タッチ開始: TouchID = %d", static_cast<uint32_t>(touchID));
    }
}
// touchID が有効であればそのタッチを追跡
else
{
    MGL::Input::Touch touch(touchID);

    // 指が離されると touchID の寿命が切れ、取得に失敗するようになる
    if (!touch)
    {
        touchID = MGL::Input::TouchID::Invalid;
        MGL_TRACE("タッチ終了");
    }
}

タッチ位置の取得#

タッチした位置を取得するにはMGL::Input::Touch::GetPositionを利用します。この関数の利用例を次に示します。

タッチ位置の取得の例
MGL::Input::Touch touch;

if (touch)
{
    auto touchPosition = touch.GetPosition();
    MGL_TRACE("タッチ位置: %f, %f", touchPosition.x, touchPosition.y);
}

タッチ位置の座標系は、標準では画面表示の物理解像度と一致しています。この動作を変更したい場合はMGL::Input::TouchSettings::SetBoundsで範囲の変更と論理解像度の適用を行ってください。詳細は取得座標への論理解像度の適用にて後述します。

タッチした瞬間の判定#

そのタッチを認識した最初のフレームであるかを知るにはMGL::Input::Touch::IsTouchedFirstを利用します。この関数の利用例を次に示します。

タッチした瞬間を判別する例
// (100, 100) からサイズ (300, 300) の範囲内のタッチを取得
MGL::Input::Touch touch(MGL::Rectangle(100.0f, 100.0f, 300.0f, 300.0f));

if (touch)
{
    MGL_TRACE("範囲内に触れている");        // ... (1)
}

if (touch.IsTouchedFirst())
{
    MGL_TRACE("範囲内でタッチを開始した");  // ... (2)
}

(1) は単純に取得の成否しか判別していないため、範囲外をタッチしてから指を範囲内に移動させても反応してしまいます。一方、(2)はタッチされた最初のフレームのみを判定しているため、最初に範囲内に触れないと反応しません。

リピート間隔の変更#

キーボードおよびゲームパッドのリピート入力の間隔は、それぞれMGL::Input::Keyboard::SetRepeatSettingまたはMGL::Input::GamepadSettings::SetRepeatSettingにて変更可能です。これらの設定はMGL::Input::RepeatSettingに任意のパラメータを設定し、引数に渡すことで適用します。

設定には時間を表す2つのパラメータと、時間の単位を表す値のセットで指定します。時間の指定には、初回のブランク期間を表すfirstBlankと、2回目以降のブランク期間を表すsecondBlankで指定します。これらの値は秒またはフレームの単位で設定し、その単位はtypeに指定します。

ユーザー入力に対するIsARepeat()の出力のタイミング
../_images/repeat_setting.svg

次の例は、キーボードとゲームパッドのリピート設定を初回60フレーム、以降は8フレームおきにする例です。

リピート設定の変更例
// リピート設定を準備
MGL::Input::RepeatSetting repeatSetting;
repeatSetting.type = MGL::Input::RepeatType::Frame;
repeatSetting.firstBlank = 60.0f;
repeatSetting.secondBlank = 8.0f;

// キーボードに適用
MGL::Input::Keyboard keyboard;
keyboard.SetRepeatSetting(repeatSetting);

// ゲームパッドに適用
MGL::Input::GamepadSettings gamepadSettings;
gamepadSettings.SetRepeatSetting(repeatSetting);

firstBlanksecondBlankはfloat型であり、フレーム指定の場合は小数点以下の値は無視されます。

typeMGL::Input::RepeatType::Secondを指定した場合、firstBlanksecondBlankの単位は秒になります。この場合はフレームレートに依存しなくなりますが、最大で1フレームの誤差が生じる場合があります。

MGL初期化後のデフォルト設定は、初回ブランク期間に0.5秒、2回目以降に0.067秒です。

取得座標への論理解像度の適用#

重要

macOSなどでは画面全体のフレームバッファの解像度と実際の出力解像度が異なる場合があります。ここで説明している「物理解像度」は、GPU上のフレームバッファの解像度として捉えてください。

マウスとタッチデバイスが取得する座標は、デフォルトでは描画領域の物理解像度に準じた値となっています。この動作はメインのフレームバッファに直接描画する場合は問題になりませんが、オフスクリーンレンダリングの結果をスケーリングして描画する場合には座標が一致しないという問題が生じます。

例えば、内部解像度が640x480のゲームを作成し、その結果を1920x1080の画面の中央にスケーリングして表示するとします。画面中央部分をクリック(またはタッチ)した場合、期待される結果は(320, 240)の座標ですが、そのままでは得られる座標は(960, 540)となります。

この解像度による座標の差異を解決するためには、あらかじめ入力の基準範囲と論理解像度を指定しておく必要があります。この設定は、マウスの場合はMGL::Input::Mouse::SetBoundsを、タッチデバイスの場合はMGL::Input::TouchSettings::SetBoundsをそれぞれ利用します。

入力の基準範囲は物理解像度に準じた変換元の領域です。先述の例で説明すると、640x480を1920x1080の中央に配置する場合、2.25倍した1440x1080を(240, 0)の座標に配置することになります。この配置する矩形、すなわちMGL::Rectangle(240.0f, 0.0f, 1440.0f, 1080.0f)が入力の基準範囲となります。

論理解像度は変換先のサイズです。今回の例では640x480が論理解像度になります。

640x480を1920x1080にスケーリングする例
../_images/user_input_bounds.svg

この基準範囲と論理解像度をマウスとタッチデバイスに適用する例を次に示します。

入力の基準範囲と論理解像度の適用例
// 基準範囲と論理解像度を定義
MGL::Rectangle bounds(240.0f, 0.0f, 1440.0f, 1080.0f);
MGL::Vector2 logicalSize(640.0f, 480.0f);

// マウスへ適用
MGL::Input::Mouse mouse;
mouse.SetBounds(bounds, logicalSize);

// タッチデバイスへ適用
MGL::Input::TouchSettings touchSettings;
touchSettings.SetBounds(bounds, logicalSize);

この適用によって、マウスとタッチデバイスから取得できる座標はゲーム画面の解像度に準拠したものとなります。

MGLでは基準範囲を元に座標を一旦正規化し、その値に論理解像度を適用して変換しています。マウスとタッチデバイスの座標を取得する各々の関数は、変換過程の正規化した値をそのまま取得することも可能です。詳細はそれぞれのAPIリファレンスを参照してください。

プラットフォーム別情報#

標準構成における各プラットフォーム別の情報です。

Windows#

利用可能なデバイス

キーボード

マウス

ゲームパッド

✔(XInput, DirectInput)

タッチデバイス

ゲームパッドの識別タイプ

デバイス

認識タイプ

XInput準拠のゲームパッド

PadType::XInput

DirectInput準拠のゲームパッド

PadType::DirectInput

WindowsではXInputとDirectInputの双方に対応していますが、動作が干渉してしまうため同時に利用することはできません。XInputのゲームパッドが認識されている場合、DirectInputのゲームパッドは利用不可能となります。

DirectInputは既に公式サポートが終了しているAPIであり、将来利用不可能となる可能性があります。

Xbox One用コントローラーはPadType::XboxOneではなくPadType::XInputとして認識されます。このため、Shareボタンと4つの背面パドルはWindowsでは利用できません。

macOS#

利用可能なデバイス

キーボード

✔(NSEvent, GameControllerフレームワーク)

マウス

ゲームパッド

✔(GameControllerフレームワーク, IOKit)

タッチデバイス

ゲームパッドの識別タイプ

デバイス

認識タイプ

対応バージョン

Apple MFi Program認証の拡張ゲームパッド

PadType::MFiExtended

DualShock4ワイヤレスコントローラー

PadType::DualShock4

11.0

DualSenseワイヤレスコントローラー

PadType::DualSense

11.3

XboxOne用コントローラー

PadType::XboxOne

11.0

その他のHIDゲームパッド

PadType::GenericHID

macOSは2種類のキーボードデリゲートが実装されており、11.0 (Big Sur) 以降はGameControllerフレームワークによる入力取得が利用可能です。未対応のバージョンではNSEventによる通知で入力取得を行っており、この取得方法には左右の修飾キーの区別が行えないという制限があります。

macOS 10.15 (Catalina) 以前のバージョンにおいては、動作が干渉する問題によりPadType::GenericHIDPadType::MFiExtendedは同時に利用することはできません。PadType::MFiExtendedのゲームパッドが認識されている場合、PadType::GenericHIDのゲームパッドは利用不可能となります。この問題はmacOS 11.0 (Big Sur) 以降では解消されており、同時に利用可能です。

PadType::DualShock4PadType::XboxOneは、未対応のバージョンでもPadType::MFiExtendedとして認識可能なバージョンが存在しています。

PadType::XboxOneにおいて、Shareボタンを利用するにはmacOS 12(Monterey)以降が必要になります。

iOS/iPadOS#

利用可能なデバイス

デバイス

対応状況

対応バージョン

キーボード

✔(GameControllerフレームワーク)

14.0

マウス

ゲームパッド

✔(GameControllerフレームワーク)

タッチデバイス

ゲームパッドの識別タイプ

デバイス

認識タイプ

対応バージョン

Apple MFi Program認証の拡張ゲームパッド

PadType::MFiExtended

DualShock4ワイヤレスコントローラー

PadType::DualShock4

14.0

DualSenseワイヤレスコントローラー

PadType::DualSense

14.5

XboxOne用コントローラー

PadType::XboxOne

14.0

PadType::DualShock4PadType::XboxOneは、未対応のバージョンでもPadType::MFiExtendedとして認識可能なバージョンが存在しています。

PadType::XboxOneにおいて、Shareボタンを利用するにはiOS/iPadOS 15.0以降が必要になります。

tvOS#

利用可能なデバイス

デバイス

対応状況

対応バージョン

キーボード

✔(GameControllerフレームワーク)

14.0

マウス

ゲームパッド

✔(GameControllerフレームワーク)

タッチデバイス

ゲームパッドの識別タイプ

デバイス

認識タイプ

対応バージョン

Siri Remote

PadType::MFiMicro

Apple MFi Program認証の拡張ゲームパッド

PadType::MFiExtended

DualShock4ワイヤレスコントローラー

PadType::DualShock4

14.0

DualSenseワイヤレスコントローラー

PadType::DualSense

14.5

XboxOne用コントローラー

PadType::XboxOne

14.0

AppleTVではSiri Remoteが標準的なデバイスとして使用されており、これは仕様上ゲームパッドとして認識されています。AppleTVで利用可能なゲームパッドの最大数は、Siri Remoteも含めて最大3つまでです。

PadType::DualShock4PadType::XboxOneは、未対応のバージョンでもPadType::MFiExtendedとして認識可能なバージョンが存在しています。

PadType::XboxOneにおいて、Shareボタンを利用するにはtvOS 15.0以降が必要になります。