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

Author Topic: Making Consistent Physics with Delta Time  (Read 6207 times)

0 Members and 1 Guest are viewing this topic.

MrProgrammer

  • Guest
Making Consistent Physics with Delta Time
« on: July 01, 2014, 08:24:55 am »
So I've got this problem where the physics in my game is inconsistent. This gif shows the player jumping at different heights. The actual values that move the player all stay the same except for delta time which the velocity is being multiplied by each frame.



As you can see, sometimes the top of the player's head clears the background thing entirely and other times it doesn't.

I'm calculating delta time based off the famous "Fix Your Timestep!" article like this:
//NOTE: I did call window.setFramerateLimit(60);

sf::Time frameTime = clock.restart();

while(frameTime.asSeconds() > 0.0f)
{
        deltaTime = std::min(frameTime, sf::Time(sf::seconds(0.5f)));
        frameTime -= deltaTime;

        for(std::vector<std::shared_ptr<State>>::iterator it = states.begin(); it != states.end(); ++it)
                (*it)->update(deltaTime);
}

window.clear(sf::Color(100, 149, 237));

for(std::vector<std::shared_ptr<State>>::iterator it = states.begin(); it != states.end(); ++it)
        (*it)->draw(window);

window.display();
 

I don't understand why this code doesn't make the physics in my game consistent. I want the player to jump the same height regardless of the frame rate. My question is how do I calculate a delta time variable that will make physics consistent?
« Last Edit: July 01, 2014, 08:27:22 am by MrProgrammer »

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Making Consistent Physics with Delta Time
« Reply #1 on: July 01, 2014, 08:32:58 am »
Your update time step doesn't look fixed to me. Your 'deltaTime' variable can have variable values.
Take a look at the implementation I did in this simple bouncing ball example : http://en.sfml-dev.org/forums/index.php?topic=15613.msg111341#msg111341 I believe that can assist you.

MrProgrammer

  • Guest
Re: Making Consistent Physics with Delta Time
« Reply #2 on: July 02, 2014, 04:27:29 am »
Alright, so after implementing Jesper Juhl's suggestion I have concluded a couple of things.

The same thing happens with high frame rates (~60fps):


With low frame rates (~10fps) the new implementation is far superior because with the method shown in my first post the player can't jump at all:


With Jesper Juhl's suggestion the player can actually jump at low frame rates:


I'm still not sure why the jump height isn't consistent, especially now that a fixed value is being used each frame. This must mean that it has to do with the code used to jump the player but I'm not sure where the fault in my code is so maybe someone can help me out.

Here is the code for player jumping:
void Player::update(sf::Time deltaTime) //The deltaTime variable is always the same now
{
        velocity.y += jump(deltaTime) + gravAccel;
        position.y += Utilities::getInstance().round(velocity.y * deltaTime.asSeconds());

        sprite.setPosition(position);
}

float Player::jump(sf::Time deltaTime)
{
        bool wasJumping = jumping;

        if(jumpPressed && onGround)
        {
                jumping = true;

        }
        else if((jumpReleased && wasJumping) || (jumpTime > maxJumpTime))
        {
                resetJumpAbility();
        }

        if(jumping)
        {
                jumpTime += deltaTime.asSeconds();

                return maxJumpTime / jumpTime * -jumpControl;
        }

        return 0.0f;
}

void Player::resetJumpAbility()
{
        jumping = false;
        jumpTime = 0.0f;
}
 
« Last Edit: July 02, 2014, 04:30:15 am by MrProgrammer »

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Making Consistent Physics with Delta Time
« Reply #3 on: July 02, 2014, 08:04:57 am »
Like Jesper said, your timestep doesn't look fixed.

Timesteps are meant to be implemented once all the way up in main() where you get deltaTime, not in every individual class that uses deltaTime.  The idea is that instead of passing the actual deltaTime to all of your game physics and simulation logic, you only ever pass in increments of a fixed size, and keep track of the "leftover" time.  So if you want your physics-rate (?) to be 20 physics per second (?), and you see a deltaTime of 35, you'd execute the physics once and set the remainder to 15.  Then on the next frame if deltaTime is 72, add that to the remainder 15 to get 87, so you execute the physics four times and set the remainder to 7.  And so on.  The key is that every physics update would get passed 1/20 no matter what (so you can easily check if you're doing it right by setting a conditional breakpoint or some cout statements).

Peteck

  • Jr. Member
  • **
  • Posts: 55
    • View Profile
Re: Making Consistent Physics with Delta Time
« Reply #4 on: July 02, 2014, 08:30:36 am »
void Player::update(sf::Time deltaTime)
{
        ..
}

float Player::jump(sf::Time deltaTime)
{
        ...
}
 

Do yourself a favor and use a constant reference for your delta-time parameter, like so
void Player::update(const sf::Time &deltaTime)
{
        ..
}

float Player::jump(const sf::Time &deltaTime)
{
        ...
}
 
Your code as it is right now, may not effect the delta-time. But if you don't plan to ever modify the delta-time, a constant reference will be a good thing to do for extra safety against the human race and better efficiency in terms of memory usage :)

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Making Consistent Physics with Delta Time
« Reply #5 on: July 02, 2014, 08:37:33 am »
Quote
Do yourself a favor and use a constant reference for your delta-time parameter
Small simple types can be passed by copy. No need to be picky about that. There are much more relevant things to say about his code, related to his problem ;)
Laurent Gomila - SFML developer

Peteck

  • Jr. Member
  • **
  • Posts: 55
    • View Profile
Re: Making Consistent Physics with Delta Time
« Reply #6 on: July 02, 2014, 09:13:20 am »
jumpTime += deltaTime.asSeconds();
return maxJumpTime / jumpTime * -jumpControl;
Try putting a clamp on the jumpTime which will clamp the value to the maxJumpTime.
And then also change this
(jumpTime > maxJumpTime)
to be
(jumpTime >= maxJumpTime)

The problem I see in your code is that jumpTime don't get limited. So if you max is 5, jumpTime can be 5.5 before resetting, or it could be 5,4 and so on. This is what I think is the cause of your bug.


No need to be picky about that.
Didn't mean to be picky, just to give a friendly advice.

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Making Consistent Physics with Delta Time
« Reply #7 on: July 02, 2014, 09:18:50 am »
Do yourself a favor and use a constant reference for your delta-time parameter ...

Or better yet, don't pass in anything at all. The whole point of a fixed time step is that it is fixed and never changes. So all you should need is a global constant somewhere, like:

  const sf::Time PHYSICS_TIME_STEP = sf::seconds(1.f / 30.f); // or whatever value you want for the physics steps

MrProgrammer

  • Guest
Re: Making Consistent Physics with Delta Time
« Reply #8 on: July 02, 2014, 08:58:01 pm »
jumpTime += deltaTime.asSeconds();
return maxJumpTime / jumpTime * -jumpControl;
Try putting a clamp on the jumpTime which will clamp the value to the maxJumpTime.
And then also change this
(jumpTime > maxJumpTime)
to be
(jumpTime >= maxJumpTime)

The problem I see in your code is that jumpTime don't get limited. So if you max is 5, jumpTime can be 5.5 before resetting, or it could be 5,4 and so on. This is what I think is the cause of your bug.

Of course! How could I miss something so trivial!

Thanks everyone for your help, this issue has been resolved now.

As for the delta time variable I'm passing into each update function. I will remove the parameter it and make a global constant somewhere because it never changes.

Mörkö

  • Jr. Member
  • **
  • Posts: 96
    • View Profile
Re: Making Consistent Physics with Delta Time
« Reply #9 on: July 02, 2014, 10:12:29 pm »
I don't understand why it would be better to add a fixed amount to jumpTime rather than adding deltaTime.

The point of using deltaTime rather than a fixed amount is to guarantee that the physics are updated the same amount regardless of game FPS, am I wrong?

If you are able to run the update function with exactly the same delta time difference every cycle, then yes there is no need to pass it anything. Maybe that's possible in a trivial systems, but in a dynamic game I don't think you can be sure of that.

Maybe I have misunderstood what you are talking about. Please clarify why it's recommended to use a fixed modifier for updating physics, rather than calculating the modifier based on delta time.

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Making Consistent Physics with Delta Time
« Reply #10 on: July 02, 2014, 10:24:06 pm »
Yes, there is a misunderstanding.  Let's try this...

Let's say you experience frames lasting 100, 200 and 50 milliseconds each.  What happens:

1) Naive approach - You move everything X pixels each frame.  The result is that the movement speed is completely different every frame.  This is almost always a bad thing.

2) DeltaTime - You move everything deltaTime * X pixels each frame, where deltaTime is 100, then 200, the 50, assuming you're timing frames correctly.  This way, the movement speed is the same every frame.  For simple applications, this is often enough.

But it's not perfect.  As an extremely simple example, let's say that a collision is happening from t=150 to t=250 milliseconds.  Your movement logic gets the deltaTimes 100, 200 and 50, so it only checks t=100, t=300 and t=350, and thus completely misses this collision.  A different set of deltaTimes would result in a very different outcome.  Throw in the complexity of a more realistic game and things can get pretty inconsistent to say the least.

3) Fixed timestep - You move everything X pixels at a time, but how many times you do that each frame varies based on the deltaTime.  Let's say you want 20 game logic "frames" per second, or one iteration of your game logic every 50 milliseconds.  Then on the 100ms frame, you execute the game logic twice.  On the 200ms frame, you execute it four times.  On the 50ms frame, you execute it once.  If you get a frame of, say, 75ms, you execute it once and store a remainder of 25, which gets added to the time of the next frame (so you never "leak" any time).

Hopefully it's obvious why this solves the problem described above.  As an additional bonus, I'm fairly sure you need a fixed timestep in order to implement replays or have time-rewinding mechanics, and it probably helps a lot with keeping networked multiplayer games in sync too.
« Last Edit: July 02, 2014, 10:30:10 pm by Ixrec »