Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Smooth character movement with Arrow keys  (Read 2797 times)

0 Members and 1 Guest are viewing this topic.

Kallyn

  • Newbie
  • *
  • Posts: 2
    • View Profile
Smooth character movement with Arrow keys
« on: February 21, 2023, 06:27:46 pm »
Hey!

I tried making a simple character movement with the arrow keys, just with a rectangle moving around the screen. However, when I press the arrow keys, the rectangle moves similarly to how a text cursor moves between text when the arrow key is held: once for one character, then a pause, then quickly through the rest of the characters. That movement is mimicked by my rectangle, moving once and pausing, then continuing to move after that. I thought I could use a while loop to detect when a key is pressed, but I realized I couldn't because the rest of the program would pause while the key is pressed, so I need to make it with an if statement instead of a while loop. Is there a way to make the movement smooth?

I'm not sure how to display code properly so I am just going to do it like this.

    while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();

            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
                rectangle.move(0, -0.5);
                y -= 0.05;
                cout << x << ", " << y << endl;
            }
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
                rectangle.move(0, 0.5);
                y += 0.05;
                cout << x << ", " << y << endl;
            }
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
                rectangle.move(-0.5, 0);
                x -= 0.05;
                cout << x << ", " << y << endl;
            }
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
                rectangle.move(0.5, 0);
                x += 0.05;
                cout << x << ", " << y << endl;
            }
        }

kojack

  • Sr. Member
  • ****
  • Posts: 343
  • C++/C# game dev teacher.
    • View Profile
Re: Smooth character movement with Arrow keys
« Reply #1 on: February 21, 2023, 11:36:34 pm »
There are two ways of checking the keyboard: event based (reading KeyPressed, KeyReleased or TextEntered events) or immediate (sf::Keyboard::isKeyPressed).
The immediate check goes outside of the event loop, otherwise it only gets called when some event comes in. In this case, the pause then repeat is caused by the TextEntered event, which is doing the standard OS behavior.

Making your code like this should fix it:
while (window.pollEvent(event))
{
        if (event.type == sf::Event::Closed)
                window.close();
}

if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
        rectangle.move(0, -0.5);
        y -= 0.05;
        cout << x << ", " << y << endl;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
        rectangle.move(0, 0.5);
        y += 0.05;
        cout << x << ", " << y << endl;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
        rectangle.move(-0.5, 0);
        x -= 0.05;
        cout << x << ", " << y << endl;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
        rectangle.move(0.5, 0);
        x += 0.05;
        cout << x << ", " << y << endl;
}
 

Raving_Maniac

  • Newbie
  • *
  • Posts: 1
    • View Profile
    • Email
Re: Smooth character movement with Arrow keys
« Reply #2 on: April 29, 2024, 12:40:26 pm »
Hello,

I was having this same problem, but reformatting my code the way kojack said fixed it. I read through quite a few other posts before I found this one.

Here is one of the others that I read:
https://en.sfml-dev.org/forums/index.php?topic=13145.msg92028#msg92028

In that post, eXpl0it3r said:
"Don't mix real-time inputs and event handling.
Read the linked tutorials (again), especially pay attention to when you should use what and where you should use what. :)
"

I read the tutorials, but still don't understand why this fixed the issue. I am pretty new to C++ and SFML so I apologise if this is not a good question.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Smooth character movement with Arrow keys
« Reply #3 on: April 29, 2024, 01:04:14 pm »
Using events over isKeyPressed isn't a "reformatting", you're changing the underlying code. Formatting usually refers to code styling change that don't affect the semantics and usually also not the syntax.

The event that you're getting from the pollEvent() call already carries all the necessary information, as such it's for one redundant to again ask the system/device what the keyboard state of a specific key is, in relation to an event. Meaning if the event is of type KeyPressed (event.type == sf::Event::KeyPressed), then you know that a key has been pressed, you don't have to query again if the key is pressed.
In the worst case you might miss the key press, although you process the event, the key might have already been let go. Meaning you get a KeyPressed event that happened like 0.5ms ago, but then you ask the device again if at this moment right now the key is pressed, but you've let go in that time frame, thus the input isn't processed. Granted this is quite an edge case, but it does happen and can lead to frustrations for players/users.
And finally, real-time input checks are slightly more expensive, as you have to query device information, while the event has already gathered that information, and on some systems (e.g. macOS), you'll need additional permissions for the function to return anything at all.

The "new" recommendation is to use events whenever possible, but also never mix events and real-time checks for the same event occurrence. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

kojack

  • Sr. Member
  • ****
  • Posts: 343
  • C++/C# game dev teacher.
    • View Profile
Re: Smooth character movement with Arrow keys
« Reply #4 on: April 29, 2024, 02:31:05 pm »
The issue with the top post was that immediate key tests were being done only even events came in.

Imagine you press left arrow, hold it for 5 seconds, then release.
Also imaging you are running at 60fps.
There will be one key down event. 5 seconds later there will be a key up event. But for those 5 seconds in between, there are no events.
An immediate key check (sf::Keyboard::isKeyPressed) doesn't tell you when a key goes down or when it goes up, it tells you the current state. If you call it in the main loop, you'd be calling it 60 times a second and it would tell you the current state.
But in the top post it was only being called twice, not every frame, so the movement wasn't happening every frame.

Another way of structuring it would be to use the events only but get them to set a bool to say the key is currently down or not. Then you'd check the bool (instead of the key directly) outside of the events. So a key down event sets the bool to true, key up event sets it to false. You can now check it as often as you want.

(Actually there may be more events coming in from operating system key repeats when a key is held, but it won't be at 60fps)