Sign in to follow this  
SilverLightProC++

Fix Your Time Step - Need Help

Recommended Posts

Hello everyone! I'm new to SFML, but come from using XNA, Allegro, and some SDL back in the day.

I've always been use to just having a function that kept my games at 60 FPS lock, and all game play, logic, rendering was done in the same loop, with no independent timers, 100% based on frames rendered.

I'm extremely interested in duplicating the Time Step as per the article: http://gafferongames.com/game-physics/fix-your-timestep/ but I'm running into some issues as I've never used Delta Time, or any methods prior, everything was handled for me before.

NOTE: I turned on VSYNC in my Window Class to keep the rate at 60 FPS.

My current code for main.cpp:

#include "Game.h"
#include <iostream>

int main()
{
    // Time Test
    sf::Clock MainClock;
    double Time = 0.0;
    const double DeltaTime = 0.01;

    double CurrentTime = MainClock.getElapsedTime().asSeconds();
    double Accumulator = 0.0;

    Game GameTest;

    while (GameTest.IsWindowOpen())
    {
        double NewTime = MainClock.getElapsedTime().asSeconds();
        double FrameTime = NewTime - CurrentTime;
        CurrentTime = NewTime;

        Accumulator += FrameTime;

        while (Accumulator >= DeltaTime)
        {
            // Game Loop
            GameTest.Input(DeltaTime);

            GameTest.Logic(DeltaTime);

            // AI
            // Physics

            Accumulator -= DeltaTime;
            Time += DeltaTime;
        }
    
        // Render Graphics
        GameTest.Render();

        // FPS - Shows in Console Window
        std::cout << "FPS: " << 1.0f / FrameTime << std::endl;
    }

    return 0;
}

My game.cpp code for moving the sprite:

// Input
void Game::Input(double TempUpdatesPerSecond)
{
    // Keyboard Movement for guy1 --- TEST !!!
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
    {
        guy1.move(0, -32 * TempUpdatesPerSecond);
    }

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
    {
        guy1.move(0, 32 *TempUpdatesPerSecond);
    }

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
    {
        guy1.move(32 * TempUpdatesPerSecond, 0);
    }

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
    {
        guy1.move(-32 * TempUpdatesPerSecond, 0);
    }
}

In the article I'm having trouble understanding what I do with Time? Why is it passed into my Update function along with Delta Time? Do I have my variables set up properly?

From the Article - Second Last Code Snip:

double t = 0.0;
const double dt = 0.01;

double currentTime = hires_time_in_seconds();
double accumulator = 0.0;

while ( !quit )
{
    double newTime = hires_time_in_seconds();
    double frameTime = newTime - currentTime;
    currentTime = newTime;

    accumulator += frameTime;

    while ( accumulator >= dt )
    {
        integrate( state, t, dt );
        accumulator -= dt;
        t += dt;
    }

    render( state );
}

I also have a cout to show how many frames per second are being rendered out to make sure it matches with vsync.

Since DeltaTime = 0.01; does this mean I'm moving at 0.01 * units per frame, at a maximum of 0.06 * units per second assuming steady 60 FPS with Vsync?

I would like to get my code working properly as to match the second last part of the article before learning how to do the final step. I also have no idea about interpolation.

I was also reading online that Delta Time is a very poor technique to use, and you should only program based on a fixed amount of ticks? My issue is making sure my games run good on 60 Hz or 200+ hz monitors while keeping the logic at a fixed update rate, and using any left over time to render as many frames as possible, I just need some guidance along the way.

Thank you!

Share this post


Link to post
Share on other sites

to convert a game loop with a framerate limiter (vsync in this case) to "fix your timestep":

 

 

1.  your update code is currently designed to run at 60Hz, or 16 2/3 ms per update.   so DT = 16 2/3 ms.

 

2. ET is simply how long it takes to render.  or the time from the beginning of update to the next update (to capture render, input, and update time for greater accuracy.

 

3. start a timer, call render, get ET

 

4. pass ET into update()

 

5. in update:

 

accumulator +=ET

while accumulator >= DT

    {

    acumulator -= DT

    run_original_update_code()

    }

 

 

 

that's all there is to it.

 

note that if accumulator >= DT*2   you drop frames!   a cap on ET is the usual workaround. an ET cap temporarily puts the game loop back into lockstep synchronization: one render, one input, and one update per loop iteration.

 

 

that's it for update.  then you have to modify render.

 

in update, you'll need to add new variables for an entity: previous_position and previous_orientation.   when you update an entity, you copy the current location and orientation to the previous location and orientation, before updating.

 

when its time to render, you tween between previous and current location and orientation of the entity to get the location and orientation for drawing.  the amount of tween is accumulator / DT.  so as accumulator goes from zero to DT, tween goes from previous  to current location and orientation.

 

me personally, i just use a framerate limiter and get on with life.   other than bragging rights, there's really no need for a game to run at 120Hz vs 60Hz.  fact is anything 15Hz or above is playable.  movies are only 24Hz, and you don't hear people complaining that movies aren't smooth.  for a long time the game world was perfectly content with 30fps.  there comes a certain point where fast enough is fast enough, and you don't really get much by running even faster.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites

I certainly wouldn't agree that a set update rate is "good enough" if it caps what people can see. Most of the reason for buying a 120hz or more monitor today and having a rig to play it is so you can enjoy the better experience it provides. Not much better way to make a bad impression than to lock people out of the option of benefiting from it.

 

Now on some stuff it might not matter that much, 2d games can certainly get away with it easier. 3d I would be much more concerned with.

Share this post


Link to post
Share on other sites

I certainly wouldn't agree that a set update rate is "good enough" if it caps what people can see. Most of the reason for buying a 120hz or more monitor today and having a rig to play it is so you can enjoy the better experience it provides. Not much better way to make a bad impression than to lock people out of the option of benefiting from it. Now on some stuff it might not matter that much, 2d games can certainly get away with it easier. 3d I would be much more concerned with.

 

Huh, isn't that like the entire idea about fix your timestep? You lock that logic at a fixed rate, but render how often your monitor can display. Sure, if you don't do the last step regarding interpolation, then if you render at 120 hz, but your update rate is 60 hz, then you are rendering every frame twice. But if you use interpolation, you can still update just 60 times per frame, but interpolate between the frames to get 120 different images

Share this post


Link to post
Share on other sites

 

I certainly wouldn't agree that a set update rate is "good enough" if it caps what people can see. Most of the reason for buying a 120hz or more monitor today and having a rig to play it is so you can enjoy the better experience it provides. Not much better way to make a bad impression than to lock people out of the option of benefiting from it. Now on some stuff it might not matter that much, 2d games can certainly get away with it easier. 3d I would be much more concerned with.

 

Huh, isn't that like the entire idea about fix your timestep? You lock that logic at a fixed rate, but render how often your monitor can display. Sure, if you don't do the last step regarding interpolation, then if you render at 120 hz, but your update rate is 60 hz, then you are rendering every frame twice. But if you use interpolation, you can still update just 60 times per frame, but interpolate between the frames to get 120 different images

 

True. 

 

Satharis, you can theoretically update at a variable rate, but I don't recommend it because you will get really weird bugs, like going through a closed door or falling off the ground when jumping due to incorrectly specified deltaTime range and a slow PC. And you need to be very careful when coding. I don't speak from experience because I never tried it, it sounds too frustrating, I don't want to try it out.

 

In conclusion: variable time step makes the game non-deterministic and physics and networking become harder and more prone to bugs. (not my experience)

Edited by codeBoggs

Share this post


Link to post
Share on other sites

In conclusion: variable time step makes the game non-deterministic and physics and networking become harder and more prone to bugs. (not my experience)

 

In my own experience, this is indeed the case. Had some nasty collision-detection bugs due to variable frametime, and even aside from that, debugging collision/physics code became much easier now that update steps are actually deterministic. So I would recommend it for that alone, even if you do not go for the expence of actually using full interpolation.

Share this post


Link to post
Share on other sites

Huh, isn't that like the entire idea about fix your timestep? You lock that logic at a fixed rate, but render how often your monitor can display. Sure, if you don't do the last step regarding interpolation, then if you render at 120 hz, but your update rate is 60 hz, then you are rendering every frame twice. But if you use interpolation, you can still update just 60 times per frame, but interpolate between the frames to get 120 different images

Yes, but that's the key part, if you aren't interpolating you are quite literally capping the frame rate to the update rate. It might say it is rendering at 120 hz and technically it is, but everything that happens on screen is repeated twice, so essentially there is no practical difference from 60 hz. As far as I know there isn't many methods to get around that besides interpolation, there's a lot of negatives to using a variable delta time.

The two fundamental aspects of good visuals are: how fast everything updates and how linear that update is. For instance 30 fps is generally visibly worse than 60 fps but 60 fps that randomly dips down to 40-45 fps every few moments often looks worse than a smooth and uninterrupted 30 fps. The problem with fixed updates is that frame rate is variable, whether its over 60 fps or not. Unless you have vsync on the monitor will happily be redrawing itself whenever it pleases even if you had been doing 1 update per frame and then suddenly it skips to two of the same frame and then two updates squashed together in one frame(because the monitor and update rate are desynchronized,) That's a fundamental problem and why just using a fixed timestep doesn't magically fix motion. Vsync can help with that, but then issues develop if the frame rate ever drops below vsync.
 
So you're left with a few options: update rate(variable or fixed) technically you can use both, and I have seen games do that, but that's generally to do update work more often rather than to update anything visual in the game. Then you can use interpolation or not. Problem with not using interpolation is that then nothing on screen moves, you are fundamentally capped at your update rate.
 

Satharis, you can theoretically update at a variable rate, but I don't recommend it because you will get really weird bugs, like going through a closed door or falling off the ground when jumping due to incorrectly specified deltaTime range and a slow PC. And you need to be very careful when coding. I don't speak from experience because I never tried it, it sounds too frustrating, I don't want to try it out.

The main problem with using variable update rate is that it is non-deterministic. The hurts for issues like networking, or if you want to generate replays. TIme in a computer is just an artificial construct and you can process it however you want to. For instance fixed timesteps are generally done using an accumulator, you accumulate time and then run a number of updates to drain that time and get caught up to just before where time is in reality. You can do the same thing with a variable time step if you wanted.

 

For instance you can add every update to the accumulator and then run updates at whatever the speed the last delta time was. Why would you want to do that? That would be one way to combat the issue you're talking about. If a variable time grows too large(say you used a breakpoint in a debugger) time continues ticking on but the game does not. Get a big enough delta time and a player will walk through an entire map instantly without colliding. Of course there are ways to combat that too, like continuous collision detection, but I digress. The trick is to set a cap on the variable time. Say one second. If more than a second passes then you simply eat up a second at a time from the accumulator and then eat up the remainder in a final update call.

 

Of course a method like that has pitfalls too like the spiral of death, but that can happen in fixed updates as well unless you account for it. The point I'm making is that there are multiple ways to handle the concept of time. Fixed time steps are pretty good, if you set the update rate high(say 120 hz or something) then that solves the issue for most people. For a 2d game I'd recommend that method even if you don't want to bother with coding interpolation, but interpolation does help get rid of "jitter." The point is not to assume people don't want to see a game moving at more than 60 fps, that's a silly assumption to make.

Share this post


Link to post
Share on other sites

The point is not to assume people don't want to see a game moving at more than 60 fps, that's a silly assumption to make.

 

True, I know people that can't stand playing on 60 fps because they are used to 120. 

Share this post


Link to post
Share on other sites

Memo to myself: Never play on a 120hz monitor, or else I'll have to buy one :D (I already have this problem with my 30'' monitor, I just pray it doesn't die before I start working...).

 

Though I think the easiest solution to this problem is just implementing fixed timestep with interpolation. Interpolation might look like a huge deal, but I feels its really simple. You don't even need to duplicate the games state, you can just add an "Vector3 lastPostion;" have a "CopyState()"-method that is run after the update, and so on... I think unless you are really under hard time-pressure, taking the extra afternoon implementing time-step interpolation in a simple way is totally worth it.

Share this post


Link to post
Share on other sites

Though I think the easiest solution to this problem is just implementing fixed timestep with interpolation. Interpolation might look like a huge deal, but I feels its really simple. You don't even need to duplicate the games state, you can just add an "Vector3 lastPostion;" have a "CopyState()"-method that is run after the update, and so on... I think unless you are really under hard time-pressure, taking the extra afternoon implementing time-step interpolation in a simple way is totally worth it.

Which is why that's the usual method used. The only negative there is the game is always running one frame behind. Sadly everything in computers is about trade offs. The alternative is to use extrapolation instead of interpolation, but then you get the problem of things clipping into walls and such because the rendering has no idea about the concept of collision. Same problem you get with dead reckoning.

Share this post


Link to post
Share on other sites

Of course, with GSync/FreeSync, this all suddenly matters a lot less, since you can get smooth VSync behavior out of an arbitrary update rate (depending on the monitor's range - some only go as low as 48hz before turning adaptive sync off!).

I'm waiting for the day of feasible 4k/120hz/(g/free)sync monitors. Well I guess I shouldn't say waiting for, they already exist. Rendering at 4k.. that's another story. Unfortunately adaptive sync is either expensive(gsync) or not guaranteed to even be functional(freesync from dishonest companies.)

Share this post


Link to post
Share on other sites
15Hz being playable is not a fact*, it's extremely subjective.

 

 

granted.

 

30 vs 15 fps is definitely smoother and is somewhat more responsive.  But 15 is still fast enough for a real time experience.  below 15 - not really.    and for a twitch game, 15Hz input and update may be a bit too slow.  most games decouple render and don't freak out at multiple updates per render, so 15fps might still be ok for twitch games, with 30Hz for input and update. not that i'd recommend it. <g>.

 

I too think it would be awesome if all games ran at 60fps or faster.   whether that's possible depends on the available hardware, how much the game does, and dev time available for optimization.  not everyone is building a trivial game on a high end PC with no real deadlines.  

 

really one should only drop from 60 to 30 or from 30 to something less if the game simply does too much to run at the higher target framerate.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites

fact is anything 15Hz or above is playable
 

There's no way I can agree with this. I played games at 30 FPS and I dropped them over that or just played with VSync turned off because of a very horrible input lag.

Sure, for turn based games it's acceptable, but definitely not for action.

Share this post


Link to post
Share on other sites

I used to play Doom on my 486SX at anywhere between 10 fps and 20 fps. I enjoyed it.

 

Before that I played games on 8 bit machines where the refresh rate was probably fewer than 10 per second. I enjoyed them too.

 

It's entirely subjective, and is a function of the type of game, the display technology, and the user expectations.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this