Mouse movement causes unwanted speed increase for player
piskypom

I've only been using Allegro for about a week. I have a lot to learn.

For the moment, I can't figure out what's causing the player movement speed to increase.

As soon as I install the mouse and register it, the player will move very fast when I move the mouse. If I leave the mouse still, the player moves at normal speed.

If I leave mouse uninstalled, then there is no problem.

https://github.com/adabo/Allegro_First_Test/tree/dev_no_switchcase_for_keypress
AllegroTest Github repo.

All the game code is in game.cpp. You'll notice that I sort of "wrapped" all the allegro initialization code into c++ member functions. It's just easier for me to look at when you look at the main.cpp.

I think all the relevant code will be in these functions:

void Game::handle_events();
void Game::handle_key_press();
void Game::draw_player();

*Edit: This is funny. I apparently broke something because now when event.mouse.x == 59, then event.keyboard.keycode== 59 (ESC), which triggers game_is_running to be false which then exits the program. Not what I want. I can't imagine how the two events are talking to each other.

*Edit2: I fixed the mouse causing the exit routine. I didn't realize I was assigning false twice to game_is_running. Here's the diff

I've been giving the player speed issue some thought and have a theory that when the mouse is moving it refreshes the screen faster than a keyboard being held down.

Or another theory is that a the key polling and the mouse movement overlap thus doubling the player's speed. I'll experiment with that.

Audric

handle_key_press() is being run after each event, which is wrong. It is basically your "game physics" or "game logic" function, so you should rename it accordingly and run it 60 times per second :

#SelectExpand
1void Game::handle_events() 2{ 3 // Fetch the event (if one exists) 4 al_wait_for_event(event_queue, &event); 5 6 switch (event.type) { 7 case ALLEGRO_EVENT_TIMER: 8 handle_key_press(); <--- here 9 can_redraw = true; 10 break; 11 case ALLEGRO_EVENT_DISPLAY_CLOSE: 12 game_is_running = false; 13 break; 14 case ALLEGRO_EVENT_KEY_DOWN: 15 std::cout << "A key was Pressed: "<< event.keyboard.keycode << std::endl; 16 store_key_state(); 17 break; 18 case ALLEGRO_EVENT_KEY_UP: 19 store_key_state(); 20 break; 21 case ALLEGRO_EVENT_MOUSE_AXES: 22 handle_mouse_action(event, &target); 23 break; 24 default: 25 std::cout << stderr << " Unsupported event received: " << event.type << std::endl; 26 break; 27 } 28 // <--- removed 29}

piskypom

Thank you Audric! You solution fixed the issue. I'm still trying to understand how that works. I need to learn about what the timer event is.

You mentioned that I should rename the handle_key_press() function. That it's name is inappropriate for what it actually does. I need a little more advice on this because with game development I have no real direction. I'm just learning as I go, teaching myself as I need, eg: youtube videos, articles. I didn't really find any extensive allegro game tutorial series, thus I'm learning the hard way.

So this handle_key_press() function is doing the heavy lifting so to speak? The game logic, as you say. I guess I'm a little confused because in my head, the reason I named it "handle_key_press" is because of what happens after the key is pressed, such as the player's position is updated. What would you recommend?

P.S: I'd also like to know about the order in which the cases in the switch are currently. Your solution puts the player position update at the top before the keyboard is is polled. Is that right? I guess I don't know how the allegro event queue system works.

Audric

The structure that you have is 95% a very commmon pattern, a game loop with fixed time step:
- An allegro timer lets you keep track of time. It "ticks" 60 times per second, a very common choice because with common 60Hz screens, players won't see different things change more frequently than 60 times per second anyway.
- Most input handlers only change the "state" of the player controller
- On each timer "tick", you have a function perform everything that changes in the "game world", to model what has happened in 1/60s. Ex: player.x+=10 means he is currently moving 600 pixels per second.
- you (re)draw the screen only if at least one "tick" has taken place. It's possible to optimize your game loop to "skip frames" when the computer is not fast enough to keep up (input events+1*logic+1*redraw > 1/60s). To the player, skipping one image occasionally is much less visible than if there is a slowdown.

I suggested renaming handle_key_press() because at the moment you may have only one active thing, but as soon as you add other game elements you'll see they all need "update" code that gets called as frequently as player update, independantly from player input : bullets keep moving, animating explosions keep changing image, etc. Making a single point of entry game_logic() is very common.

edit: About the order of things : The switch "cases" all have a break, so the order they are written does not change the behavior. The events are present in the queue in whatever order they happened.

piskypom

Hm. So the event: ALLEGRO_EVENT_TIMER is called 60 times/per second? If that is the case then yes, it makes much more sense in this design pattern to update per frame tick, rather than each gameloop. As you said, maybe the loop could be out of sync with the event_timer. Am I understanding correctly?

Also, does the timer run asynchronous to the game loop or in it's own thread?

Ok, so the queue is literally that: a list/array of events lined up and then the switch just checks the next one in line. So it literally does not matter which order the cases are in. Correct?

Edgar Reynaldo

Generally you want to check events until the queue is empty, and then draw, but continue to wait for timer events as normal. Your timer tells you when to redraw and when to process logic.

piskypom

OK. I want to do things logically. So what you're saying is that I'm drawing each tick regardless if the queue is empty? If that is the case, how do I check if the queue is empty?

#SelectExpand
1switch (event.type) { 2 case ALLEGRO_EVENT_TIMER: 3 if (event_queue == EMPTY) { 4 // update and draw stuff; 5 } 6}

Edgar Reynaldo
piskypom

:-X
Ya know, I'm just gonna applaud/thank you for not throwing "rtm!" at me. I even have the v5 manual open in the adjacent tab in my browser. I certainly could have found that if I would have taken 5 seconds to the events.

Audric

Yes, all these allegro systems are asynchronous. They push events at the end of the queue, it can happen at any moment in parallel with the execution of your code.

The al_wait_for_event() removes the first event at the front of the queue and then the switch part is when you examine it.
In fact, if the queue is already empty, al_wait_for_event() stops your program until there is (at least) one. So if you don't close the window or touch the keyboard or mouse, the program is completely stopped at this point in your code... until a timer event occurs! This queue is set up to receive timer events as well, so even with no player input, there will be timer event every 1/60s to make this part of code proceed forward.

piskypom

Now that is cool! I'm really liking this framework. It's got lots of features, but not so much that it takes away the fun of discovery and invention (or re-invention in my case).

Edgar Reynaldo

Think about it. If you're processing your logic once every event, it will speed up when there are a lot of events. If you only process your logic once per timer tick it will stay at the same rate.

piskypom

So true. Using an event system is completely new to me. It's pretty nice, though. And I like the structure and organization of it all. I came across so many bugs yesterday not fully understanding the even queue. I managed to knock out all of them. It's slowly starting to sink in.

Thread #617304. Printed from Allegro.cc