3

さて、私は非推奨のDirectInputの使用を避けようとしています。

しかし、ゲームの「フレーム」または「反復」ごとに、それに応じて行動できるように、すべての主要な状態を奪う必要があります。たとえば、プレーヤーがVK_RIGHTキーを押している場合、そのフレーム上でsmidgenだけを右に移動します。

WM_INPUTメッセージの問題は、ゲームループの記述方法が原因で、フレームごとに予測できない回数表示される可能性があることです。

    MSGメッセージ;
    while(1)
    {{
        if(PeekMessage(&message、NULL、0、0、PM_REMOVE))
        {{
            if(message.message == WM_QUIT)
            {{
                壊す ; //WM_QUIT時に保釈
            }
            
            TranslateMessage(&message);
            DispatchMessage(&message);
        }
        そうしないと
        {{
            //メッセージがないので、ゲームを実行します。
            アップデート() ;
            描く() ;
        }
    }

したがって、複数のWM_INPUTメッセージがそこにスタックされている場合、それらはすべてUpdate()/ Draw()の前に処理されます。

BOOLの配列を使用して、どのキーがダウンしていたかを記憶することで、この問題を解決しました。

    bool array_of_keys_that_are_down [256];

    ケースWM_INPUT:
        if(そのキーボード入力)
        {{
            array_of_keys_that_are_down [VK_CODE] = TRUE;
        }

Update()関数がチェックするため、これは正常に機能します

    void Update()
    {{
        if(array_of_keys_that_are_down [VK_RIGHT])
        {{
            //プレーヤーを少し右に動かします
        }
    }

しかし、問題は、WM_INPUTメッセージが十分な頻度で生成されないことです。プレーヤーがずっと指を下に置いていたとしても、VK_RIGHTの最初の押下とその後のVK_RIGHTメッセージの間に約1秒の遅延があります。これは、DirectInputとは異なりますkeyboard->GetDeviceState( 256, (void*)array_of_keys_that_are_down );(1回の呼び出しで各フレームのすべてのキー状態をスナッチアウトします)

だから私は迷子になりました。監視する必要のある各キーに対してGetAsyncKeystate()関数呼び出しを使用する以外に、フレームごとにすべてのキー状態を確実に取得できない場合は、DirectInputの使用を回避する方法はありません。

DirectInputはこの問題に対する非常に優れた解決策であるように思われますが、非推奨になった場合は、Win32APIのみを使用してこれを便利に行う方法が本当に必要です。

現在array_of_keys_that_are_down、すべてのFALSEのすべてのフレームにリセットされます。

    memset(array_of_keys_that_are_down、0、sizeof(array_of_keys_that_are_down));

*編集

私はこの問題に取り組んできましたが、1つの解決策は、キーの状態が解放されたら、それをリセットすることだけです。

    ケースWM_INPUT:
        if(そのキーボード入力)
        {{
            if(そのダウンプレス)
                array_of_keys_that_are_down [VK_CODE] = TRUE;
            そうしないと
                array_of_keys_that_are_down [VK_CODE] = FALSE;
        }

それは薄っぺらなように見えるので、私はこの解決策が好きではありません。ユーザーがキーを押しているときにアプリケーションから離れると、アップストロークWM_INPUTメッセージが表示されないため、ユーザーがスイッチを戻して同じキーをもう一度押すまで、そのキーは「スタック」します。それは奇妙な「スティッキーキー」のバグになります。

4

5 に答える 5

7

代わりに使用できますGetKeyboardState。一般的に必要なのは2つの配列です。1つは前のフレームの入力状態を格納し、もう1つは現在のフレームを格納します。これにより、保留とトリガーの区別などが可能になります。

// note, cannot use bool because of specialization
std::vector<unsigned char> previous(256);
std::vector<unsigned char> current(256);

// in update_keys or similar:
current.swap(previous); // constant time, yay
GetKeyboardState(&current[0]); // normally do error checking

これで完了です。

于 2010-02-14T21:02:02.017 に答える
1

提示されたソリューションはそれを行う正しい方法です-自動リピートを無視し、ダウン/アップ状態を記録するだけです。

タスク切り替えの問題を処理するには、WM_ACTIVATEメッセージを調べます。これにより、ウィンドウのフォーカスが失われたことを検出できます。これが関連するウィンドウで発生した場合、すべてのキーが解放されたと想定します。(これは、非排他的な協調レベルを使用する場合にDirectInputで行う必要があることと似ています。)

于 2010-02-14T20:36:52.077 に答える
0

遅延があるとおっしゃっていましたが、遅延を減らしたいという印象を受けました。タイプマティック遅延と速度レートを設定するために「SystemParametersInfo」を呼び出してみませんかキーボードの遅延を確認する必要があります。「SPI_GETKEYBOARDDELAY」およびキーボード速度'SPI_GETKEYBOARDSPEED'。関数は次のようになります。

int SetKeyboardSpeed(int nDelay){
   /*最も速いnDelay=31、最も遅いnDelay = 0 * /
   return(SystemParametersInfo(SPI_SETKEYBOARDSPEED、nDelay、NULL、SPIF_SENDCHANGE)> 0);
}
int SetKeyboardDelay(int nDelay){
   / * 0 =最短(約250ms)から最長3(約1秒)* /
   return(SystemParametersInfo(SPI_SETKEYBOARDDELAY、nDelay、NULL、SPIIF_SENDCHANGE)> 0);
}

編集:反対票に対するBlindyのコメントに応えて-あなたは100%正しい-私がこれにコードを入力したときに気づかなかった...そしてはい、あなたはそれに指を置いた、決してグローバル/システムを変更しない-ユーザーが知らないうちに広い設定!私はブリンディのコメントによって訂正されたままです。私の答えは100%間違っているので無視してください!

これがお役に立てば幸いです、よろしく、トム。

于 2010-02-14T17:32:41.977 に答える
0
  1. ウィンドウメッセージを使用してキーの押下を検出し、押されたキーの配列を自分で追跡します。
  2. WM_ACTIVATEメッセージを監視し、その時点でGetAsyncKeyboardStateを使用して、実際の状態と一致するように配列を「修正」します。

その組み合わせは物事の一貫性を保つはずです。

于 2010-02-15T12:14:00.593 に答える
0

問題に対する最良の(結果の一貫性と効率の点で)解決策は、生の入力を使用することです。イベントベース、高速、効率的で、入力を見逃す可能性はありません。

GetAsyncKeyState呼び出しで入力を見逃す可能性があります。たとえば、60 Hzで動作するゲームの反復の開始時に、GetAsyncKeyStateを呼び出し、キーが押されていないとします。ここまでは順調ですね。次に、5ミリ秒後にキー(たとえばVK_TAB)を押して、5ミリ秒間押し続けます。次に、次のゲームの反復の開始時に(約6.67ミリ秒後)、GetAsyncKeyStateを再度呼び出します。しかし、その時までにキーは再び押されません。そして、ゲームの観点から、それは決して押されませんでした!行き過ぎのように見えるかもしれませんが、そうではありません。このシステムを使用し、60FPSで入力を逃すゲームをプレイしました。イライラして不要。

私はこの問題に取り組んできましたが、1つの解決策は、キーの状態が解放されたら、それをリセットすることだけです。

case WM_INPUT :
    if( its keyboard input )
    {
        if( its a down press )
            array_of_keys_that_are_down[ VK_CODE ] = TRUE ;
        else
            array_of_keys_that_are_down[ VK_CODE ] = FALSE ;
    }

薄っぺらなように見えるので、私はこの解決策が好きではありません。ユーザーがキーを押しているときにアプリケーションから離れると、アップストロークWM_INPUTメッセージが表示されないため、ユーザーがスイッチを戻して同じキーをもう一度押すまで、そのキーは「スタック」します。それは奇妙な「スティッキーキー」のバグになります。

これは、raw入力デバイスを登録するときにRAWINPUTDEVICE構造体のRIDEV_INPUTSINKフラグで修正されます。ウィンドウがフォアグラウンドにない場合でもメッセージを受け取ります。

使用しているAPIに問題がないようです。システムと対話する特定の方法が必要です。

しかし、ゲームの「フレーム」または「反復」ごとに、それに応じて行動できるように、すべての主要な状態を奪う必要があります。たとえば、プレーヤーがVK_RIGHTキーを押している場合、そのフレーム上でsmidgenだけを右に移動します。

WM_INPUTメッセージの問題は、ゲームループの記述方法が原因で、フレームごとに予測できない回数表示される可能性があることです。

BOOLの配列を使用して、どのキーが押されたかを記憶することで、この問題を解決しました。

キーの状態をチェックするときに知りたいので、独自の入力処理レイヤーを作成します。ポーリングベースのシステムが必要なので、KeyboardStateHandlerクラスを作成し、すべてのキープレスとキーリリースの生の入力イベントに応答するようにします。次に、ゲームループで、keyboardStateHandler.GetKeys()を呼び出し、すべてのキーの状態を取得します。

あなたが本当にやりたいのは、適切な堅牢な入力処理レイヤーを作成することだと思います。これは、上記で提起したすべての問題の解決策になります。

ここにいくつかあります:

https://bell0bytes.eu/inputsystem/Web アーカイブリンク

https://www.gamedev.net/blog/355/entry-2250186-designing-a-robust-input-handling-system-for-games/Web アーカイブリンク

https://blog.gemserk.com/2012/08/23/decouple-game-logic-from-input-handling-logic/Web アーカイブリンク

GetKeyAsyncState、DirectInput、Raw Input、またはその他のメソッドなど、実際に入力を取得するために何を使用するかは関係ありません。入力処理をゲームロジックから分離する必要があります。

于 2021-01-29T16:01:00.247 に答える