Tim Tim - 6 months ago 18
Linux Question

How to handle multiple keypresses at once with SDL?

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

Answer

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