Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » [A5] multiple key events

This thread is locked; no one can reply to it. rss feed Print
[A5] multiple key events
Neil Roy
Member #2,229
April 2002
avatar

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)

#SelectExpand
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...

---
“I love you too.” - last words of Wanda Roy

Arthur Kalliokoski
Second in Command
February 2005
avatar

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
avatar

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)

#SelectExpand
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
avatar

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...

#SelectExpand
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. :)

---
“I love you too.” - last words of Wanda Roy

Winfield
Member #15,244
July 2013
avatar

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
avatar

could also just have a direction variable.. as Arthur was aluding to. With an enum for DIR_NORTH, DIR_SOUTH, etc.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Neil Roy
Member #2,229
April 2002
avatar

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.

---
“I love you too.” - last words of Wanda Roy

Thomas Fjellstrom
Member #476
June 2000
avatar

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.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Arthur Kalliokoski
Second in Command
February 2005
avatar

#SelectExpand
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
avatar

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.
It looked the same to me when you first mentioned it, I couldn't understand where there was a difference until now. D'OH

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. :)

---
“I love you too.” - last words of Wanda Roy

Go to: