Как обрабатывать несколько нажатий клавиш одновременно с SDL?

знакомлюсь с программированием OpenGL с использованием SDL на Ubuntu с использованием c++. После некоторого осмотра и экспериментов я начинаю понимать. Мне нужен совет по обработке событий клавиатуры с SDL.

У меня есть камера 1-го лица и может ходить fwd, назад, стрейф влево и вправо и использовать мышь, чтобы посмотреть вокруг, что здорово. Вот моя функция processEvents:

void processEvents()
{
    int mid_x = screen_width  >> 1;
int mid_y = screen_height >> 1;
int mpx = event.motion.x;
int mpy = event.motion.y;
float angle_y  = 0.0f;
float angle_z  = 0.0f;

while(SDL_PollEvent(&event))
{
    switch(event.type)
    {
        case SDL_KEYDOWN:
            switch(event.key.keysym.sym)
            {
                case SDLK_ESCAPE:
                    quit = true;
                    break;
                case SDLK_w:
                    objCamera.Move_Camera( CAMERASPEED);
                    break;
                case SDLK_s:
                    objCamera.Move_Camera(-CAMERASPEED);
                    break;
                case SDLK_d:
                    objCamera.Strafe_Camera( CAMERASPEED);
                    break;
                case SDLK_a:
                    objCamera.Strafe_Camera(-CAMERASPEED);
                    break;
                default:
                    break;
            }
            break;

        case SDL_MOUSEMOTION:
            if( (mpx == mid_x) && (mpy == mid_y) ) return;

            SDL_WarpMouse(mid_x, mid_y);

            // Get the direction from the mouse cursor, set a resonable maneuvering speed
            angle_y = (float)( (mid_x - mpx) ) / 1000;      
            angle_z = (float)( (mid_y - mpy) ) / 1000;

            // The higher the value is the faster the camera looks around.
            objCamera.mView.y += angle_z * 2;

            // limit the rotation around the x-axis
            if((objCamera.mView.y - objCamera.mPos.y) > 8)  objCamera.mView.y = objCamera.mPos.y + 8;
            if((objCamera.mView.y - objCamera.mPos.y) <-8)  objCamera.mView.y = objCamera.mPos.y - 8;

            objCamera.Rotate_View(-angle_y);

            break;

        case SDL_QUIT:
            quit = true;
            break;

        case SDL_VIDEORESIZE:
            screen = SDL_SetVideoMode( event.resize.w, event.resize.h, screen_bpp, SDL_OPENGL | SDL_HWSURFACE | SDL_RESIZABLE | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE );
            screen_width = event.resize.w;
            screen_height = event.resize.h;
            init_opengl();
            std::cout << "Resized to width: " << event.resize.w << " height: " << event.resize.h << std::endl;
            break;

        default:
            break;
    }
}
}

теперь, пока это работает, у него есть некоторые ограничения. Самый большой и цель моего вопроса заключается в том, что он, похоже, процесс только последнюю клавишу, которая была нажата. Поэтому, если я держу "С", чтобы идти назад, и нажимаю "d", чтобы стрелять вправо, я в конечном итоге стреляю вправо, но не иду назад.

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

спасибо

5 ответов


хорошим подходом будет написать обработчик клавиатуры ("input"), который будет обрабатывать входные события и сохранять состояние события в какой - то структуре (ассоциативный массив звучит хорошо-ключ[keyCode]).

каждый раз, когда обработчик клавиатуры получает событие "клавиша нажата", он устанавливает ключ как включен (true), а когда он получает событие "клавиша вниз", он устанавливает его как отключено (false).

после этого вы можете проверить множественные ключи сразу без вытягивать события сразу, и вы будете возможность повторного использования клавиатуры по всему кадру, не передавая ее подпрограммам.

некоторый быстрый псевдо-код:

class KeyboardHandler {
    handleKeyboardEvent(SDL Event) {
        keyState[event.code] = event.state;
    }

    bool isPressed(keyCode) {
        return (keyState[keyCode] == PRESSED);
    }

    bool isReleased(keyCode) {
        return (keyState[keyCode] == RELEASED);
    }

    keyState[];
}

...

while(SDL Pull events)
{
    switch(event.type) {
        case SDL_KEYDOWN:
        case SDL_KEYUP:
                keyHandler.handleKeyboardEvent(event);
            break;
        case SDL_ANOTHER_EVENT:
                ...
            break;
    }   
}

// When you need to use it:
if(keyHandler.isPressed(SOME_KEY) && keyHandler.isPressed(SOME_OTHER_KEY))
    doStuff(TM);

SDL отслеживает текущее состояние всех ключей. Вы можете получить доступ к этому состоянию через:

SDL_GetKeyState()

Итак, на каждой итерации вы можете обновлять движения на основе ключевого состояния. Чтобы сделать движение плавным, вы должны обновить величину движения на основе времени, прошедшего между обновлениями.


Если вы используете SDL2, используйте SDL_GetKeyboardState.

пример:

const Uint8 *keyboard_state_array = SDL_GetKeyboardState(NULL);

SDL_PollEvent(&event);

if(event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)
{
    // Move centerpoint of rotation for one of the trees:
    if (keyboard_state_array[SDL_SCANCODE_UP] && !(keyboard_state_array[SDL_SCANCODE_DOWN]))
    {
        --location.y;
    }
    else if (!keyboard_state_array[SDL_SCANCODE_UP] && keyboard_state_array[SDL_SCANCODE_DOWN])
    {
        ++location.y;
    }

    if (keyboard_state_array[SDL_SCANCODE_RIGHT] && !keyboard_state_array[SDL_SCANCODE_LEFT])
    {
        ++location.x;
    }
    else if (!keyboard_state_array[SDL_SCANCODE_RIGHT] && keyboard_state_array[SDL_SCANCODE_LEFT])
    {
        --location.x;
    }
}

вместо того, чтобы смотреть только на события keydown, любое решение, которое будет заботиться о нескольких ключах сразу, должно будет смотреть как на события keydown, так и на события keyup и отслеживать состояние рассматриваемых ключей.

Итак, вместо (псевдокода):

on keydown:
    case left_key:
        object.setMovement(left)
    case forward_key:
        object.setMovement(forward)

вместо этого у вас будет что-то вроде (опять псевдокод):

on keydown:
    case left_key:
        keystates[left] = true
        object.updateMovement(keystates)
    case forward_key:
        keystates[forward] = true
        object.updateMovement(keystates)

on keyup:
    case left_key:
        keystates[left] = false
        object.updateMovement(keystates)
    case forward_key:
        keystates[forward] = false
        object.updateMovement(keystates)

тут updateMovement рутина будет смотреть на keystates и выяснить составное движение на основе состояния всех ключей движения вместе.


использовать SDL_GetKeyState получить состояние клавиатуры