|
[A5] multiple key events |
Neil Roy
Member #2,229
April 2002
|
I am working on my Allegro 5 game, which is coming along nicely, I am really loving the library. I do have one "problem" which I am not sure there is a solution to, but I thought I would throw this out there and see if anyone has any ideas. I had the same "problem" with Allegro 4. It's not a library problem really though, at least I don't think so. What happens in my Pacman style game is that a player can be moving along, say to the right in the maze. They wish to turn up. They get so wrapped up in the game and the ghosts chasing them that they forget to let go of the right cursor key and press UP key, so they have both keys pressed at the same time. This is fairly common. To their dismay, their character keeps going right and doesn't turn up at all and they end up dying and cursing the programmer. I have tried to change how I code this, by clearing the key_pressed[] array when a new key is pressed and the player will then turn as expected, even with a new key pressed, but then the game won't respond to the right keypress until that key is released. Now obviously they should have released it and I have advised against holding keys down as you don't need to hold a key down at all in my game, just tap the direction you wish to go then let go of the key and your character will happily travel in that direction unless told otherwise. But is there a better solution to this do you think? I haven't tested if this happens in other games yet. Just thought I would throw this out there just in case there is something I am missing. Thanks in advance. Example code I use (edited to keep things brief) 1while(!done && !pacman.dead) {
2 al_wait_for_event(event_queue, &event);
3
4 if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
5 done = true;
6 }
7
8 // check for key input and process it
9 else if(event.type == ALLEGRO_EVENT_KEY_DOWN) {
10 al_get_keyboard_state(&keys);
11 switch(event.keyboard.keycode) {
12 case ALLEGRO_KEY_ESCAPE:
13 // Do stuff for ESCAPE (removed for brevity)
14 break;
15 default:
16 pressed_key[event.keyboard.keycode] = true;
17 }
18 }
19 else if(event.type == ALLEGRO_EVENT_KEY_UP) {
20 al_get_keyboard_state(&keys);
21 pressed_key[event.keyboard.keycode] = false;
22 }
23
24 else if(event.type == ALLEGRO_EVENT_TIMER) {
25 if(event.timer.source == pacman.timer) {
26 redraw = true;
27 redraw_timer = al_get_timer_count(setting.redraw_timer);
28
29 if(pressed_key[ALLEGRO_KEY_UP]) {
30 // do stuff for up
31 }
32 else if(pressed_key[ALLEGRO_KEY_DOWN]) {
33 // do stuff for down
34 }
35 if(pressed_key[ALLEGRO_KEY_LEFT]) {
36 // do stuff for left
37 }
38 else if(pressed_key[ALLEGRO_KEY_RIGHT]) {
39 // do stuff for right
40 }
41 if(pressed_key[ALLEGRO_KEY_LCTRL] || pressed_key[ALLEGRO_KEY_RCTRL]) {
42 // do stuff for CTRL
43 }
44 else {
45 // do other stuff
46 }
47 /*** CHEAT KEYS ***
48 You don't want to know these :)
49 */
50 if(setting.cheat_mode) {
51 }
52
53 // etc...
--- |
Arthur Kalliokoski
Second in Command
February 2005
|
I'd probably have a variable that can only hold 4 states, and the last direction key would set that state. Otherwise, you might check out the keyboard.timestamp field to see which was pressed last. They all watch too much MSNBC... they get ideas. |
ph03nix
Member #15,028
April 2013
|
I modified your code to reflect Arthur's suggestion. If you want to make it more advanced, you would check which directions are available, and if that key is pressed you allow pacman to move that direction (so if you press left and up and he can only move up, he still moves up) 1//store the direction pacman is moving in
2int direction = 0;
3
4while(!done && !pacman.dead) {
5 al_wait_for_event(event_queue, &event);
6
7 if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
8 done = true;
9 }
10
11 // check for key input and process it
12 else if(event.type == ALLEGRO_EVENT_KEY_DOWN) {
13 al_get_keyboard_state(&keys);
14 switch(event.keyboard.keycode) {
15 case ALLEGRO_KEY_ESCAPE:
16 // Do stuff for ESCAPE (removed for brevity)
17 break;
18 default:
19 pressed_key[event.keyboard.keycode] = true;
20 }
21 }
22 else if(event.type == ALLEGRO_EVENT_KEY_UP) {
23 al_get_keyboard_state(&keys);
24 pressed_key[event.keyboard.keycode] = false;
25 }
26
27 else if(event.type == ALLEGRO_EVENT_TIMER) {
28 if(event.timer.source == pacman.timer) {
29 redraw = true;
30 redraw_timer = al_get_timer_count(setting.redraw_timer);
31
32 if(pressed_key[ALLEGRO_KEY_UP){
33 direction = 0;
34 }else if(pressed_key[ALLEGRO_KEY_DOWN){
35 direction = 1;
36 }else if(pressed_key[ALLEGRO_KEY_LEFT){
37 direction = 2;
38 }else if(pressed_key[ALLEGRO_KEY_RIGHT){
39 direction = 3;
40 }
41
42 //changed to direction instead of key inputs
43 if(direction==0) {
44 // do stuff for up
45 }
46 else if(direction==1) {
47 // do stuff for down
48 }
49 else if(direction==2) {
50 // do stuff for left
51 }
52 else if(direction==3) {
53 // do stuff for right
54 }
55 if(pressed_key[ALLEGRO_KEY_LCTRL] || pressed_key[ALLEGRO_KEY_RCTRL]) {
56 // do stuff for CTRL
57 }
58 else {
59 // do other stuff
60 }
61 /*** CHEAT KEYS ***
62 You don't want to know these :)
63 */
64 if(setting.cheat_mode) {
65 }
66
67 // etc...
|
Neil Roy
Member #2,229
April 2002
|
Arthur Kalliokoski said: I'd probably have a variable that can only hold 4 states, and the last direction key would set that state. Otherwise, you might check out the keyboard.timestamp field to see which was pressed last. Oooh... that looks interesting. I'm just not exactly sure how I would implement it. Make a struct for the key_pressed[] array perhaps which would include a member to store the time it was pressed perhaps... Edit: ph03nix, I don't see how your code changes anything. Edit2: Okay, fixed the problem! Thanks for the great idea with the timestamp! I remade my key_pressed[] array so that it used a struct which included a bool for whether or not it was pressed and a double for the timestamp. I then check the time for each direction and set a direction variable based on which key was pressed most recently and act accordingly... 1 double timest = 0.0;
2 short dir = -1;
3
4 if(pressed_key[ALLEGRO_KEY_UP].down) {
5 timest = pressed_key[ALLEGRO_KEY_UP].time;
6 dir = 0;
7 }
8 else if(pressed_key[ALLEGRO_KEY_DOWN].down) {
9 if(timest==0) {
10 timest = pressed_key[ALLEGRO_KEY_DOWN].time;
11 dir = 1;
12 }
13 else {
14 if(pressed_key[ALLEGRO_KEY_DOWN].time > timest) {
15 timest = pressed_key[ALLEGRO_KEY_DOWN].time;
16 dir = 1;
17 }
18 }
19 }
20 if(pressed_key[ALLEGRO_KEY_LEFT].down) {
21 if(timest==0) {
22 timest = pressed_key[ALLEGRO_KEY_LEFT].time;
23 dir = 3;
24 }
25 else {
26 if(pressed_key[ALLEGRO_KEY_LEFT].time > timest) {
27 timest = pressed_key[ALLEGRO_KEY_LEFT].time;
28 dir = 3;
29 }
30 }
31 }
32 else if(pressed_key[ALLEGRO_KEY_RIGHT].down) {
33 if(timest==0) {
34 timest = pressed_key[ALLEGRO_KEY_RIGHT].time;
35 dir = 4;
36 }
37 else {
38 if(pressed_key[ALLEGRO_KEY_RIGHT].time > timest) {
39 timest = pressed_key[ALLEGRO_KEY_RIGHT].time;
40 dir = 4;
41 }
42 }
43 }
44
45 if(dir == 0) {
46 // UP
47 }
48 else if(dir == 1) {
49 // DOWN
50 }
51 if(dir == 3) {
52 // LEFT
53 }
54 else if(dir == 4) {
55 // RIGHT
56 }
Thanks for the ideas. --- |
Winfield
Member #15,244
July 2013
|
Sounds like these gentlemen have you sorted out, but do keep in mind that some keyboards won't correctly report multiple keypresses on the hardware level - I've actually had one or two that report completely incorrect keys when the left and up arrows are pressed - so if anyone reports problems, make sure to rule out the keyboard they're using before you stress too much over your code. |
Thomas Fjellstrom
Member #476
June 2000
|
could also just have a direction variable.. as Arthur was aluding to. With an enum for DIR_NORTH, DIR_SOUTH, etc. -- |
Neil Roy
Member #2,229
April 2002
|
Actually, the direction code won't make a lick of difference because if I am pressing UP and LEFT at the same time, and it hits the code that tests for UP and finds it is pressed, it will set the direction to UP and ignore the other tests, which is what it is doing already. With the timestamp it sets the time the key was pressed and then can respond to the most recent keypress. I set a direction variable with the new code based on the most recent timestamp. As for the keyboard problem, I'll keep that in mind, thanks. --- |
Thomas Fjellstrom
Member #476
June 2000
|
You'd set the direction variable when you get the key_down event, so it has the "last key pressed" functionality built in. no need to check a key array or key state. -- |
Arthur Kalliokoski
Second in Command
February 2005
|
1 case ALLEGRO_EVENT_KEY_DOWN:
2 {
3 int k = event.keyboard.keycode;
4
5 if (k == ALLEGRO_KEY_ESCAPE)
6 {
7 quit = true;
8 }
9
10 else
11 if( (k == ALLEGRO_KEY_UP) || (k == ALLEGRO_KEY_PAD_8) )
12 {
13 direction = NORTH;
14 }
15
16 else
17 if( (k == ALLEGRO_KEY_LEFT) || (k == ALLEGRO_KEY_PAD_4) )
18 {
19 direction = WEST;
20 }
21
22 else
23 if( (k == ALLEGRO_KEY_RIGHT) || (k == ALLEGRO_KEY_PAD_6) )
24 {
25 direction = EAST;
26 }
27
28 else
29 if( (k == ALLEGRO_KEY_DOWN) || (k == ALLEGRO_KEY_PAD_2) )
30 {
31 direction = SOUTH;
32 }
33
34 key_array[k] = true;
35 break;
36 }
37
38 case ALLEGRO_EVENT_KEY_UP:
39 {
40 key_array[event.keyboard.keycode] = false;
41 break;
42 }
Bear in mind that you can have several different event loops, based on what you're doing (selecting from menus vs. playing the game, etc.). They all watch too much MSNBC... they get ideas. |
Neil Roy
Member #2,229
April 2002
|
Thomas Fjellstrom said: You'd set the direction variable when you get the key_down event, so it has the "last key pressed" functionality built in. no need to check a key array or key state. Ah, okay, I see what you mean now. The key array will hold the fact that both keys are pressed, where as the direction variable is a result of only the last key pressed. I guess it's a result of rewriting old Allegro 4 code that brought this problem on in the first place. I'll give that a go then. I suppose had I started programming with Allegro 5 in the first place and never even seen Allegro 4 or earlier this is probably the way I would have went now that I think about it. Thanks. Arthur Kalliokoski said: Bear in mind that you can have several different event loops, based on what you're doing (selecting from menus vs. playing the game, etc.). Thanks for that. And I like your idea of adding the keypad, I think I'll add that as well, nice idea. --- |
|