0

XBox 360 コントローラーをポーリングせずに読み取ろうとしています。(正確には、私は Logitech F310 を実際に使用していますが、私の Windows 10 PC はそれを XBox 360 コントローラーとして認識しています。) オーバーラップ I/O を使用して 2 つのスレッドでブロックする、かなり厄介な HID コードをいくつか作成しました。 1 つは HID デバイスから読み取る準備ができているレポートがあることを示し、もう 1 つは UI スレッドが HID スレッドの終了を要求したことを示します。これで問題なく動作しますが、HID ドライバーの動作は XInput とは多少異なります。特に、2 つのトリガーを 1 つの値に統合し、それらの差のみを渡します (プレイヤーの指がコントロールから離れている場合、ゲームは HID 値が 0x80 であることを期待しているという奇妙な主張について)。XInput はこれらを 2 つの異なる値として扱います。これは大きな改善です。また、XInput はハット スイッチを 4 ビットとして報告します。

私にとっての XInput の欠点は、コントローラーが値またはボタンのいずれかを変更するまで、読み取り要求をブロックする方法がないように見えることです。HID デバイスとして、ReadFile 呼び出しはブロックされます (正確には、データが利用可能になるまで WaitForMultipleEvents がブロックされます)。XInput はポーリングを想定しているようです。ゲームの状態を更新するたびにコントローラーをポーリングするように自然に記述されるゲームの場合 (たとえば、新しいビデオ フレームが表示されるたびに 1 回)、これは理にかなっています。しかし、別の目的でコントローラーを使用したい場合 (私は演劇用のアプリケーションに取り組んでいます)、HID API が提供するような純粋な非同期システムが必要になる場合があります。ただし、HID API は 2 つの値トリガーを結合します。

ここで、XInput を使用してデバイスを読み取ると、すべてのコントロールの状態を取得するだけでなく、パケット番号も取得します。MSDN によると、パケット番号は、コントロールの状態が変化した場合にのみ変化します。そうすれば、連続するパケット番号が同じである場合、最初のパケット以降の処理に煩わされる必要はありません。コントローラーの状態が変化していないことがわかっているからです。しかし、あなたはまだポーリングを行っていますが、これは私にはやや下品です。

しかし、興味深いのは、ポーリングの間に大きな遅延 (100 ミリ秒) を入れると、値コントロール (トリガーまたはスティック) が移動しているときにパケット数が 1 よりも多く増加することがわかります。これは、デバイスがポーリングを待たずにパケットを送信していること、およびポーリングするたびに最新のパケットしか取得していないことを示唆していると思います。その場合、まったくポーリングする必要はなく、パケットが送信されるまでブロックし、送信されたときにのみ反応できるようにする必要があるようです。しかし、これがオプションであるという兆候は見つかりません。HID APIでブロックできるので、やってみずに諦めたくない(ここでアドバイスを求めることも含めて)。

コントローラー用の独自のドライバーを作成する以外に (独自のドキュメントがなければオプションであるかどうかもわかりません)、重複する I/O (またはその他のブロッキング方法) を使用して XBox 360 コントローラーを読み取る方法を知っている人はいますか?トリガーを個別の値として、ハットを 4 つのボタンとして XInput が行う方法は?

以下は、コントローラーを読み取り、読み取り間でパケット番号が複数回ジャンプできることを示す、私が書いたコードです。

#include <Windows.h>
#include <Xinput.h>
#include <stdio.h>

#define MAX_CONTROLLERS 4

int main()
{
    DWORD userIndex;

    XINPUT_STATE xs;
    XINPUT_VIBRATION v;

    XInputEnable(TRUE);

    // Which one are we?

    for (userIndex = 0; userIndex < XUSER_MAX_COUNT; ++userIndex)
        if (XInputGetState(userIndex, &xs) == ERROR_SUCCESS)
            break;

    if (userIndex == XUSER_MAX_COUNT)
    {
        printf("Couldn't find an Xbox 360 controller.\n");
        getchar();
        return -1;
    }

    printf("Using controller #%1d.\n", userIndex);

    while (TRUE)
    {
        DWORD res = XInputGetState(userIndex, &xs);

        printf("%5d %6d: %3d %3d %3d %3d %3d %3d 0x%04X\n",
                res,
            xs.dwPacketNumber,
            xs.Gamepad.bLeftTrigger & 0xFF,
            xs.Gamepad.bRightTrigger & 0xFF,
            xs.Gamepad.sThumbLX & 0xFF,
            xs.Gamepad.sThumbLY & 0xFF,
            xs.Gamepad.sThumbRX & 0xFF,
            xs.Gamepad.sThumbRY & 0xFF,
            xs.Gamepad.wButtons);

        if (xs.Gamepad.wButtons == 0x000F) // mash down the hat
            break;

        Sleep(100);
    }

    getchar();
    return 0;
}

DirectInput はトリガーを 1 つの値に結合するため、あまり役に立たないことに注意してください。

ありがとう!

4

1 に答える 1

1

これに利点があるかどうかはわかりませんが、定期的にポーリングし、状態が変化したときにセマフォ (またはその他のシグナル) を設定するスレッドを作成できますか? 次に、メイン スレッドがポーリング スレッドからのシグナルの待機をブロックする可能性があります。しかし、一部のコントローラーでは、サムスティックを動かしたかどうかにかかわらず、フレームごとにサムスティックの値がわずかに変化するため、このシステムには利点がない可能性があります。(ノイズ) もちろん、小さな変更を無視して、大きな変更が発生したときにのみセマフォに通知することもできます。

于 2016-08-24T00:31:36.577 に答える