When using a variable time step, when should dt be applied? My current system works like this:

Code:

vel += accvel.x += frictionvel.y += gravitycap(vel, max_vel)

pos.x += (vel.x * dt)// check horiz ejection

pos.y += (vel.y * dt)// check vert ejection

The player has a variable jump height. The problem is that for a max jump, the apex is not consistent. I tried a few ways to cap the jump.

1. Set a timer at the start of the jump, and subtract dt from it each step. When the timer is zero, clear acc.y and let gravity take over.2. Set a jump length at the start of the jump, and subtract position delta y from it each step. When length is zero, clear acc.y and let gravity take over.

Both of these systems had the same problem, so I'm thinking I'm not factoring in dt everywhere I need to. Any thoughts?

Yes, that is a problem with Euler integration. The error varies with the size of your timestep.

If you want consistency, you can use a fixed frame step instead. You can embed a fixed step within a variable step with an accumulator: e.g. if you get a "dt" of 55, and you have a fixed step of 10, you can run 5 fixed steps, and then carry that extra 5 over for the next update.

In some cases you can use numerical methods that don't vary with the timestep size. e.g. you could use a quadratic equation to calculate your jump height, though it tends to be more difficult for these techniques to adapt to changes mid-flight.

You can also just increase the accuracy of your simulation so that variance in dt makes less of a difference. You can subdivide dt, or use a more robust integration technique, but this ultimately only reduces the error/variation. It does not completely eliminate them.

It's usually best to put some sort of cap on the maximum step size, too. If you get a sudden lag frame for whatever reason, some system interruption, etc. you don't want to run a 5 second dt all of a sudden and walk through walls.

I ended up switching to a fixed step embedded within a variable step as you described. Everything seems to be working as expected. I now need to adjust the physics because he jumps twice as high as intended, but it is consistent across workloads. Thanks.

Here's an article I found while researching your explanation, if it will help anyone else.

Physics are predictable as long as nothing changes state. If you strike a ball, it'll roll 100% predictably, as long as nothing's in its path. This is the case where a variable-length timestep would work without problems.

The problem is when you also need to detect when the ball strikes other balls, or bumps against the wall. If you're doing something like, move everything for dt, check for collisions, move everything for dt, check for collisions, etc, then the results of the collisions will depend on dt.

The ideal way to handle this is to figure out when, in simulation-time, the next state change is, and when you run the dt where that state change occurs, you recalculate your trajectories based on the exact moment inside dt the state change happens.

So if the ball is struck at 0t, and it's headed towards another ball, you can calculate that the two balls will collide at 3.6t (for example). If dt is always 1t, then the fourth call to the physics simulator will simulate the time between 3t and 4t, and we know the collision happens at 3.6t, so 4t needs to represent a state where the ball progressed for 0.6t, and then struck another ball, and the two balls progressed at new trajectories for 0.4t. One way to modify state in the middle of dt is to subdivide; run 0.6t, now you're at the time you know the collision happens, so perform your recalculations (and figure out when next state change is), then continue the simulation for 0.4t, and now you're at 4t.

It's worth noting that this is a completely internal simulation; the only external forces are when the player strikes the ball, which starts the simulation at 0t, and then the simulation runs itself only allowing internal forces to change the state, until all balls have come to rest, which marks the end of the simulation, and only then is another external force allowed to be applied. This is like deciding your move, then watching a movie play back showing what happened; you can play that movie back at any speed or framerate, and it'll always show the same thing. This is a pretty simple model useful for games like billiards, golf, bowling, etc. When you need to deal with continuous player input, everything gets more complex, because you can only check for new player input between dt's, right?

Basically, if you interpolate movement using dt, and dt is variable, then you have to be able to figure out the timing of collisions and other state changes (such as capping a jump) at a sub-dt level, and know how to apply those state changes in the middle of a dt, rather than just at the end of a dt. It's a lot of math and unless your simulation is for something simple like billiards, bowling, etc, you don't really gain a whole lot that you wouldn't from just approximating it with fixed dt's or semi-fixed dt's like in that article.

Basically, if you interpolate movement using dt, and dt is variable, then you have to be able to figure out the timing of collisions and other state changes (such as capping a jump) at a sub-dt level

As your own example illustrates, this is true of a fixed time step too.

In some simple cases, it doesn't really matter. For instance, if you collide with a wall, and the logic is "eject and stop", it doesn't really matter when in the time step it happened. But if it's "bounce off", then yeah, it makes a difference. And even with "eject and stop", corner cases might be easier to resolve with sub-step physics.

But as you say, it's more complicated and may well not be worth it in many cases if the time step is short enough and reliable enough.

Yeah, fixed timesteps are usually short enough that they work well enough. The other advantage with fixed timestamps is that the error is consistent. For example, your jump apex will always be the same, as long as the starting conditions are always the same (i.e, starting at the same Y location, the same amount of force is applied to your jump, etc), because every time you step through your jump, dt will always be exactly the same as it was the last time you stepped through your jump, so the timing of the apex will always happen X dt's after the jump started, and since the jump can only start in between dt's and not in the middle of one, everything lines up nicely.

So it's true that you still have the same flaw by not interpolating state changes at a sub-dt level, but you won't notice because the error happens the same way each time. Now your problem is when you change your game's fixed timestep, like going from 60hz to 50hz and adjusting dt to compensate, that will cause the errors to be different, though still consistent with themselves. That would result in situations where you can only clear a certain jump at one framerate and not the other.

Who is online

Users browsing this forum: No registered users and 6 guests

You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum