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

Author Topic: Fluent Movement  (Read 3140 times)

0 Members and 1 Guest are viewing this topic.

DerKami

  • Newbie
  • *
  • Posts: 4
    • View Profile
Fluent Movement
« on: October 03, 2016, 12:10:37 pm »
Hello there,

I just started fiddling around with SFML and c++.
I already did some graphics programming with e.g. Andorra 2D, which was something similar to SFML but for pascal/Delphi.
I got the book: SFML Game Development from PACKT to guide me.

And now i'm stuck on a difficult topic...
I just love it if games are fluent and many games are. e.g. Stardew Valley.
But i can't get this level of smoothness to my circle with a black background.


I made a Video with different Settings (NoLimit + No VSync, NoLimit + VSync, 60 FPS, 240 FPS)
There are some tiny little differences and i'd like to emphasize on the first 15 Seconds where i show without limits and then with VSync on. You can clearly see, that the ball doesn't run as smooth as without limits.
So how do i manage to get a fluent movement like without limits?

PS:
http://en.sfml-dev.org/forums/index.php?topic=20677.0
I checked this thread but it doesn't seem to fit/solve my problem exactly..
Mabye i'm wrong but on my little laptop with a shitty intel card, i don't have this stutter. I can't even disable VSync because Intel is a b...

PPS:
I've made another video:

It's clearer to see what i mean with higher speed.

Thanks for any thoughts.
Kami
« Last Edit: October 03, 2016, 12:48:04 pm by DerKami »

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
Re: Fluent Movement
« Reply #1 on: October 04, 2016, 10:42:21 am »
Can't say for sure but this looks a lot like your updates are just a bit inconsistent (i.e. sometimes you draw or update twice between drawing or updating again, which causes the fake  stutter.

Have a look here: Fix Your Timestep!

DerKami

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Fluent Movement
« Reply #2 on: October 04, 2016, 01:16:45 pm »
Hi Mario,

my Book covers exactly this "Fix your Timestep" thing.
So my updates are built exactly after your link's topic.

void Game::run()
{
        sf::Clock clock;
        sf::Time timeSinceLastUpdate = sf::Time::Zero;
        sf::Time cr = sf::Time::Zero;

        float secondsTimer = 0;
        float fps = 0;

        while (mWindow.isOpen())
        {
                cr = clock.restart();
                timeSinceLastUpdate += cr;
                secondsTimer += cr.asSeconds();
                fps = 1.f / cr.asSeconds();

                while (timeSinceLastUpdate > TimePerFrame)
                {
                        timeSinceLastUpdate -= TimePerFrame;
                        processEvents();
                        update(TimePerFrame);
                }

                if (secondsTimer > 1) {
                        mFps.setString(std::to_string(std::round(fps)));
                        secondsTimer = 0.f;
                }

                render();
        }
}

Greetings
Kami

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Fluent Movement
« Reply #3 on: October 04, 2016, 05:44:47 pm »
The stuttering is caused by irregular frame lengths and a solution is to use interpolation as described in "The Final Touch" section of that Fix Your Timestep article, linked above.

Vertical synchronisation limits the frame rate to - at maximum - the refresh rate of the display. Updating the image at a different time (sometime between display refreshes) can cause smoother movement as it is more accurately positioned for the exact time but can cause visual tearing, which is an undesirable side-effect but can be un-noticable at slower motion speeds.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

DerKami

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Fluent Movement
« Reply #4 on: October 04, 2016, 07:39:50 pm »
Yeah i thought something like this could be the problem.
But i really don't get the pseudo code on the page. It seems like it's missing something and i don't get the idea behind it yet ...

Maybe i have to reread it a couple of times.

Why is t constantly increasing?
What's withing integrate( currentState, t, dt );? Is it the update function?
What's with that state things...

I'm not a native speaker, but state doesn't seem to fit in there...
I'll try to fiddle a bit with that "code" there.
« Last Edit: October 04, 2016, 07:41:53 pm by DerKami »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Fluent Movement
« Reply #5 on: October 04, 2016, 09:35:14 pm »
I had to read Fix Your Timestep more than once to fully understand it so I relate to your position.

Integrate in this context pretty much just means update, yes. The word integrate is used because this article follows on from their previous article about implementing physics integration.

State in this context is the current state of something/everything.
For example, the current state of a player's graphic could be its position, velocity, direction, size etc..
In theory, "current state" is all of the current values of all variables. Note that during "the final touch" section, it requires the previous state to also be available. That means that you need two copies of everything you are "integration"/updating. In reality, though, you should only really need a copy of everything that is drawn - for interpolation.

t represents the current overall time and dt is the timestep (which should be fixed/constant).

If you use my Kairos timing library, you can use the Timestep class or the Timestep Lite class to simplify most of the work involved.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

DerKami

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Fluent Movement
« Reply #6 on: October 05, 2016, 01:14:27 am »
I often try to understand what's happening by translating the code/varables into understandable statements.

This is what i get:
double t = 0.0; // time since beginning (useful for timed events/skill refresh/etc. would not work those times should be saved between sessions... then i don't get the point of know this
double dt = 0.01; // my fixed timegap for physics (0.01 seconds which basically means i have a tick rate of 100)

double currentTime = hires_time_in_seconds(); // used to calculate the actual timegap
double accumulator = 0.0; // used to accumulate time because the actual timegap is very often much lower then 0.01 seconds

State previous; // still don't get... why i'd need this here.. Probably abstraction
State current; // same as above

while ( !quit )
{
    double newTime = time(); // this is used to calculate the timegap
    double frameTime = newTime - currentTime; // this is the actual timegap
    if ( frameTime > 0.25 )  // why should we max it out to 0.25? If this happens, there's something strange...
        frameTime = 0.25;  // probably the only reason why this could happen if we have some heavy loading within the loop which should be avoided...
    currentTime = newTime; // this is used for the next timegap calculation clearly...

    accumulator += frameTime; accumulate timegap because it is often lower then 0.01

    while ( accumulator >= dt )
    {
        previousState = currentState; // still probably just some abstraction layer for states
        integrate( currentState, t, dt ); // why would the update method would need the current state.. the method should update the current state so it knows the current state or am i wrong here?
        t += dt; // accumulate t obiously
        accumulator -= dt; // so we had an actualy update gap so we have to lower the accumulator.. otherwise endless loop
    }

    const double alpha = accumulator / dt; // why?

    State state = currentState * alpha +
        previousState * ( 1.0 - alpha );  // why?

    render( state ); // rendering is "assuming" what will happen next with the currentState/Alpha/PreviousState thing.. this is a rather pretty complex task to do if you have ton's of informations going on here..
}
 

Maybe while i wrote this down i actually understand somewhat how this works..
Basically we try to do some assuming to what our next state will look like to interpolate the rendering.
Like if we move an object from 0 to 100 with a speed of 100 per second we can achive this fluently IF we have constant 0.01 timegap (ALWAYS).
Which is not the case so it could be, that we have, with any reason we don't now, accumulated about 0.05 timegap because of an update which just took more time... we would do 5 updates in the next loop which would lead to move the object 5 units with one step.

The whole problem is, if one update would tike 0.05 seconds, we would not even be able to draw in that time so the ball would always jump...
The only way to solve it, is to actually do the update not in the same thread as the drawing.
Because this code right obove, is in one thread, so if our update for example is doing a constant 30 fps because it takes so long, our drawing would take too 30 fps instead of 60 for example with VSync on a 60Hz monitor.

Please correct me if i understand something wrong here... I'm just confused.
The more i think about it the more sense does it make but in the same time it doesn't because of logic flaws in my head.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Fluent Movement
« Reply #7 on: October 05, 2016, 10:12:31 pm »
There is no prediction here. The interpolation of frames is the between the previous state of the frame and the current state of the frame. Technically, this interpolation produces results that are exactly one frame behind the actual position so would "lag" behind by one frame. This is usually unnoticable and is used in many games. The other option, which produces more accurate positioning for the exact time would be extrapolation instead of interpolation. Extrapolation predicts the next frame and 'interpolates' between the current frame and the predicted next frame. Although the position is more accurate in constant motion, changes of motion can result in sudden jumps in the motion.
I created an example for Kairos that allows you to switch between interpolation and extrapolation to see the difference. I also made a video of that example (the current settings, which are changed throughout the video, are shown in the window bar):

Notice the "jerkiness" when extrapolation is used and the direction is changed.

By the way, t (or overall/current accumulated time) is just the total time that has elapsed ingame (as been processed and updated in timesteps) since updates began. Every time a timestep (dt) is processed, it adds it to the overall time, t.

The "why" sections in your code are the confusing part of the article. It works out how far along the next timestep that time has passed and uses that to interpolate the two frames. Again, it uses the previous one with the current frame instead of the current one and the next (predicted) frame.

Render state just means draw the graphics to the window using the current state (values). Note that this is the interpolated state - all of the values in the state will be between the ones in the previous state and the ones in the current state.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

 

anything