views:

701

answers:

4

Hi, been getting myself familiar with OpenGL programming using SDL on Ubuntu using c++. After some looking around and experimenting I am starting to understand. I need advice on keyboard event handling with SDL.

I have a 1st person camera, and can walk fwd, back, strafe left and right and use the mouse to look around which is great. Here is my processEvents function:

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

now while this is working, it has some limitations. The biggest one and the purpose of my question is that it seems to only process the latest key that was pressed. So if I am holding 's' to walk backwards and I press 'd' to strafe right, I end up strafing right but not going backwards.

Can someone point me in the right direction for better keyboard handling with SDL, support for multiple keypresses at once, etc?

Thanks

+2  A: 

Instead of only looking at keydown events, any solution which is going to be caring about multiple keys at once is going to have to be looking at both keydown and keyup events, and keeping track of the state of the keys in question.

So instead of (pseudocode):

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

instead you'd have something more like (again pseudocode):

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

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

Then the updateMovement routine would look at keystates and figure out a composite movement based on the states of all movement keys together.

Amber
+1  A: 

SDL keeps track of the current state of all keys. You can access this state via:

SDL_GetKeyState()

So, each iteration you can update the movements based on the key state. To make the movement smooth you should update the movement magnitude based on the time elapsed between updates.

Karl Voigtland
+2  A: 

A good approach will be to write a keyboard ("input") handler that will process input events and keep the event's state in some sort of a structure (associative array sounds good - key[keyCode]).

Every time the keyboard handler receives a 'key pressed' event, it sets the key as enabled (true) and when it gets a key down event, it sets it as disabled (false).

Then you can check multiple keys at once without pulling events directly, and you will be able to re-use the keyboard across the entire frame without passing it around to subroutines.

Some fast pseudo code:

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);
LiraNuna
A: 

use SDL_GetKeyState to get the keyboard state

hiena