pub struct TransformExtrapolationPlugin<LinVel: VelocitySource, AngVel: VelocitySource> {
pub extrapolate_translation_all: bool,
pub extrapolate_rotation_all: bool,
/* private fields */
}
Expand description
A plugin for Transform
extrapolation, making movement in FixedUpdate
appear smooth.
Transform extrapolation predicts future positions based on velocity, and applies easing
between the current and predicted Transform
in between fixed timesteps.
This results in movement that looks smooth and feels responsive, but can stutter
when the prediction is incorrect, such as when velocity changes abruptly.
This plugin requires the TransformEasingPlugin
to function. It is automatically added
if not already present in the app.
Note that unlike TransformInterpolationPlugin
, this plugin does not support scale easing.
However, the ScaleInterpolation
component can still be used even when translation and rotation are extrapolated.
§Usage
Transform extrapolation requires velocity to predict future positions.
Instead of providing its own velocity components, the TransformExtrapolationPlugin
lets you specify your own velocity components that you manage yourself.
First, make sure you have components for velocity, and implement the VelocitySource
trait on a QueryData
type:
use bevy::{ecs::query::QueryData, prelude::*};
use bevy_transform_interpolation::VelocitySource;
#[derive(Component, Default)]
struct LinearVelocity(Vec3);
#[derive(Component, Default)]
struct AngularVelocity(Vec3);
#[derive(QueryData)]
struct LinVelSource;
impl VelocitySource for LinVelSource {
// Components storing the previous and current velocities.
// Note: For extrapolation, the `Previous` component is not used, so we can make it the same as `Current`.
type Previous = LinearVelocity;
type Current = LinearVelocity;
fn previous(start: &Self::Previous) -> Vec3 {
start.0
}
fn current(end: &Self::Current) -> Vec3 {
end.0
}
}
#[derive(QueryData)]
struct AngVelSource;
impl VelocitySource for AngVelSource {
type Previous = AngularVelocity;
type Current = AngularVelocity;
fn previous(start: &Self::Previous) -> Vec3 {
start.0
}
fn current(end: &Self::Current) -> Vec3 {
end.0
}
}
Then, add the TransformExtrapolationPlugin
to the app with the velocity sources:
use bevy::{ecs::query::QueryData, prelude::*};
use bevy_transform_interpolation::{prelude::*, VelocitySource};
fn main() {
let mut app = App::new();
app.add_plugins((
TransformInterpolationPlugin::default(),
TransformExtrapolationPlugin::<LinVelSource, AngVelSource>::default(),
));
// Optional: Insert velocity components automatically for entities with extrapolation.
app.register_required_components::<TranslationExtrapolation, LinearVelocity>();
app.register_required_components::<RotationExtrapolation, AngularVelocity>();
// ...
app.run();
}
Transform extrapolation can now be enabled for a given entity by adding the TransformExtrapolation
component:
fn setup(mut commands: Commands) {
// Extrapolate translation and rotation.
commands.spawn((
Transform::default(),
TransformExtrapolation,
));
}
Now, any changes made to the translation or rotation of the entity in FixedPreUpdate
, FixedUpdate
,
or FixedPostUpdate
will automatically be smoothed in between fixed timesteps.
Transform properties can also be extrapolated individually by adding the TranslationExtrapolation
and RotationExtrapolation
components.
fn setup(mut commands: Commands) {
// Only extrapolate translation.
commands.spawn((Transform::default(), TranslationExtrapolation));
// Only extrapolate rotation.
commands.spawn((Transform::default(), RotationExtrapolation));
}
If you want all entities with a Transform
to be extrapolated by default, you can use
TransformExtrapolationPlugin::extrapolate_all()
, or set the extrapolate_translation_all
and extrapolate_rotation_all
fields.
fn main() {
App::new()
.add_plugins(TransformExtrapolationPlugin::<LinVelSource, AngVelSource> {
// Extrapolate translation by default, but not rotation.
extrapolate_translation_all: true,
extrapolate_rotation_all: false,
})
// ...
.run();
}
When 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
and NoRotationEasing
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 extrapolation for the entity for the remainder of that fixed timestep.
§Alternatives
For many applications, the stutter caused by mispredictions in extrapolation may be undesirable.
In these cases, the TransformInterpolationPlugin
can be a better alternative.
Transform interpolation eases between the previous and current Transform
,
resulting in movement that is always smooth and accurate. The downside is that the rendered
positions can lag slightly behind the true positions, making movement feel delayed.
§Easing Backends
By default, transform extrapolation uses linear interpolation (lerp
) for easing translation,
and spherical linear interpolation (slerp
) for easing rotation.
If the previous and current velocities are also available, it is possible to use Hermite interpolation
with the TransformHermiteEasingPlugin
to get smoother and more accurate easing. To enable Hermite interpolation
for extrapolation, add the TransformHermiteEasing
component to the entity in addition to the extrapolation components.
Fields§
§extrapolate_translation_all: bool
If true
, translation will be extrapolated for all entities with the Transform
component by default.
This can be overridden for individual entities by adding the NoTranslationEasing
or NoTransformEasing
component.
extrapolate_rotation_all: bool
If true
, rotation will be extrapolated for all entities with the Transform
component by default.
This can be overridden for individual entities by adding the NoRotationEasing
or NoTransformEasing
component.
Implementations§
source§impl<LinVel: VelocitySource, AngVel: VelocitySource> TransformExtrapolationPlugin<LinVel, AngVel>
impl<LinVel: VelocitySource, AngVel: VelocitySource> TransformExtrapolationPlugin<LinVel, AngVel>
sourcepub fn extrapolate_all() -> Self
pub fn extrapolate_all() -> Self
Enables extrapolation for translation and rotation for all entities with the Transform
component.
This can be overridden for individual entities by adding the NoTransformEasing
component,
or the individual NoTranslationEasing
and NoRotationEasing
components.
Trait Implementations§
source§impl<LinVel: Debug + VelocitySource, AngVel: Debug + VelocitySource> Debug for TransformExtrapolationPlugin<LinVel, AngVel>
impl<LinVel: Debug + VelocitySource, AngVel: Debug + VelocitySource> Debug for TransformExtrapolationPlugin<LinVel, AngVel>
source§impl<LinVel: VelocitySource, AngVel: VelocitySource> Default for TransformExtrapolationPlugin<LinVel, AngVel>
impl<LinVel: VelocitySource, AngVel: VelocitySource> Default for TransformExtrapolationPlugin<LinVel, AngVel>
source§impl<LinVel: VelocitySource, AngVel: VelocitySource> Plugin for TransformExtrapolationPlugin<LinVel, AngVel>
impl<LinVel: VelocitySource, AngVel: VelocitySource> Plugin for TransformExtrapolationPlugin<LinVel, AngVel>
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 ready(&self, _app: &App) -> bool
fn ready(&self, _app: &App) -> bool
finish
should be called.source§fn cleanup(&self, _app: &mut App)
fn cleanup(&self, _app: &mut App)
Auto Trait Implementations§
impl<LinVel, AngVel> Freeze for TransformExtrapolationPlugin<LinVel, AngVel>
impl<LinVel, AngVel> RefUnwindSafe for TransformExtrapolationPlugin<LinVel, AngVel>where
LinVel: RefUnwindSafe,
AngVel: RefUnwindSafe,
impl<LinVel, AngVel> Send for TransformExtrapolationPlugin<LinVel, AngVel>
impl<LinVel, AngVel> Sync for TransformExtrapolationPlugin<LinVel, AngVel>
impl<LinVel, AngVel> Unpin for TransformExtrapolationPlugin<LinVel, AngVel>
impl<LinVel, AngVel> UnwindSafe for TransformExtrapolationPlugin<LinVel, AngVel>where
LinVel: UnwindSafe,
AngVel: UnwindSafe,
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 more