pub struct PhysicsInterpolationPlugin { /* private fields */ }Expand description
A plugin for Transform interpolation and extrapolation for rigid bodies.
§Overview
To make behavior deterministic and independent of frame rate, Avian runs physics at a fixed timestep
in FixedPostUpdate by default. However, when this timestep doesn’t match the display frame rate,
movement can appear choppy, especially on displays with high refresh rates.
The conventional solution is to ease transforms in between physics ticks to smooth out the visual result. This can be done using either interpolation or extrapolation.
§Interpolation
Transform interpolation computes a Transform that is somewhere in between the current position
and the position from the previous physics tick. This produces smooth and accurate movement.
The downside of interpolation is that it causes rendering to be slightly behind the physics simulation. This can make movement feel slightly delayed, but this is rarely noticeable unless using a very small physics tick rate.
§Extrapolation
Transform extrapolation computes a Transform that is somewhere in between the current position
and a future position predicted based on velocity. This produces movement that looks smooth and feels
very responsive.
The downside of extrapolation is that it can be less accurate. When the prediction is wrong, the rendered positions may jump to correct the mispredictions. This can be noticeable when the entity changes direction or speed rapidly.
Extrapolation is primarily inteded for cases where low latency and high responsiveness are crucial for gameplay, such as first-person shooters and racing games. For most other games, interpolation is often the better choice.
§Usage
The PhysicsInterpolationPlugin is included in the PhysicsPlugins by default,
so most apps don’t need to add it manually.
Transform interpolation and extrapolation can be enabled for individual entities
using the TransformInterpolation and TransformExtrapolation components respectively:
use avian2d::prelude::*;
use bevy::prelude::*;
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():
fn main() {
App::new()
.add_plugins(PhysicsPlugins::default().set(PhysicsInterpolationPlugin::interpolate_all()))
// ...
.run();
}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.
§Hermite Interpolation
By default, linear interpolation (lerp) is used for easing translation and scale,
and spherical linear interpolation (slerp) is used for easing rotation.
This is computationally efficient and works well for most cases.
However, linear interpolation doesn’t consider velocity, which can make trajectories look less smooth
at low tick rates. Very high angular velocities (ex: for car wheels or fan blades) can be especially problematic,
as slerp always takes the shortest path between two rotations, which can sometimes cause entities to rotate
in the opposite direction.
Unlike linear interpolation, Hermite interpolation uses both position and velocity information
to estimate the trajectories of entities, producing smoother results. To enable it for interpolation
or extrapolation, add the TransformHermiteEasing component or the individual TranslationHermiteEasing
and RotationHermiteEasing components:
fn setup(mut commands: Commands) {
// Enable Hermite interpolation for this rigid body.
commands.spawn((
RigidBody::Dynamic,
Transform::default(),
TransformInterpolation,
TransformHermiteEasing,
));
}Hermite interpolation is more expensive than linear interpolation, so it is generally recommended to only use it when it produces noticeable benefits. For most cases, linear interpolation should be sufficient.
Note that scale interpolation is always linear, and does not support Hermite interpolation.
§General Interpolation or Extrapolation
Avian uses bevy_transform_interpolation for interpolation and extrapolation.
It is not limited to physics entities, so it is actually possible to use the components
shown here for interpolating the Transform of any entity!
Refer to the bevy_transform_interpolation documentation for more information on how to use it.
Implementations§
Source§impl PhysicsInterpolationPlugin
impl PhysicsInterpolationPlugin
Sourcepub const fn interpolate_all() -> Self
pub const fn interpolate_all() -> Self
Enables interpolation of translation and rotation for all rigid bodies.
This can be overridden for individual entities by adding the NoTransformEasing component,
or the individual NoTranslationEasing and NoRotationEasing components.
Sourcepub const fn interpolate_translation_all() -> Self
pub const fn interpolate_translation_all() -> Self
Enables interpolation of translation for all rigid bodies.
This can be overridden for individual entities by adding the NoTranslationEasing component.
Sourcepub const fn interpolate_rotation_all() -> Self
pub const fn interpolate_rotation_all() -> Self
Enables interpolation of rotation for all rigid bodies.
This can be overridden for individual entities by adding the NoRotationEasing component.
Sourcepub const fn extrapolate_all() -> Self
pub const fn extrapolate_all() -> Self
Enables extrapolation of translation and rotation for all rigid bodies.
This can be overridden for individual entities by adding the NoTransformEasing component,
or the individual NoTranslationEasing and NoRotationEasing components.
Sourcepub const fn extrapolate_translation_all() -> Self
pub const fn extrapolate_translation_all() -> Self
Enables extrapolation of translation for all rigid bodies.
This can be overridden for individual entities by adding the NoTranslationEasing component.
Sourcepub const fn extrapolate_rotation_all() -> Self
pub const fn extrapolate_rotation_all() -> Self
Enables extrapolation of rotation for all rigid bodies.
This can be overridden for individual entities by adding the NoRotationEasing component.
Trait Implementations§
Source§impl Debug for PhysicsInterpolationPlugin
impl Debug for PhysicsInterpolationPlugin
Source§impl Default for PhysicsInterpolationPlugin
impl Default for PhysicsInterpolationPlugin
Source§fn default() -> PhysicsInterpolationPlugin
fn default() -> PhysicsInterpolationPlugin
Source§impl Plugin for PhysicsInterpolationPlugin
impl Plugin for PhysicsInterpolationPlugin
Source§fn ready(&self, _app: &App) -> bool
fn ready(&self, _app: &App) -> bool
finish should be called.Source§fn finish(&self, _app: &mut App)
fn finish(&self, _app: &mut App)
App, once all plugins registered are ready. This can
be useful for plugins that depends on another plugin asynchronous setup, like the renderer.Source§fn cleanup(&self, _app: &mut App)
fn cleanup(&self, _app: &mut App)
Auto Trait Implementations§
impl Freeze for PhysicsInterpolationPlugin
impl RefUnwindSafe for PhysicsInterpolationPlugin
impl Send for PhysicsInterpolationPlugin
impl Sync for PhysicsInterpolationPlugin
impl Unpin for PhysicsInterpolationPlugin
impl UnwindSafe for PhysicsInterpolationPlugin
Blanket Implementations§
Source§impl<T, U> AsBindGroupShaderType<U> for T
impl<T, U> AsBindGroupShaderType<U> for T
Source§fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
T ShaderType for self. When used in AsBindGroup
derives, it is safe to assume that all images in self exist.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> FromWorld for Twhere
T: Default,
impl<T> FromWorld for Twhere
T: Default,
Source§fn from_world(_world: &mut World) -> T
fn from_world(_world: &mut World) -> T
Creates Self using default().
Source§impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
Source§fn into_result(self) -> Result<T, RunSystemError>
fn into_result(self) -> Result<T, RunSystemError>
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.