To produce frame rate independent behavior and deterministic results, Avian runs at a fixed timestep in FixedPostUpdate by default. However, this can often lead to visual stutter when the fixed timestep does not match the display refresh rate, especially at low physics tick rates.
Avian should support Transform interpolation to visually smooth out movement in between fixed timesteps.
Granularly interpolating only specific transform properties
Optional Hermite interpolation to produce more accurate easing and fix visual interpolation artifacts caused by very large angular velocities (ex: for car wheels or fan blades)
Custom easing backends
A new interpolation example has been added to demonstrate the new interpolation and extrapolation functionality.
Note: You can see that restitution doesn't work as well for low tick rates; this is expected.
Overview
To enable interpolation/extrapolation functionality, add the PhysicsInterpolationPlugin:
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
PhysicsPlugins::default(),
PhysicsInterpolationPlugin::default(),
))
// ...other plugins, resources, and systems
.run();
}
Interpolation and extrapolation can be enabled for individual entities using the TransformInterpolation and TransformExtrapolation components respectively:
fn setup(mut commands: Commands) {
// Enable interpolation for this rigid body.
commands.spawn((
RigidBody::Dynamic,
Transform::default(),
TransformInterpolation,
));
// Enable extrapolation for this rigid body.
commands.spawn((
RigidBody::Dynamic,
Transform::default(),
TransformExtrapolation,
));
}
Now, any changes made to the Transform of the entity in FixedPreUpdate, FixedUpdate, or FixedPostUpdate will automatically be smoothed in between fixed timesteps.
Transform properties can also be interpolated individually by adding the TranslationInterpolation, RotationInterpolation, and ScaleInterpolation components, and similarly for extrapolation.
fn setup(mut commands: Commands) {
// Only interpolate translation.
commands.spawn((Transform::default(), TranslationInterpolation));
// Only interpolate rotation.
commands.spawn((Transform::default(), RotationInterpolation));
// Only interpolate scale.
commands.spawn((Transform::default(), ScaleInterpolation));
// Mix and match!
// Extrapolate translation and interpolate rotation.
commands.spawn((
Transform::default(),
TranslationExtrapolation,
RotationInterpolation,
));
}
If you want all rigid bodies to be interpolated or extrapolated by default, you can use PhysicsInterpolationPlugin::interpolate_all() or PhysicsInterpolationPlugin::extrapolate_all():
When interpolation or extrapolation is enabled for all entities by default, you can still opt out of it for individual entities by adding the NoTransformEasing component, or the individual NoTranslationEasing, NoRotationEasing, and NoScaleEasing components.
Note that changing Transform manually in any schedule that doesn't use a fixed timestep is also supported, but it is equivalent to teleporting, and disables interpolation for the entity for the remainder of that fixed timestep.
Caveats
big_space should sort of work with bevy_transform_interpolation, but transitions between grid cells aren't eased correctly. Avian itself doesn't support big_space yet either, but it's something to keep in mind. This should be fixable on the bevy_transform_interpolation side.
bevy_transform_interpolation technically stores duplicate position data, since we could use the existing Position and Rotation components for the current "gameplay transform". However, these physics components are in global space while Transform isn't, which could complicate hierarchies. For now, I chose to accept this small amount of duplication; if it is an issue, we could make bevy_transform_interpolation accept arbitrary "position sources" similar to the "velocity sources" it already has.
The extrapolation currently doesn't integrate velocity for the prediction, so it won't account for gravity or external forces.
Note
This PR should probably not be merged yet, as bevy_transform_interpolation hasn't been released on crates.io yet, and this PR is also depending on a branch that is blocked on either Bevy 0.15 or a new RC releasing. I will merge this for the upcoming Avian release though.
Objective
Closes #444.
To produce frame rate independent behavior and deterministic results, Avian runs at a fixed timestep in
FixedPostUpdate
by default. However, this can often lead to visual stutter when the fixed timestep does not match the display refresh rate, especially at low physics tick rates.Avian should support
Transform
interpolation to visually smooth out movement in between fixed timesteps.Solution
Add a
PhysicsInterpolationPlugin
powered by my new cratebevy_transform_interpolation
! It supports:A new
interpolation
example has been added to demonstrate the new interpolation and extrapolation functionality.https://github.com/user-attachments/assets/0eac03ac-f8b3-4b82-b828-d36c0976a7cc
Note: You can see that restitution doesn't work as well for low tick rates; this is expected.
Overview
To enable interpolation/extrapolation functionality, add the
PhysicsInterpolationPlugin
:Interpolation and extrapolation can be enabled for individual entities using the
TransformInterpolation
andTransformExtrapolation
components respectively:Now, any changes made to the
Transform
of the entity inFixedPreUpdate
,FixedUpdate
, orFixedPostUpdate
will automatically be smoothed in between fixed timesteps.Transform properties can also be interpolated individually by adding the
TranslationInterpolation
,RotationInterpolation
, andScaleInterpolation
components, and similarly for extrapolation.If you want all rigid bodies to be interpolated or extrapolated by default, you can use
PhysicsInterpolationPlugin::interpolate_all()
orPhysicsInterpolationPlugin::extrapolate_all()
:When interpolation or extrapolation is enabled for all entities by default, you can still opt out of it for individual entities by adding the
NoTransformEasing
component, or the individualNoTranslationEasing
,NoRotationEasing
, andNoScaleEasing
components.Note that changing
Transform
manually in any schedule that doesn't use a fixed timestep is also supported, but it is equivalent to teleporting, and disables interpolation for the entity for the remainder of that fixed timestep.Caveats
big_space
should sort of work withbevy_transform_interpolation
, but transitions between grid cells aren't eased correctly. Avian itself doesn't supportbig_space
yet either, but it's something to keep in mind. This should be fixable on thebevy_transform_interpolation
side.bevy_transform_interpolation
technically stores duplicate position data, since we could use the existingPosition
andRotation
components for the current "gameplay transform". However, these physics components are in global space whileTransform
isn't, which could complicate hierarchies. For now, I chose to accept this small amount of duplication; if it is an issue, we could makebevy_transform_interpolation
accept arbitrary "position sources" similar to the "velocity sources" it already has.Note
This PR should probably not be merged yet, as
bevy_transform_interpolation
hasn't been released on crates.io yet, and this PR is also depending on a branch that is blocked on either Bevy 0.15 or a new RC releasing. I will merge this for the upcoming Avian release though.