MoveAndSlide

Struct MoveAndSlide 

Source
pub struct MoveAndSlide<'w, 's> {
    pub spatial_query: SpatialQuery<'w, 's>,
    pub colliders: Query<'w, 's, (&'static Collider, &'static Position, &'static Rotation, Option<&'static CollisionLayers>), (With<ColliderOf>, Without<Sensor>)>,
    pub length_unit: Res<'w, PhysicsLengthUnit>,
}
Expand description

A SystemParam for the move and slide algorithm, also known as collide and slide or step slide.

Move and slide is the core movement and collision algorithm used by most kinematic character controllers. It attempts to move a shape along a desired velocity vector, sliding along any colliders that are hit on the way.

§Algorithm

At a high level, the algorithm works as follows:

  1. Sweep the shape along the desired velocity vector.
  2. If no collision is detected, move the full distance.
  3. If a collision is detected:
    • Move up to the point of collision.
    • Project the remaining velocity onto the contact surfaces to obtain a new sliding velocity.
  4. Repeat with the new sliding velocity until movement is complete.

The algorithm also includes depenetration passes before and after movement to improve stability and ensure that the shape does not intersect any colliders.

§Configuration

MoveAndSlideConfig allows configuring various aspects of the algorithm. See its documentation for more information.

Additionally, move_and_slide can be given a callback that is called for each contact surface that is detected during movement. This allows for custom handling of collisions, such as triggering events, or modifying movement based on specific colliders.

§Other Utilities

In addition to the main move_and_slide method, this system parameter also provides utilities for:

  • Performing shape casts optimized for movement via cast_move.
  • Depenetrating shapes that are intersecting colliders via depenetrate.
  • Performing intersection tests via intersections.
  • Projecting velocities to slide along contact planes via project_velocity.

These methods are used internally by the move and slide algorithm, but can also be used independently for custom movement and collision handling.

§Resources

Some useful resources for learning more about the move and slide algorithm include:

Note that while the high-level concepts are similar across different implementations, details may vary.

Fields§

§spatial_query: SpatialQuery<'w, 's>

The SpatialQuery system parameter used to perform shape casts and other geometric queries.

§colliders: Query<'w, 's, (&'static Collider, &'static Position, &'static Rotation, Option<&'static CollisionLayers>), (With<ColliderOf>, Without<Sensor>)>

The Query used to query for colliders.

§length_unit: Res<'w, PhysicsLengthUnit>

A units-per-meter scaling factor that adjusts some thresholds and tolerances to the scale of the world for better behavior.

Implementations§

Source§

impl<'w, 's> MoveAndSlide<'w, 's>

Source

pub fn move_and_slide( &self, shape: &Collider, shape_position: Vector, shape_rotation: Quaternion, velocity: Vector, delta_time: Duration, config: &MoveAndSlideConfig, filter: &SpatialQueryFilter, on_hit: impl FnMut(MoveAndSlideHitData<'_>) -> MoveAndSlideHitResponse, ) -> MoveAndSlideOutput

Moves a shape along a given velocity vector, sliding along any colliders that are hit on the way.

See MoveAndSlide for an overview of the algorithm.

§Arguments
  • shape: The shape being cast represented as a Collider.
  • shape_position: Where the shape is cast from.
  • shape_rotation: The rotation of the shape being cast.
  • velocity: The initial velocity vector along which to move the shape. This will be modified to reflect sliding along surfaces.
  • delta_time: The duration over which to move the shape. velocity * delta_time gives the total desired movement vector.
  • config: A MoveAndSlideConfig that determines the behavior of the move and slide. MoveAndSlideConfig::default() should be a good start for most cases.
  • filter: A SpatialQueryFilter that determines which colliders are taken into account in the query. It is highly recommended to exclude the entity holding the collider itself, otherwise the character will collide with itself.
  • on_hit: A callback that is called when a collider is hit as part of the move and slide iterations. The returned MoveAndSlideHitResponse determines how to handle the hit. If you don’t have any special handling per collision, you can pass |_| MoveAndSlideHitResponse::Accept.
§Example
use bevy::prelude::*;
use std::collections::HashSet;
use avian3d::{prelude::*, math::{Vector, AdjustPrecision as _, AsF32 as _}};

#[derive(Component)]
struct CharacterController {
    velocity: Vector,
}

fn perform_move_and_slide(
    player: Single<(Entity, &Collider, &mut CharacterController, &mut Transform)>,
    move_and_slide: MoveAndSlide,
    time: Res<Time>
) {
    let (entity, collider, mut controller, mut transform) = player.into_inner();
    let velocity = controller.velocity + Vector::X * 10.0;
    let filter = SpatialQueryFilter::from_excluded_entities([entity]);
    let mut collisions = HashSet::new();
    let out = move_and_slide.move_and_slide(
        collider,
         transform.translation.adjust_precision(),
         transform.rotation.adjust_precision(),
        velocity,
        time.delta(),
        &MoveAndSlideConfig::default(),
        &filter,
        |hit| {
            collisions.insert(hit.entity);
            MoveAndSlideHitResponse::Accept
        },
    );
     transform.translation = out.position.f32();
    controller.velocity = out.projected_velocity;
    info!("Colliding with entities: {:?}", collisions);
}
Source

pub fn cast_move( &self, shape: &Collider, shape_position: Vector, shape_rotation: Quaternion, movement: Vector, skin_width: Scalar, filter: &SpatialQueryFilter, ) -> Option<MoveHitData>

A shape cast optimized for movement. Use this if you want to move a collider with a given velocity and stop so that it keeps a distance of skin_width from the first collider on its path.

This operation is most useful when you ensure that the character is not intersecting any colliders before moving. To do this, call MoveAndSlide::depenetrate and add the resulting offset vector to the character’s position before calling this method. See the example below.

It is often useful to clip the velocity afterwards so that it no longer points into the contact plane using Self::project_velocity.

§Arguments
  • shape: The shape being cast represented as a Collider.
  • shape_position: Where the shape is cast from.
  • shape_rotation: The rotation of the shape being cast.
  • movement: The direction and magnitude of the movement. If this is Vector::ZERO, this method can still return Some(MoveHitData) if the shape started off intersecting a collider.
  • skin_width: A ShapeCastConfig that determines the behavior of the cast.
  • filter: A SpatialQueryFilter that determines which colliders are taken into account in the query. It is highly recommended to exclude the entity holding the collider itself, otherwise the character will collide with itself.
§Returns
  • Some(MoveHitData) if the shape hit a collider on the way, or started off intersecting a collider.
  • None if the shape is able to move the full distance without hitting a collider.
§Example
use bevy::prelude::*;
use avian3d::{prelude::*, math::{Vector, Dir, AdjustPrecision as _, AsF32 as _}};

#[derive(Component)]
struct CharacterController {
    velocity: Vector,
}

fn perform_cast_move(
    player: Single<(Entity, &Collider, &mut CharacterController, &mut Transform)>,
    move_and_slide: MoveAndSlide,
    time: Res<Time>
) {
    let (entity, collider, mut controller, mut transform) = player.into_inner();
    let filter = SpatialQueryFilter::from_excluded_entities([entity]);
    let config = MoveAndSlideConfig::default();

    // Ensure that the character is not intersecting with any colliders.
    let offset = move_and_slide.depenetrate(
        collider,
         transform.translation.adjust_precision(),
         transform.rotation.adjust_precision(),
        &((&config).into()),
        &filter,
    );
     transform.translation += offset.f32();
    let velocity = controller.velocity;

    let hit = move_and_slide.cast_move(
        collider,
         transform.translation.adjust_precision(),
         transform.rotation.adjust_precision(),
        velocity * time.delta_secs().adjust_precision(),
        config.skin_width,
        &filter,
    );
    if let Some(hit) = hit {
        // We collided with something on the way. Advance as much as possible.
         transform.translation += (velocity.normalize_or_zero() * hit.distance).f32();
        // Then project the velocity to make sure it no longer points towards the contact plane.
        controller.velocity =
            MoveAndSlide::project_velocity(velocity, &[Dir::new_unchecked(hit.normal1.f32())])
    } else {
        // We traveled the full distance without colliding.
         transform.translation += velocity.f32();
    }
}
Source

pub fn depenetrate( &self, shape: &Collider, shape_position: Vector, shape_rotation: Quaternion, config: &DepenetrationConfig, filter: &SpatialQueryFilter, ) -> Vector

Moves a collider so that it no longer intersects any other collider and keeps a minimum distance of DepenetrationConfig::skin_width scaled by the PhysicsLengthUnit.

Depenetration is an iterative process that solves penetrations for all planes, until we either reached MoveAndSlideConfig::move_and_slide_iterations or the accumulated error is less than MoveAndSlideConfig::max_depenetration_error. If the maximum number of iterations was reached before the error is below the threshold, the current best attempt is returned, in which case the collider may still be intersecting with other colliders.

This method is equivalent to calling Self::depenetrate_intersections with the results of Self::intersections.

§Arguments
  • shape: The shape that intersections are tested against represented as a Collider.
  • shape_position: The position of the shape.
  • shape_rotation: The rotation of the shape.
  • config: A DepenetrationConfig that determines the behavior of the depenetration. DepenetrationConfig::default() should be a good start for most cases.
  • filter: A SpatialQueryFilter that determines which colliders are taken into account in the query.
§Example
use bevy::prelude::*;
use avian3d::{prelude::*, character_controller::move_and_slide::DepenetrationConfig, math::{AdjustPrecision as _, AsF32 as _}};
fn depenetrate_player(
    player: Single<(Entity, &Collider, &mut Transform)>,
    move_and_slide: MoveAndSlide,
    time: Res<Time>
) {
    let (entity, collider, mut transform) = player.into_inner();
    let filter = SpatialQueryFilter::from_excluded_entities([entity]);

    let offset = move_and_slide.depenetrate(
        collider,
         transform.translation.adjust_precision(),
         transform.rotation.adjust_precision(),
        &DepenetrationConfig::default(),
        &filter,
    );
     transform.translation += offset.f32();
}

See also MoveAndSlide::cast_move for a typical usage scenario.

Source

pub fn depenetrate_intersections( &self, config: &DepenetrationConfig, intersections: &[(Dir, Scalar)], ) -> Vector

Manual version of MoveAndSlide::depenetrate.

Moves a collider so that it no longer intersects any other collider and keeps a minimum distance of DepenetrationConfig::skin_width scaled by the PhysicsLengthUnit. The intersections should be provided as a list of contact plane normals and penetration distances, which can be obtained via MoveAndSlide::intersections.

Depenetration is an iterative process that solves penetrations for all planes, until we either reached MoveAndSlideConfig::move_and_slide_iterations or the accumulated error is less than MoveAndSlideConfig::max_depenetration_error. If the maximum number of iterations was reached before the error is below the threshold, the current best attempt is returned, in which case the collider may still be intersecting with other colliders.

§Arguments
  • config: A DepenetrationConfig that determines the behavior of the depenetration. DepenetrationConfig::default() should be a good start for most cases.
  • intersections: A list of contact plane normals and penetration distances representing the intersections to resolve.
§Returns

A displacement vector that can be added to the shape_position to resolve the intersections, or the best attempt if the max iterations were reached before a solution was found.

§Example
use bevy::prelude::*;
use avian3d::{prelude::*, character_controller::move_and_slide::DepenetrationConfig, math::{AdjustPrecision as _, AsF32 as _}};
fn depenetrate_player_manually(
    player: Single<(Entity, &Collider, &mut Transform)>,
    move_and_slide: MoveAndSlide,
    time: Res<Time>
) {
    let (entity, collider, mut transform) = player.into_inner();
    let filter = SpatialQueryFilter::from_excluded_entities([entity]);
    let config = DepenetrationConfig::default();

    let mut intersections = Vec::new();
    move_and_slide.intersections(
        collider,
         transform.translation.adjust_precision(),
         transform.rotation.adjust_precision(),
        config.skin_width,
        &filter,
        |contact_point, normal| {
            intersections.push((normal, contact_point.penetration + config.skin_width));
            true
        },
    );
    let offset = move_and_slide.depenetrate_intersections(&config, &intersections);
     transform.translation += offset.f32();
}
Source

pub fn intersections( &self, shape: &Collider, shape_position: Vector, shape_rotation: Quaternion, prediction_distance: Scalar, filter: &SpatialQueryFilter, callback: impl FnMut(&ContactPoint, Dir) -> bool, )

An intersection test that calls a callback for each Collider found that is closer to the given shape with a given position and rotation than prediction_distance.

§Arguments
  • shape: The shape that intersections are tested against represented as a Collider.
  • shape_position: The position of the shape.
  • shape_rotation: The rotation of the shape.
  • filter: A SpatialQueryFilter that determines which colliders are taken into account in the query.
  • prediction_distance: An extra margin applied to the Collider.
  • callback: A callback that is called for each intersection found. The callback receives the deepest contact point and the contact normal. Returning false will stop further processing of intersections.
§Example

See MoveAndSlide::depenetrate_intersections for a typical usage scenario.

Source

pub fn project_velocity(v: Vector, normals: &[Dir]) -> Vector

Projects input velocity v onto the planes defined by the given normals. This ensures that velocity does not point into any of the planes, but along them.

This is often used after MoveAndSlide::cast_move to ensure a character moved that way does not try to continue moving into colliding geometry.

Trait Implementations§

Source§

impl SystemParam for MoveAndSlide<'_, '_>

Source§

type State = FetchState

Used to store data which persists across invocations of a system.
Source§

type Item<'w, 's> = MoveAndSlide<'w, 's>

The item type returned when constructing this system param. The value of this associated type should be Self, instantiated with new lifetimes. Read more
Source§

fn init_state(world: &mut World) -> Self::State

Creates a new instance of this param’s State.
Source§

fn init_access( state: &Self::State, system_meta: &mut SystemMeta, component_access_set: &mut FilteredAccessSet, world: &mut World, )

Registers any World access used by this SystemParam
Source§

fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World)

Applies any deferred mutations stored in this SystemParam’s state. This is used to apply Commands during ApplyDeferred.
Source§

fn queue( state: &mut Self::State, system_meta: &SystemMeta, world: DeferredWorld<'_>, )

Queues any deferred mutations to be applied at the next ApplyDeferred.
Source§

unsafe fn validate_param<'w, 's>( state: &'s mut Self::State, _system_meta: &SystemMeta, _world: UnsafeWorldCell<'w>, ) -> Result<(), SystemParamValidationError>

Validates that the param can be acquired by the get_param. Read more
Source§

unsafe fn get_param<'w, 's>( state: &'s mut Self::State, system_meta: &SystemMeta, world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> Self::Item<'w, 's>

Creates a parameter to be passed into a SystemParamFunction. Read more
Source§

impl<'w, 's> ReadOnlySystemParam for MoveAndSlide<'w, 's>

Auto Trait Implementations§

§

impl<'w, 's> Freeze for MoveAndSlide<'w, 's>

§

impl<'w, 's> !RefUnwindSafe for MoveAndSlide<'w, 's>

§

impl<'w, 's> Send for MoveAndSlide<'w, 's>

§

impl<'w, 's> Sync for MoveAndSlide<'w, 's>

§

impl<'w, 's> Unpin for MoveAndSlide<'w, 's>

§

impl<'w, 's> !UnwindSafe for MoveAndSlide<'w, 's>

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 for T
where T: Any,

Source§

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

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

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

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

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

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Send + Sync>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

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

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be 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, W> HasTypeWitness<W> for T
where W: MakeTypeWitness<Arg = T>, T: ?Sized,

Source§

const WITNESS: W = W::MAKE

A constant of the type witness
Source§

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

Source§

const TYPE_EQ: TypeEq<T, <T as Identity>::Type> = TypeEq::NEW

Proof that Self is the same type as Self::Type, provides methods for casting between Self and Self::Type.
Source§

type Type = T

The same type as Self, used to emulate type equality bounds (T == U) with associated type equality constraints (T: Identity<Type = U>).
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> IntoResult<T> for T

Source§

fn into_result(self) -> Result<T, RunSystemError>

Converts this type into the system output type.
Source§

impl<A> Is for A
where A: Any,

Source§

fn is<T>() -> bool
where T: Any,

Checks if the current type “is” another type, using a TypeId equality comparison. This is most useful in the context of generic logic. Read more
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
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> 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<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,