bevy_transform_interpolation::extrapolation

Struct TransformExtrapolationPlugin

source
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>

source

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>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<LinVel: VelocitySource, AngVel: VelocitySource> Default for TransformExtrapolationPlugin<LinVel, AngVel>

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<LinVel: VelocitySource, AngVel: VelocitySource> Plugin for TransformExtrapolationPlugin<LinVel, AngVel>

source§

fn build(&self, app: &mut App)

Configures the App to which this plugin is added.
source§

fn finish(&self, app: &mut App)

Finish adding this plugin to the 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

Has the plugin finished its setup? This can be useful for plugins that need something asynchronous to happen before they can finish their setup, like the initialization of a renderer. Once the plugin is ready, finish should be called.
source§

fn cleanup(&self, _app: &mut App)

Runs after all plugins are built and finished, but before the app schedule is executed. This can be useful if you have some resource that other plugins need during their build step, but after build you want to remove it and send it to another thread.
source§

fn name(&self) -> &str

Configures a name for the Plugin which is primarily used for checking plugin uniqueness and debugging.
source§

fn is_unique(&self) -> bool

If the plugin can be meaningfully instantiated several times in an App, override this method to return false.

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>
where LinVel: Unpin, AngVel: Unpin,

§

impl<LinVel, AngVel> UnwindSafe for TransformExtrapolationPlugin<LinVel, AngVel>
where LinVel: UnwindSafe, AngVel: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T, U> AsBindGroupShaderType<U> for T
where U: ShaderType, &'a T: for<'a> Into<U>,

source§

fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U

Return the T ShaderType for self. When used in AsBindGroup derives, it is safe to assume that all images in self exist.
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> Downcast<T> for T

source§

fn downcast(&self) -> &T

source§

impl<T> Downcast for T
where T: Any,

source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert 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>

Convert 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)

Convert &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)

Convert &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
where T: Any + Send + Sync,

source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> FromWorld for T
where T: Default,

source§

fn from_world(_world: &mut World) -> T

Creates Self using default().

source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

source§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> Upcast<T> for T

source§

fn upcast(&self) -> Option<&T>

source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

source§

fn vzip(self) -> V

source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

impl<T> ConditionalSend for T
where T: Send,

source§

impl<Marker, T> Plugins<Marker> for T
where T: Plugins<Marker>,

source§

impl<T> Settings for T
where T: 'static + Send + Sync,

source§

impl<T> WasmNotSend for T
where T: Send,

source§

impl<T> WasmNotSendSync for T

source§

impl<T> WasmNotSync for T
where T: Sync,