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 avian3d::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>
. Box<dyn Any>
can
then be further downcast
into Box<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>
. Rc<Any>
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> 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> 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> Pointable for T
impl<T> Pointable for T
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.