2009-08-10 14 views
9

me he familiarizado con la programación de OpenGL usando SDL en Ubuntu usando C++. Después de mirar alrededor y experimentar estoy empezando a comprender. Necesito consejos sobre el manejo de eventos de teclado con SDL.¿Cómo manejar múltiples pulsaciones de teclas a la vez con SDL?

Tengo una cámara en 1ra persona, y puedo caminar hacia adelante, atrás, atacar a izquierda y derecha y usar el mouse para mirar alrededor, lo cual es genial. Aquí está mi función 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; 
    } 
} 
} 

ahora que esto está funcionando, tiene algunas limitaciones. El más grande y el propósito de mi pregunta es que parece que solo procesa la última clave que se presionó. Entonces, si sostengo 's' para caminar hacia atrás y presiono 'd' para atacar a la derecha, termino ametrallando a la derecha pero no retrocediendo.

¿Puede alguien señalarme en la dirección correcta para un mejor manejo del teclado con SDL, soporte para múltiples pulsaciones de teclas a la vez, etc.?

Gracias

Respuesta

12

Un buen enfoque será escribir un controlador de teclado ("entrada") que procesará eventos de entrada y mantendrá el estado del evento en algún tipo de estructura (matriz asociativa suena bien - tecla [código clave]).

Cada vez que el controlador de teclado recibe un evento 'tecla presionada', establece la clave como habilitada (verdadera) y cuando obtiene un evento de pulsación de tecla, lo establece como deshabilitado (falso).

Luego puede verificar varias claves a la vez sin tirar eventos directamente, y podrá reutilizar el teclado en todo el marco sin pasarlo a las subrutinas.

Algunos pseudo código rápido:

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); 
+0

Realmente una buena solución, esto se puede utilizar en casi todos los juegos – devsaw

3

En lugar de mirar sólo los eventos KeyDown, cualquier solución que se va a preocuparse por varias teclas a la vez va a tener que estar mirando a ambos eventos KeyDown y keyup, y hacer el seguimiento del estado de las llaves en cuestión.

Así que en lugar de (pseudocódigo):

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

vez que tendríamos algo más parecido (de nuevo pseudocódigo):

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) 

A continuación, la rutina updateMovement miraría keystates y encontrar una movimiento compuesto basado en los estados de todas las teclas de movimiento juntas.

16

SDL realiza un seguimiento del estado actual de todas las teclas. Se puede acceder a través de este estado:

SDL_GetKeyState()

Por lo tanto, cada iteración puede actualizar los movimientos basados ​​en el estado clave. Para que el movimiento sea fluido, debe actualizar la magnitud del movimiento en función del tiempo transcurrido entre las actualizaciones.

3

Si está utilizando SDL2 a continuación, utilizar SDL_GetKeyboardState.

Ejemplo:

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