Check Key Symbol:
If the 'type' field of our SDL_Event variable equals SDL_KEYDOWN, this simply means a key on the keyboard was pressed. To determine which key was pressed, there is another field called 'key', but since SDL gathers a lot of input information, we need the sub-field 'keysym' of the field 'key'. However, we still need the specific symbol of the key pressed, so the field 'sym' is used.
Like 'type', 'sym' is an enumeration variable which can be compared against SDL's defined enumerations. In this case, the name of the enumeration variable type is SDLKey. The escape key is given the title SDLK_ESCAPE, or a value of 27, so to check if escape was pressed we simply compare the 'sym' sub-field against SDLK_ESCAPE. The 'sym' sub-field will appear as input.key.keysym.sym for the following example:
The window should now close when the close or quit button is clicked but also when the escape key is pressed. Since the user now has a choice, the text output in stdout can have multiple outcomes. For this example, the escape key was pressed, but had the close button been clicked, the prompt would read "Close button clicked". For examples where output can vary, I recommend trying to get several, different outcomes (since I will only show one outcome).
The comparison for the outer while loop was changed for this example. The new comparison will only exit when loop is set to 0, but since loop is only ever set to 1 or 0, neither comparison is better. Remember that using 0 and 1 is just a suggestion, you're welcome to check while (loop != 42) {, just remember to set loop to 42 in order to exit.
Managing Presses and Releases:
For interactive applications, we may want buttons that perform actions several times (unlike exiting which is done once). The question is whether you want actions to occur once each time the key is pressed (like opening and closing a menu) or actions to continuously occur while the key is pressed (like walking in a video game). Each situation has a solution.
If you were to hold the escape key from the previous example, the actions would still only be performed once. All we need for one-press keys is to test for SDL_KEYDOWN. If the key you are looking for is pressed, you can place commands in the if statement (like loop = 0; or the printf shown above). However, specifying commands for each tested key can create a large inner while loop, so I recommend representing key presses with variables.
As an example, say we use an int variable called 'escape_key' which is initially set to 0. Once the escape key is pressed (SDL_KEYDOWN event type and the pressed key equals SDLK_ESCAPE), we set 'escape_key' to 1. One problem now is that it will stay at 1 until assigned a different value, so we set the value to zero at the beginning of each program cycle (outer while loop). This variable now represents whether the escape key was pressed during that cycle.
For keys which are held, we still set a variable to 1 if the key is pressed. The difference is that we don't assign this variable 0 each frame; we only assign it 0 during an SDL_KEYUP event type. The SDL_KEYUP type is similar to SDL_KEYDOWN except that it detects whenever a key is released. This means our variable will be 1 until the key is released.
The following example will draw colored rectangles if either the r, g, or b keys are pressed:
/*thanks to tohtml.com for syntax highlighting*/ #include <SDL/SDL.h> #include <stdio.h> SDL_Surface *screen; /*visible surface*/ SDL_Event input; /*holds input*/ int loop = 1; /*set to zero to exit*/ SDLKey pressed; /*holds symbol of pressed key*/ int r_key = 0, g_key = 0, b_key = 0; /*1 when pressed*/ Uint32 red, green, blue; /*colors for rectangle*/ int main(int argc, char** argv) { SDL_Rect block; /*Rectangle to cover screen*/ block.x = 0; block.y = 0; block.w = 640; block.h = 480; /*start up SDL and setup window*/ SDL_Init(SDL_INIT_EVERYTHING); screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); /*setup colors after screen initialized*/ red = SDL_MapRGB(screen->format, 255, 0, 0); green = SDL_MapRGB(screen->format, 0, 255, 0); blue = SDL_MapRGB(screen->format, 0, 0, 255); /*loop is needed to keep window open*/ while (loop != 0) { /*set non-held keys to zero each cycle*/ g_key = 0; /*read all events per cycle*/ while (SDL_PollEvent(&input)) { /*check for close button clicks*/ if (input.type == SDL_QUIT) {loop = 0;} /*check for key presses*/ if (input.type == SDL_KEYDOWN) { pressed = input.key.keysym.sym; if (pressed == SDLK_ESCAPE) {loop = 0;} if (pressed == SDLK_r) {r_key = 1;} if (pressed == SDLK_g) {g_key = 1;} if (pressed == SDLK_b) {b_key = 1;} } /*check for key releases*/ if (input.type == SDL_KEYUP) { pressed = input.key.keysym.sym; /*set held keys to zero*/ if (pressed == SDLK_r) r_key = 0; if (pressed == SDLK_b) b_key = 0; } } /*draw rectangle if key is pressed*/ if (r_key == 1) { printf("Drawing red rectangle\n"); SDL_FillRect(screen, &block, red); } if (g_key == 1) { printf("Drawing green rectangle\n"); SDL_FillRect(screen, &block, green); } if (b_key == 1) { printf("Drawing blue rectangle\n"); SDL_FillRect(screen, &block, blue); } SDL_Delay(20); /*wait 20ms*/ SDL_Flip(screen); /*update screen*/ } /*perform final commands then exit*/ SDL_Quit(); /*shutdown SDL*/ fflush(stdout); /*update stdout*/ return 0; } | ||
prompt/stdout: Drawing red rectangle Drawing red rectangle Drawing red rectangle Drawing red rectangle Drawing red rectangle Drawing green rectangle Drawing blue rectangle Drawing blue rectangle Drawing blue rectangle Drawing blue rectangle Drawing green rectangle | ||
output inside window:
|
For this example, I held the r key for 5 frames, pressed the g key once, held the b key for 4 keys, and pressed the g key once more. The final result was a green rectangle which is the size of the visible surface. I held the g key for longer than one frame, but only the pressing was detected. There are many more possible outcomes, so feel free to test this example.
To detect the r, g, and b keys the enumerations SDLK_r, SDLK_g, and SDLK_b are used. This works for other letters (SDLK_c for c) and numbers (SDLK_3 for 3), but for symbols and non-visible inputs (like backspace) the enumeration is spelled out (like SDLK_BACKSPACE). Also, the program still exits when escape is pressed, but the escape key is not given a variable like the r, g, and b keys.
The example tests for SDL_KEYDOWN events and will change the variables r_key, g_key, and b_key (integers set to zero) to the value 1 when the r, g, or b key is pressed. When an SDL_KEYUP event occurs, we check for whether the r or b key is released and set r_key or b_key to 0 since they are keys to be held. These variables can now be used to check whether the r or b key is currently held. The variable g_key is set to 0 each frame since it only detects initial presses.
Using Arrays:
Now imagine creating around one hundred integers, one for each key, and checking them all. fortunately, this is exactly what arrays are for. SDL even has a key enumeration called SDLK_LAST which is a count of all the keys it handles. The array will look something like "int key_array[SDLK_LAST];". Of course, we need to set each index to 0 before proceeding since these values are not initialized, so in main we place the commands "int key_index;" and "for (key_index = 0; key_index < SDLK_LAST; key_index += 1) {key_array[key_index] = 0;}".
Now, we can set an int from the array to 1 whenever an SDL_KEYDOWN event occurs with the command "key_array[input.key.keysym.sym] = 1;". We can also do this for when the key is released with "key_array[input.key.keysym.sym] = 0;" when SDL_KEYUP type occurs. For keys with one-press detection, we still need to set them individually to 0, like "key_array[SDLK_g] = 0;" which will set the 103rd index (reserved for the g key) to 0 each cycle.
Here is an example of an array which we must manage:
/*thanks to tohtml.com for syntax highlighting*/ #include <SDL/SDL.h> #include <stdio.h> SDL_Surface *screen; /*visible surface*/ SDL_Event input; /*holds input*/ int loop = 1; /*set to zero to exit*/ int key_array[SDLK_LAST]; /*array of keys (0 or 1)*/ Uint32 color_1, color_2, color_3; /*colors for rectangle*/ int main(int argc, char** argv) { int key_index; SDL_Rect block; /*Rectangle to cover screen*/ block.x = 0; block.y = 0; block.w = 640; block.h = 480; for (key_index = 0; key_index < SDLK_LAST; key_index += 1) { key_array[key_index] = 0; /*set each value to 0 initially*/ } /*start up SDL and setup window*/ SDL_Init(SDL_INIT_EVERYTHING); screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); /*setup colors after screen initialized*/ color_1 = SDL_MapRGB(screen->format, 240, 128, 128); color_2 = SDL_MapRGB(screen->format, 50, 205, 50); color_3 = SDL_MapRGB(screen->format, 135, 206, 250); /*loop is needed to keep window open*/ while (loop != 0) { /*set to zero for one-press detection*/ key_array[SDLK_3] = 0; /*read all events per cycle*/ while (SDL_PollEvent(&input)) { /*check for close button clicks*/ if (input.type == SDL_QUIT) {loop = 0;} /*check for key presses*/ if (input.type == SDL_KEYDOWN) { key_array[input.key.keysym.sym] = 1; } /*check for key releases*/ if (input.type == SDL_KEYUP) { key_array[input.key.keysym.sym] = 0; } } /*exit if escape pressed*/ if (key_array[SDLK_ESCAPE] == 1) loop = 0; /*draw rectangle if key is pressed*/ if (key_array[SDLK_1] == 1) { printf("Drawing rectangle of color_1\n"); SDL_FillRect(screen, &block, color_1); } if (key_array[SDLK_2] == 1) { printf("Drawing rectangle of color_2\n"); SDL_FillRect(screen, &block, color_2); } if (key_array[SDLK_3] == 1) { printf("Drawing rectangle of color_3\n"); SDL_FillRect(screen, &block, color_3); } SDL_Delay(20); /*wait 20ms*/ SDL_Flip(screen); /*update screen*/ } /*perform final commands then exit*/ SDL_Quit(); /*shutdown SDL*/ fflush(stdout); /*update stdout*/ return 0; } | ||
prompt/stdout: Drawing rectangle of color_1 Drawing rectangle of color_1 Drawing rectangle of color_1 Drawing rectangle of color_1 Drawing rectangle of color_2 Drawing rectangle of color_2 Drawing rectangle of color_2 Drawing rectangle of color_3 Drawing rectangle of color_1 Drawing rectangle of color_1 Drawing rectangle of color_1 | ||
output inside window:
|
I held the 1 key for 4 frames, held the 2 key for 3 frames, pressed the 3 key once, and finally held the 1 key again for 3 frames. This resulted in a light coral rectangle which takes up the entire screen. The values of key_array at SDLK_1 and SDLK_2 (1 and 2 keys) detect held keys, while the SDLK_3 value (3 key) only detects presses.
SDL can save us a few steps and give us a pointer to its own updated array. The output of SDL_GetKeyState is an array of Uint8 variables which equal 0 or 1, and its input is a pointer to an int. The page http://lazyfoo.net/SDL_tutorials/lesson10/index.php (written in the similar language C++) provides a great example of managing keys, but also says we only need to pass NULL (or 0) as an argument. A pointer of NULL refers to the first address in memory which is restricted by most operating systems, so it often means the pointer is unused.
Here is how dramatic a difference it can make for managing keys:
/*thanks to tohtml.com for syntax highlighting*/ #include <SDL/SDL.h> #include <stdio.h> SDL_Surface *screen; /*visible surface*/ SDL_Event input; /*holds input*/ int loop = 1; /*set to zero to exit*/ Uint8* sdl_key_array; /*holds states of all keys, automatically*/ Uint32 color_1, color_2, color_3; /*colors for rectangle*/ int main(int argc, char** argv) { SDL_Rect block; /*Rectangle to cover screen*/ block.x = 0; block.y = 0; block.w = 640; block.h = 480; /*start up SDL and setup window*/ SDL_Init(SDL_INIT_EVERYTHING); screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); /*retrieve array of all keys*/ sdl_key_array = SDL_GetKeyState(NULL); /*setup colors after screen initialized*/ color_1 = SDL_MapRGB(screen->format, 240, 128, 128); color_2 = SDL_MapRGB(screen->format, 50, 205, 50); color_3 = SDL_MapRGB(screen->format, 135, 206, 250); /*loop is needed to keep window open*/ while (loop != 0) { /*make SDLK_1 index a one-press detection*/ sdl_key_array[SDLK_1] = 0; /*read all events per cycle*/ while (SDL_PollEvent(&input)) { if (input.type == SDL_QUIT) loop = 0; } /*exit if escape pressed*/ if (sdl_key_array[SDLK_ESCAPE] == 1) loop = 0; /*draw rectangle if key is pressed*/ if (sdl_key_array[SDLK_1] == 1) { printf("Drawing rectangle of color_1\n"); SDL_FillRect(screen, &block, color_1); } if (sdl_key_array[SDLK_2] == 1) { printf("Drawing rectangle of color_2\n"); SDL_FillRect(screen, &block, color_2); } if (sdl_key_array[SDLK_3] == 1) { printf("Drawing rectangle of color_3\n"); SDL_FillRect(screen, &block, color_3); } SDL_Delay(20); /*wait 20ms*/ SDL_Flip(screen); /*update screen*/ } /*perform final commands then exit*/ SDL_Quit(); /*shutdown SDL*/ fflush(stdout); /*update stdout*/ return 0; } | ||
prompt/stdout: Drawing rectangle of color_1 Drawing rectangle of color_2 Drawing rectangle of color_2 Drawing rectangle of color_3 Drawing rectangle of color_3 Drawing rectangle of color_1 Drawing rectangle of color_2 Drawing rectangle of color_2 Drawing rectangle of color_3 Drawing rectangle of color_3 | ||
output inside window:
|
Similar to the previous example, there are three keys which color the screen three different colors. This example uses the keys 1, 2, and 3 as well as the colors light coral, lime green, and sky blue (as seen at http://www.tayloredmktg.com/rgb/). My key holds are slightly shorter this time (2 frames) and I stopped at the sky blue color. This example only detects initial presses for the 1 key.
To wrap up this post, a list of key enumerations can be found at http://wiki.libsdl.org/moin.cgi/SDL_Keycode?highlight=%28\bCategoryEnum\b%29|%28CategoryKeyboard%29|%28SGEnumerations%29 and more information about SDL_GetKeyState can be seen at http://sdl.beuc.net/sdl.wiki/SDL_GetKeyState. For more information about keyboard input, visit http://content.gpwiki.org/index.php/SDL:Tutorials:Keyboard_Input_using_an_Event_Loop, http://lazyfoo.net/SDL_tutorials/lesson08/index.php (also in C++), and http://content.gpwiki.org/index.php/SDL:Tutorials:Practical_Keyboard_Input.
Thank you for reading; the next post will discuss mouse input.
P.S. this post was revoked for editing after I discovered that SDL detects key presses only once with no repetition. Also, note that tutorials from http://lazyfoo.net/SDL_tutorials/ will be in C++ with some C mixed in (and use images, which are covered in the lessons 2 and 3). tutorials from http://content.gpwiki.org/index.php/SDL:Tutorials are almost all C.
No comments:
Post a Comment