GetRawInputData против GetAsyncKeyState () - PullRequest
2 голосов
/ 14 февраля 2010

Ну, я пытаюсь избежать использования устаревшего DirectInput.

Но мне нужно, чтобы на каждом «кадре» или «итерации» игры хватало ВСЕ КЛЮЧЕВЫЕ СОСТОЯНИЯ, чтобы я мог действовать соответственно . Например, если игрок нажал на кнопку VK_RIGHT, он просто переместится на этот кадр прямо на пятно.

Проблема с сообщениями WM_INPUT заключается в том, что они могут появляться непредсказуемым числом раз за кадр из-за способа написания игрового цикла:

    MSG message ;
    while( 1 )
    {
        if( PeekMessage( &message, NULL, 0, 0, PM_REMOVE ) )
        {
            if( message.message == WM_QUIT )
            {
                break ;  // bail when WM_QUIT
            }

            TranslateMessage( &message ) ;
            DispatchMessage( &message ) ;
        }
        else
        {
            // No messages, so run the game.
            Update() ;
            Draw() ;
        }
    }

Так что, если более одного сообщения WM_INPUT помещены туда, то все они будут обработаны перед Update () / Draw ().

Я решил эту проблему, используя массив BOOL, чтобы запомнить, какие клавиши были недоступны:


    bool array_of_keys_that_are_down[ 256 ] ;

    case WM_INPUT :
        if( its keyboard input )
        {
            array_of_keys_that_are_down[ VK_CODE ] = TRUE ;
        }

Это прекрасно работает, потому что функция Update () проверяет


    void Update()
    {
        if( array_of_keys_that_are_down[ VK_RIGHT ] )
        {
            // Move the player right a bit
        }
    }

НО проблема в том, что сообщения WM_INPUT не генерируются часто достаточно . Между первым нажатием VK_RIGHT и последующими сообщениями VK_RIGHT существует задержка около 1 секунды, даже если игрок все время держал на нем палец. Это не похоже на DirectInput, где вы можете keyboard->GetDeviceState( 256, (void*)array_of_keys_that_are_down ); (выхватить все состояния клавиш в каждом кадре одним вызовом)

Так что я потерян. Кроме обращения к функциям GetAsyncKeystate () для каждой клавиши, которую мне нужно отслеживать, я не вижу способа избежать использования DirectInput, если вы не можете надежно выхватить все состояния клавиш в каждом кадре.

Мне кажется, что DirectInput был очень хорошим решением для этой проблемы, но если он устарел, то действительно должен быть какой-то способ сделать это удобно, используя только Win32 API.

В настоящее время array_of_keys_that_are_down возвращается к каждому FALSE для каждого кадра.

    memset( array_of_keys_that_are_down, 0, sizeof( array_of_keys_that_are_down ) ) ;

* EDIT

Я работал над этой проблемой, и одно из решений - сбросить состояние ключа только после его отпускания

    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 ;
        }

Мне не нравится это решение, потому что оно кажется хлипким . Если пользователь выключается из приложения, нажимая клавишу, эта клавиша будет «зависать» до тех пор, пока он не переключится назад и снова не нажмет ту же клавишу, потому что мы никогда не получим сообщение upstroke WM_INPUT . Это делает для странных ошибок "липкий ключ".

Ответы [ 4 ]

6 голосов
/ 15 февраля 2010

Вместо этого можно использовать GetKeyboardState. То, что вы обычно хотите, это два массива; один хранит состояние ввода предыдущих кадров, а другой - текущий. Это позволяет проводить различие между удержанием и срабатыванием.

// 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

И все готово.

1 голос
/ 14 февраля 2010

Представленное решение является правильным способом сделать это - игнорировать автоповтор и просто записывать состояния «вниз / вверх».

Чтобы справиться с проблемой переключения задач, посмотрите сообщение WM_ACTIVATE - оно позволяет определить, когда окно теряет фокус. Когда это происходит с соответствующим окном, предположим, что все ключи отпущены. (Это похоже на то, что нужно делать с DirectInput при использовании неэксклюзивного кооперативного уровня.)

0 голосов
/ 15 февраля 2010
  1. использовать оконные сообщения для обнаружения нажатия клавиш и отслеживать массив нажал клавиши сам.
  2. часы для сообщение WM_ACTIVATE и использовать GetAsyncKeyboardState в этой точке «исправить» массив, чтобы соответствовать фактическое состояние.

Эта комбинация должна сохранять последовательность.

0 голосов
/ 14 февраля 2010

Поскольку вы сказали, что есть задержка, у меня сложилось впечатление, что вы хотите уменьшить задержку, почему бы не вызвать ' SystemParametersInfo ', чтобы установить типичную задержку и скорость, вам нужно будет посмотреть при задержке клавиатуры .. 'SPI_GETKEYBOARDDELAY' и скорости клавиатуры 'SPI_GETKEYBOARDSPEED'. Функция будет выглядеть так:

int SetKeyboardSpeed(int nDelay){
   /* fastest nDelay = 31, slowest nDelay = 0 */
   return (SystemParametersInfo(SPI_SETKEYBOARDSPEED, nDelay, NULL, SPIF_SENDCHANGE) > 0);
}
int SetKeyboardDelay(int nDelay){
   /* 0 = shortest (approx 250ms) to 3 longest (approx 1sec) */
   return (SystemParametersInfo(SPI_SETKEYBOARDDELAY, nDelay, NULL, SPIIF_SENDCHANGE) > 0);
}

Редактировать: В ответ на комментарий Blindy для downvote - Вы на 100% правы - Никогда не осознавал, что, когда я набрал код в этом ... и да, вы указали на это, Никогда не меняйте глобальные / общесистемные настройки, пока пользователь об этом не знает! Я исправлен комментариями Блинди. Пожалуйста, не обращайте внимания на мой ответ, так как это 100% неправильно !

Надеюсь, это поможет, С наилучшими пожеланиями, Том.

...