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

Author Topic: Correct way to do player movement  (Read 21451 times)

0 Members and 1 Guest are viewing this topic.

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Correct way to do player movement
« on: April 18, 2012, 11:51:19 pm »
Hello all,

First of all, sorry if it doesn't belong here.

Since I'm a beginner in C++, I'm a bit confused about what is the correct way to do player movement. Some tutorials insist that I should use a switch/case statement. However, when I started to implement smooth player movement, I based it on the code of the Pong example that comes with SFML. So, at the moment, my sprite movement code looks like this:

Code: [Select]
float deltaTime = clock.restart().asSeconds();

if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
sprite.move(0.f, -playerSpeed * deltaTime);
}

if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
sprite.move(0.f, playerSpeed * deltaTime);
}

if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
sprite.move(-playerSpeed * deltaTime, 0.f);
}

if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
sprite.move(playerSpeed * deltaTime, 0.f);
}

It works like a charm (in fact, I've tried many, many smooth movement tutorials, and failed with most of them), but I have a strange feeling that it not supposed to be done this way in a larger, state machine driven game, and only meant for a simple example, like the Pong game. Can I just leave it this way, and go on implementing the rest, or should I redo this with a switch statement?

Your help would be greatly appreciated.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Correct way to do player movement
« Reply #1 on: April 19, 2012, 12:25:43 am »
On a large scale you'd have some hooking somewhere inbetween to make everything as independed as possible, but in the end it often isn't very diffrent from what you have now.

What can easily change, is the way you calculate the position. For instance you may want to have some gravity, so you'll have to subtract some amount from the position, or you want the player to move equally fast in the diagonal and calculate the squareroot of the x and y position, etc.

But for simple eight direction movement this is 'the' solution. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Correct way to do player movement
« Reply #2 on: April 19, 2012, 12:47:52 am »
Thanks for your reply.

Since I'm planning to do a 2d RPG, only with 4-directional movement (I still need to figure out how to move only one direction at once, thus disabling diagonal movement), looks like this will be enough.

Also, regarding this code, I don't really understand how the clock works in SFML2. In 1.6, I remember there was a function to get elapsed time. But now, we have clock.restart, which is kind of "scary" to me. Why we need to restart the clock every frame? And won't it mess something up, or kill performance? (Although I believe that even if it's called "clock", the SFML clock is not related to the system clock, or the actual timing hardware/oscillators.)

julen26

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
    • http://julen26.blogspot.com
Re: Correct way to do player movement
« Reply #3 on: April 19, 2012, 02:21:11 am »
In this case, deltatime should be the time of one frame. So you have to calculate it correctly.
Declare "deltatime" out of the main loop.
clock.restart();

//events...

deltatime = clock.getElapsedTime().asSeconds();
For more advanced loop, you could use a fixed timestep. This tutorial explains it very well:
http://www.koonsolo.com/news/dewitters-gameloop/

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Correct way to do player movement
« Reply #4 on: April 19, 2012, 03:02:02 am »
So basically, the SFML clock works like a stopwatch (starts at the begining of the frame, stops when the frame has been rendered, returns the elapsed time, then restarts for the next frame)?

Actually, I don't really need to do serious timing (there is no networking, or anything requiring extreme accuracy, in my "game"), the only thing that bothers me is the ever-restarting clock, but then it seems it's the only way to do, and won't do any harm.

But the article you've linked looks interesting, I'll take a look at it. Thank you.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Correct way to do player movement
« Reply #5 on: April 19, 2012, 10:26:35 am »
In this case, deltatime should be the time of one frame. So you have to calculate it correctly.

But keep in mind, the time of one frame doesn't include just the events or just the drawing.
So restarting the clock while acquire the deltaTime is the right right way, otherwise you ignore the fact, that the time between two frames is the sum of passed time of the update/event handling and the drawing.

Now the question remains where to put the restart statement.
If you set it at the end or the beginning of the game loop you'll get the deltaTime of the last frame. If you put it between the update and the draw blocks you'll get the sum of the elasped time for the update block of the last frame and the draw block of the frame before the last one.

For fixed times steps there's also another article I can point to: http://gafferongames.com/game-physics/fix-your-timestep/

Restarting your clock doesn't take away any performance, so don't worry.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Correct way to do player movement
« Reply #6 on: April 20, 2012, 02:58:18 pm »
Sorry for bumping this, I didn't want to start another topic (and the problem is relevant to the original question anyway). So, how am I supposed to "filter" input? I mean, at the moment, I control the character with the arrow keys. If I press, for example, down and right, the character moves diagonally. But I'd like to avoid this, and limit the character movement to only horizontal or vertical.

Here's an example what I've tried so far: When the up or down arrow is pressed, I made a test do determine wether the left or right arrow is pressed, and only started to move the character when neither of they were pressed. I did the same for left and right. This worked, but not as expected, as there is no way to change direction until the original key has been released.

In RPG Maker, the system I'd like to recreate, when the player holds down a key and the character starts to move, pressing another direction key will overwrite the first one, but the first one will remain in the input buffer until released. For example, if I hold down right arrow, the player moves to the right, but if I press up arrow (while holding down right arrow), the character will move up immediately, thus the system ignores if the previous key is down or not. If I release up arrow (while still holding right arrow), the character will move to the right, I don't have to release and hold right arrow again.

Sorry if it wasn't clear what I'd like to achive, but could someone shed some light on this?

Thank you.

tanmanknex

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Re: Correct way to do player movement
« Reply #7 on: April 23, 2012, 03:29:53 am »
I'm not very experienced, but I think I know what you mean.  Would the sf::Keyboard::isKeyPressed(sf::Keyboard::(key)) work for your purposes?  As you can probably guess, it returns a boolean of whether or not the key is pressed.  Maybe have a couple variables that store the current key and previous key and whether or not they are pressed?  Like I said, I'm pretty new to programming and SFML but hopefully I helped you think of something.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Correct way to do player movement
« Reply #8 on: April 23, 2012, 05:49:20 pm »
Would the sf::Keyboard::isKeyPressed(sf::Keyboard::(key)) work for your purposes? 

I think he's fully aware of this function and the question was more about how to do it. ;)

There are two diffrent ways to handle input, one directly with sf::Keyboard and one via the event handling (window.pollEvent(event)).
The first one is just a state checking function, so you can either tell if it's pressed or not. The second gets fired by the event system of your OS and thus if you press down a key, there will at first be an event then a pause and then a repeated firing of events. The pause and repeation speed can often be configure in the settings of your OS and thus isn't a useable way for inputs, but it has the event for the case a key gets released.

So for your problem:
If a key get's pressed for the first time I'd push the keycode onto a stack. This way you'll always have the wanted key in the top element.
For the removing part you'd either could use the key released event, which can give you a bit a messy code or you could just check if the key representing the key code of the top element is not pressed and then pop it.
Now how do you know if the key gets pressed for the first time. You have a vector of four booleans for each direction key. If a key is pressed you set the respectiv key to true and when poping the top element you set it to false.

I hope you understood my thoughts. And I can't give a guarantee since I didn't implement it.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/