SDL:Tutorials:Practical Keyboard Input

From GPWiki
Jump to: navigation, search

Before you learn anything here, you should know a few things first:


If you have read any other tutorials for game input with SDL, they probably gave you code like this:

SDL_Event keyevent;    //The SDL event that we will poll to get events.
 
while (SDL_PollEvent(&keyevent))   //Poll our SDL key event for any keystrokes.
{
  switch(keyevent.type){
    case SDL_KEYDOWN:
      switch(keyevent.key.keysym.sym){
        case SDLK_LEFT:
          charx -= 1;
          break;
        case SDLK_RIGHT:
          charx += 1;
          break;
        case SDLK_UP:
          chary -= 1;
          break;
        case SDLK_DOWN:
          chary += 1;
          break;
        default:
          break;
      }
    }
  }
}

What this code does, obviously, is detect keystrokes and adjust the characters position based upon which key was pressed.

Now, this is fine for some types of games. An example would be an RPG. When the player presses a key, this recognizes it and moves the character. But what about for more "actiony" games, where the character (Or whatever else is being moved around) is moving faster? You could put this code in a game like that, and it has been done. But what is frequently misunderstood is that keyboard events only occur when a key is first depressed or let up. That can't be stressed enough.

Because of this, when the user holds down an arrow key in a game with this code in it, expecting to keep on moving until they let up on the key, will only move one unit. Only one event is made, and only one event is acted upon. To keep our character moving for as long as the key is pressed, we have to implement a few variables that will keep track of what's going on:

SDL_Event keyevent;    //The SDL event that we will poll to get events.
 
while (SDL_PollEvent(&keyevent))   //Poll our SDL key event for any keystrokes.
{
  switch(keyevent.type){
    case SDL_KEYDOWN:
      switch(keyevent.key.keysym.sym){
        case SDLK_LEFT:
          charxvel = -1;
          break;
        case SDLK_RIGHT:
          charxvel = 1;
          break;
        case SDLK_UP:
          charyvel = -1;
          break;
        case SDLK_DOWN:
          charyvel = 1;
          break;
        default:
          break;
       }
       break;
    case SDL_KEYUP:
      switch(keyevent.key.keysym.sym){
        case SDLK_LEFT:
          charxvel = 0;
          break;
        case SDLK_RIGHT:
          charxvel = 0;
          break;
        case SDLK_UP:
          charyvel = 0;
          break;
        case SDLK_DOWN:
          charyvel = 0;
          break;
        default:
          break;
      }
  }
}
 
charx += charxvel;
chary += charyvel;

The above code introduces a few new variables: charxvel and charyvel. When a key is pressed, these velocity variables are adjusted so that if you added the character positions to them, we would have the normal new position we would get from the first code example. What makes this work is the lines saying: charx += charxvel; and chary += charyvel;. These lines actually change the character positions according just like was in the sentence before. What's so special about these is that they are not inside any event structures, and so are called on every execution of the main loop. This causes the characters position to be adjusted for the duration of the keypress, not just once at the beginning of the press. When the key is let up, the velocity variable is set to zero to stop the character's movement.

Another good part of this design is that the player can have two arrow keys depressed at once, and have the character moving in an according diagonal direction.

So here we have a nice input system, ready for use. It works fine for most games, but what if we want to add a little bit extra interest to it? Maybe something like a system of acceleration, perhaps? Such that as long as the user is holding down the key, the velocity will increase. I'll save you the trouble of thinking how to do this:

SDL_Event keyevent;    //The SDL event that we will poll to get events.
 
bool upup;             //Whether the up key is up.
bool downup;           //Likewise for the down key.
bool leftup;           //You get the drift.
bool rightup; 
 
upup = true;           //Initialize all the key booleans to true.
downup = true;
leftup = true;
rightup = true;
 
int charyvel;
int charxvel;
 
while (SDL_PollEvent(&keyevent))   //Poll our SDL key event for any keystrokes.
{
  if (keyevent.type == SDL_KEYDOWN)
  {
    switch(keyevent.key.keysym.sym){
      case SDLK_LEFT:
        leftup = false;
        charxvel = -1;
        break;
      case SDLK_RIGHT:
        rightup = false;
        charxvel = 1;
        break;
      case SDLK_UP:
        upup = false;
        charyvel = -1;
        break;
      case SDLK_DOWN:
        downup = false;
        charyvel = 1;
        break;
      default:
        done=1;
        break;
    }
  }
  if (keyevent.type == SDL_KEYUP)
  {
    switch(keyevent.key.keysym.sym){
      case SDLK_LEFT:
        leftup = true;
        charxvel = 0;
        break;
      case SDLK_RIGHT:
        rightup = true;
        charxvel = 0;
        break;
      case SDLK_UP:
        upup = true;
        charyvel = 0;
        break;
      case SDLK_DOWN:
        downup = true;
        charyvel = 0;
        break;
      default:
        break;
    }
  }
}
if (!upup) charyvel -= 1;  //These continue to accelerate the character if the user is holding down a button.
if (!downup) charyvel += 1; 
if (!rightup) charxvel += 1;
if (!leftup) charxvel -= 1;
 
charx += charxvel;
chary += charyvel;

As you can see, this code introduces four more variables, booleans that keep track of what keys have been pressed. When the key is pressed down, the corresponding boolean is set to false and when it is let up, the boolean is set to true. After the event polling loop, we have new code that looks at these new booleans, and if they are still down, it accelerates the character further. Just as before, the lines under that move the character according to its velocity.

There are many other little effects that you could use to add interest to your input system. You might try removing the charxvel = 0; lines from the inside of the SDL_KEYUP case, which would add an interesting effect where nothing decelerates and the user has to counteract the movement with further key presses. Another interesting experiment which would add a bit of realism to the scheme laid out here would be adding a system of deceleration to slow down the character's movement when the key is up and the velocity variables aren't zero.

Hopefully all of this is easy to understand. If not, then don't hesitate to post any questions in the forum. My cohorts or I will be sure to help you.

Much of the code and concepts in this tutorial was taken from Eat.