-
-
Notifications
You must be signed in to change notification settings - Fork 261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interpolate simulation state at render time #19
Conversation
This commit follows the suggestion from: https://www.gafferongames.com/post/fix_your_timestep/ The problem is that the simulation is stepped with a fixed time step, as it should be to maintain simulation stability. However, the time step of each game loop may vary based on external factors to the physics engine. For example, if the timestep is 1/60 but the game loop iterates at a faster rate, then the physics simulation will appear to advance faster than real-time. The idea for the solution is that the renderer / game loop 'produces' time, and the simulation consumes it. As such, a new resource has been added to keep track of the amount of time produced and consumed. It represents how far ahead of the simulation the rendering is. The step_world_system has been modified to add Time.delta_seconds to the SimulationToRenderTime resource, and then if there is more than a time step of time to simulate, we loop through stepping the simulation and subtracting the simulation time step from the resource until there is less than a time step of time remaining to simulate. As noted in the article, if this was all we did then the motion of objects due to physics would look jittery. We have to interpolate the positions and orientations of simulated objects between each simulation step. The sync_transform_system has been modified to predict the position and rotation of each body based on its current position and rotation, linear velocity, and angular velocity. Ideally the linear and angular accelerations would be taken into account but these are not public.
@@ -60,12 +60,11 @@ pub fn create_joints_system( | |||
|
|||
/// System responsible for performing one timestep of the physics world. | |||
pub fn step_world_system( | |||
(time, mut sim_to_render_time): (Res<Time>, ResMut<SimulationToRenderTime>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that these variables had to be made into tuples of resources to work around the limit of 10 parameters to a Bevy system function.
src/physics/systems.rs
Outdated
rb.position.translation.vector.y, | ||
); | ||
let vel_0 = Vec2::new(rb.linvel.x, rb.linvel.y); | ||
let pos_t = pos_0 + vel_0 * dt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like extrapolation rather than interpolation, which could show an entity at a position it will never be at in case of sudden turns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. So then input events between simulation steps need to be taken into account for the rendering.
I was thinking it would be useful to ask the physics engine to predict the position of the body at t seconds ahead of where the simulation is at.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to take acceleration state into account for the extrapolation as well but those fields weren’t accessible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at the gaffer on games article again and it seems they suggest interpolating between the previous physics state and the current based on the time remaining in the accumulator. This seems backward in time to me. We just simulated the current state based on input that happened before the current simulation time, whatever remains in the accumulator is time into the future as far as the simulation is concerned, but the article suggests interpolating between two past points in time. It seems it’s suggesting to introduce a time step of lag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now rewritten to do what the article suggested - store the previous position and rotation, lerp the position, and slerp the rotation. It introduces a physics time step of lag though. :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this PR.
The extrapolation part is slightly wrong. The angular velocity of a rigid-body is expressed at its center-of-mass. So the rotation cannot be integrated by just multiplying the angular velocity by dt
. In fact it should be taken into account the way we do in Rapier there.
Anyaway, @kabergstrom pointed out, what you are doing here is an extrapolation, which will always yield wrong positions because it ignores all contacts/joints. What it done in the GafferOnGames article is an interpolation between the last state and the current state. So instead of integrating the velocities, I think you should get the new rigid-body position, and interpolate between the previous transform and the new one.
This introduces a physics-step of lag.
Using glam types as this will be called from code using Bevy.
Co-authored-by: Sébastien Crozet <sebastien@crozet.re>
All good, thanks! |
This commit follows the suggestion from:
https://www.gafferongames.com/post/fix_your_timestep/
The problem is that the simulation is stepped with a fixed time step, as it
should be to maintain simulation stability. However, the time step of each game
loop may vary based on external factors to the physics engine. For example, if
the timestep is 1/60 but the game loop iterates at a faster rate, then the
physics simulation will appear to advance faster than real-time.
The idea for the solution is that the renderer / game loop 'produces' time, and
the simulation consumes it. As such, a new resource has been added to keep
track of the amount of time produced and consumed. It represents how far ahead
of the simulation the rendering is.
The step_world_system has been modified to add Time.delta_seconds to the
SimulationToRenderTime resource, and then if there is more than a time step of
time to simulate, we loop through stepping the simulation and subtracting the
simulation time step from the resource until there is less than a time step of
time remaining to simulate.
As noted in the article, if this was all we did then the motion of objects due
to physics would look jittery. We have to interpolate the positions and
orientations of simulated objects between each simulation step. The
sync_transform_system has been modified to lerp and slerp the position and rotation of
each body based on its previous and current simulation position and rotation.