Skip to content

Avian v0.2.0

Latest
Compare
Choose a tag to compare
@Jondolf Jondolf released this 21 Dec 00:22
· 5 commits to main since this release
5b80901

Avian Physics 0.2 has been released! 🪶

Avian 0.2 cover image

Highlights

Avian 0.2 is another massive release, with several new features, quality-of-life improvements, and important bug fixes. Highlights include:

  • Reworked scheduling: Avian now runs in Bevy's FixedPostUpdate instead of having its own fixed timestep in PostUpdate, simplifying scheduling and fixing several common footguns.
  • Transform interpolation: Movement at fixed timesteps can be visually smoothed with built-in transform interpolation or extrapolation.
  • Mass property rework: Mass properties have been overhauled from the ground up to be much more intuitive, flexible, and configurable.
  • Physics picking: Colliders have a picking backend for bevy_picking.
  • Disabling physics entities: Rigid bodies, colliders, and joints can be temporarily disabled with marker components.
  • Better defaults: Collision layers, friction, and restitution now have more sensible and configurable defaults.
  • Improved 3D friction: Friction behavior in 3D is much more stable and realistic than before.
  • Limit maximum speeds: The maximum speed of rigid bodies can be easily clamped for stability and gameplay purposes.
  • Bevy 0.15 support: Avian supports the latest version of Bevy.

Check out the announcement blog post for a more in-depth overview of what has changed and why. A more complete changelog can also be found after the migration guide below.

Migration Guide

Take SpatialQueryFilter by reference in spatial queries #402

Spatial queries performed through SpatialQuery now take SpatialQueryFilter by reference.

Use hooks for component initialization #483

PrepareSet::PreInit has been renamed to PrepareSet::First, and PrepareSet::InitRigidBodies, PrepareSet::InitColliders, and PrepareSet::InitMassProperties have been removed. Most missing components are now initialized by component lifecycle hooks.

CcdPlugin and SpatialQueryPipeline no longer store a schedule and are now unit structs. Instead of SpatialQueryPlugin::new(my_schedule) or SpatialQueryPlugin::default(), just use SpatialQueryPlugin.

Use FixedPostUpdate by default and simplify scheduling #457

Previously, physics was run in PostUpdate with a custom fixed timestep by default. The primary purpose of the fixed timestep is to make behavior consistent and frame rate independent.

This custom scheduling logic has been removed, and physics now runs in Bevy's FixedPostUpdate by default. This further unifies physics with Bevy's own APIs and simplifies scheduling. However, it also means that physics now runs before Update, unlike before.

For most users, no changes should be necessary, and systems that were running in Update can remain there. If you want to run systems at the same fixed timestep as physics, consider using FixedUpdate.

The Time<Physics> clock now automatically follows the clock used by the schedule that physics is run in. In FixedPostUpdate and other schedules with a fixed timestep, Time<Fixed> is used, but if physics is instead configured to run in a schedule with a variable timestep, like PostUpdate, it will use Time<Virtual>.

Previously, the physics timestep could be configured like this:

app.insert_resource(Time::new_with(Physics::fixed_hz(60.0)));

Now, if you are running physics in FixedPostUpdate, you should simply configure Time<Fixed> directly:

app.insert_resource(Time::<Fixed>::from_hz(60.0)));

The following types and methods have also been removed as a part of this rework:

  • TimestepMode
  • Physics::from_timestep
  • Physics::fixed_hz
  • Physics::fixed_once_hz
  • Physics::variable
  • Time::<Physics>::from_timestep
  • Time::<Physics>::timestep_mode
  • Time::<Physics>::timestep_mode_mut
  • Time::<Physics>::set_timestep_mode

Previously, camera following logic had to be scheduled relative to both physics and transform propagation:

// Run after physics, before transform propagation.
app.add_systems(
    PostUpdate,
    camera_follow_player
        .after(PhysicsSet::Sync)
        .before(TransformSystem::TransformPropagate),
);

Since physics is now run in FixedPostUpdate, which is before Update, it is enough to order the system against just transform propagation:

// Note: camera following could technically be in `Update` too now.
app.add_systems(
    PostUpdate,
    camera_follow_player.before(TransformSystem::TransformPropagate),
);

Use a single layer as the default membership instead of all #476 #494

Previously, CollisionLayers defaulted to "all memberships, all filters", meaning that everything belonged to every layer and could interact with every layer. This turned out to be very limiting in practice, as it made it impossible to target things like ray casts to specific layers, unless the memberships of all colliders were set explicitly.

Now, colliders only belong to the first layer by default. This means that the first bit 0b0001 in the layer mask is reserved for the default layer.

This also applies to enum-based layers using the PhysicsLayer derive macro. To make the default layer explicit, physics layer enums must now implement Default, and specify which variant represents the default layer 0b0001.

#[derive(PhysicsLayer, Default)]
enum GameLayer {
    #[default]
    Default, // The name doesn't matter, but Default is used here for clarity
    Player,
    Enemy,
    Ground,
}

Rework mass properties #500 #532 #574

Inverse Mass Components

  • InverseMass and InverseInertia have been removed, and Inertia has been renamed to AngularInertia.
  • RigidBodyQueryItem methods effective_inv_mass and effective_world_inv_inertia have been renamed to effective_inverse_mass and effective_global_inverse_inertia.

MassPropertyPlugin

The MassPropertyPlugin is now needed to update mass properties automatically based on attached colliders. Most apps won't need to add it manually, as it is included in the PhysicsPlugins plugin group by default.

Behavior Changes

  • Mass, AngularInertia, and CenterOfMass are now optional, and can be used to override the mass properties of an entity if present, ignoring the entity's collider. Mass properties that are not set are still computed from the entity's Collider and ColliderDensity.
  • Mass properties of child entities still contribute to the total mass properties of rigid bodies by default, but the total values are stored in ComputedMass, ComputedAngularInertia, and ComputedCenterOfMass instead of Mass, AngularInertia, and CenterOfMass. The latter components are now never modified by Avian directly.
  • To prevent colliders or descendants from contributing to the total mass properties, add the NoAutoMass, NoAutoAngularInertia, and NoAutoCenterOfMass marker components to the rigid body, giving you full manual control.
  • Previously, changing Mass at runtime did not affect angular inertia. Now, it is scaled accordingly, unless NoAutoAngularInertia is present.
  • Previously, specifying the CenterOfMass at spawn did nothing unless an initial Mass was specified, even if the entity had a collider that would give it mass. This has been fixed.
  • Previously, Mass, AngularInertia, and CenterOfMass did nothing on child colliders. Now, they effectively override ColliderMassProperties when computing the total mass properties for the rigid body.
  • Previously, zero mass and angular inertia were treated as invalid. It emitted warnings, which was especially problematic and spammy for runtime collider constructors. Now, they are treated as acceptable values, and interpreted as infinite mass, like in most other engines.

API Changes

  • Mass, AngularInertia, CenterOfMass, ColliderDensity, and ColliderMassProperties now always use f32 types, even with the f64 feature. Total mass properties stored in ComputedMass, ComputedAngularInertia, and ComputedCenterOfMass still support f64.
  • In 3D, AngularInertia now stores a principal angular inertia (Vec3) and the orientation of the local inertial frame (Quat) instead of an inertia tensor (Mat3). However, several different constructors are provided, including from_tensor.
  • MassPropertiesBundle::new_computed and ColliderMassProperties::from_collider have been renamed to from_shape.
  • ColliderMassProperties now stores a MassProperties2d/MassProperties3d instead of separate properties.
  • Types implementing AnyCollider must now also implement the ComputeMassProperties2d/ComputeMassProperties3d trait instead of the mass_properties method.

Collider Constructors #540

Collider::regular_polygon and ColliderConstructor::RegularPolygon now use a u32 instead of usize for sides.

Use required components for component initialization #541

The CollidingEntities component is no longer added automatically. To read entities that are colliding with a given entity, you must now add the CollidingEntities component for it manually.

To revert to the old behavior, you can also make CollidingEntities a required component for colliders:

app.register_required_components::<Collider, CollidingEntities>();

Improvements to friction and restitution #551

Friction and Restitution are no longer inserted automatically for rigid bodies. Instead, there are now DefaultFriction and DefaultRestitution resources, which are used for bodies with no Friction or Restitution specified. These resources can be configured to change the global defaults for friction and restitution.

The default restitution is now 0.0, meaning that bodies are no longer bouncy by default. The default coefficients of friction have also been increased from 0.3 to 0.5.

Add SolverSchedulePlugin to encapsulate solver scheduling #577

System set configuration and scheduling related to the solver and substepping loop are now primarily in the new SolverSchedulePlugin. It is included in the PhysicsPlugins plugin group, so for most applications, this should not be a breaking change.

Improve SpatialQuery APIs and docs, and add more configuration #510

Shape Casting Configuration

SpatialQuery methods like cast_shape and shape_hits now take a ShapeCastConfig, which contains a lot of the existing configuration options, along with a few new options.

// Before
let hits = spatial.shape_hits(
    &Collider::sphere(0.5),
    Vec3::ZERO,
    Quat::default(),
    Dir3::ZERO,
    100.0,
    10,
    false,
    &SpatialQueryFilter::from_mask(LayerMask::ALL),
);

// After
let hits = spatial.shape_hits(
    &Collider::sphere(0.5),
    Vec3::ZERO,
    Quat::default(),
    Dir3::ZERO,
    10,
    &ShapeCastConfig::from_max_distance(100.0),
    &SpatialQueryFilter::from_mask(LayerMask::ALL),
);

Time of Impact → Distance

Spatial query APIs that mention the "time of impact" have been changed to refer to "distance". This affects names of properties and methods, such as:

  • RayCaster::max_time_of_impactRayCaster::max_distance
  • RayCaster::with_max_time_of_impactRayCaster::with_max_distance
  • ShapeCaster::max_time_of_impactShapeCaster::max_distance
  • ShapeCaster::with_max_time_of_impactShapeCaster::with_max_distance
  • RayHitData::time_of_impactRayHitData::distance
  • ShapeHitData::time_of_impactShapeHitData::distance
  • max_time_of_impact on SpatialQuery methods → RayCastConfig::max_distance or ShapeCastConfig::max_distance

This was changed because "distance" is clearer than "time of impact" for many users, and it is still an accurate term, as the cast directions in Avian are always normalized, so the "velocity" is of unit length.


What's Changed

New Contributors

Full Changelog: v0.1.2...v0.2.0