Avian Physics 0.2 has been released! 🪶
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 inPostUpdate
, 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
andInverseInertia
have been removed, andInertia
has been renamed toAngularInertia
.RigidBodyQueryItem
methodseffective_inv_mass
andeffective_world_inv_inertia
have been renamed toeffective_inverse_mass
andeffective_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
, andCenterOfMass
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'sCollider
andColliderDensity
.- 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
, andComputedCenterOfMass
instead ofMass
,AngularInertia
, andCenterOfMass
. 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
, andNoAutoCenterOfMass
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, unlessNoAutoAngularInertia
is present. - Previously, specifying the
CenterOfMass
at spawn did nothing unless an initialMass
was specified, even if the entity had a collider that would give it mass. This has been fixed. - Previously,
Mass
,AngularInertia
, andCenterOfMass
did nothing on child colliders. Now, they effectively overrideColliderMassProperties
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
, andColliderMassProperties
now always usef32
types, even with thef64
feature. Total mass properties stored inComputedMass
,ComputedAngularInertia
, andComputedCenterOfMass
still supportf64
.- 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, includingfrom_tensor
. MassPropertiesBundle::new_computed
andColliderMassProperties::from_collider
have been renamed tofrom_shape
.ColliderMassProperties
now stores aMassProperties2d
/MassProperties3d
instead of separate properties.- Types implementing
AnyCollider
must now also implement theComputeMassProperties2d
/ComputeMassProperties3d
trait instead of themass_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_impact
→RayCaster::max_distance
RayCaster::with_max_time_of_impact
→RayCaster::with_max_distance
ShapeCaster::max_time_of_impact
→ShapeCaster::max_distance
ShapeCaster::with_max_time_of_impact
→ShapeCaster::with_max_distance
RayHitData::time_of_impact
→RayHitData::distance
ShapeHitData::time_of_impact
→ShapeHitData::distance
max_time_of_impact
onSpatialQuery
methods →RayCastConfig::max_distance
orShapeCastConfig::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
- Take
SpatialQueryFilter
by reference in spatial queries by @Jondolf in #402 - Use hooks for component initialization by @Jondolf in #483
- Use
FixedPostUpdate
by default and simplify scheduling by @Jondolf in #457 - Fix locked axes in gyro torque by @unpairedbracket in #486
- Add predicate variants to all casts by @janhohenheim in #493
- Only warn about 'overlapping at spawn' for dynamic bodies by @RJ in #491
- Use a single layer as the default membership instead of all. by @Aceeri in #476
- Make
PhysicsLayer
require a#[default]
variant by @Jondolf in #494 - Fix real part of quaternion derivatives by @unpairedbracket in #488
- Fix default schedule in
IntegratorPlugin::new
docs by @Jondolf in #495 - Make contacts deterministic across Worlds by @cBournhonesque in #480
- Fix missing feature flag by @janhohenheim in #502
- add some #[must_use] to LockedAxes builder fns by @RJ in #506
- Optimize
wake_on_collision_ended
when large number of collisions are occurring by @datael in #508 - experimentation with debugdump by @Vrixyz in #383
- Fix 3D rotation update in
transform_to_position
by @Jondolf in #520 - Make disabling joints possible. by @shanecelis in #519
- Allow interpolation of scales by @janhohenheim in #512
- Add convenience methods for determining if collisions are starting or stopping by @ndarilek in #529
- Rework
Mass
andInertia
and addGlobalAngularInertia
in 3D by @Jondolf in #500 - Add
MassPropertyPlugin
by @Jondolf in #532 - Update to Bevy 0.15 release candidate by @Jondolf in #540
- Use required components for component initialization by @Jondolf in #541
- Fix 3D friction by @Jondolf in #542
- Use entity Display in messages instead of Debug by @NiseVoid in #545
- Fix spatial query doc tests by @NiseVoid in #546
- Fix overlap warnings: yeet edition by @NiseVoid in #547
- Added missing deref and derefMut trait to AngularVelocity in 2D by @Lommix in #544
- Improvements to friction and restitution by @Jondolf in #551
- Add physics picking backend using
bevy_picking
by @Jondolf in #554 - Replace snapshots with hash-based cross-platform determinism test by @Jondolf in #555
- Add
find_deepest_contact
forContacts
andContactManifold
by @Jondolf in #556 - Add helpers for
ContactData
andSingleContact
to flip contact data by @Jondolf in #557 - Fix plugin table by @Jondolf in #565
- Fix new Rust 1.83.0 lints by @Jondolf in #569
- Migrate from Bevy 0.15 RC to full 0.15 by @Jondolf in #570
- Fix rotation multiplication order in
transform_to_position
by @Jondolf in #575 - Make sure
Collider::triangle
is oriented CCW by @Jondolf in #579 - Add
MaxLinearSpeed
andMaxAngularSpeed
by @Jondolf in #580 - Improve docs and heading consistency by @Jondolf in #581
- Mass Property Rework by @Jondolf in #574
- Add
SolverSchedulePlugin
to encapsulate solver scheduling by @Jondolf in #577 - Physics Interpolation and Extrapolation by @Jondolf in #566
- Add
RigidBodyDisabled
by @Jondolf in #536 - Add
cargo doc
to CI and fix doc links by @Jondolf in #582 - Improve
SpatialQuery
APIs and docs, and add more configuration by @Jondolf in #510 - revert schedule change for debug render. fixes #496 by @Hellzbellz123 in #497
- fix documentation for apply_force_at_point (local vs world space) by @johannesvollmer in #430
- Add
ColliderDisabled
by @Jondolf in #584 - Tweak disabling docs by @Jondolf in #585
- Renormalize rotation in 2D integration and clean up
renormalize
by @Jondolf in #590 - Fix missing
bevy_render/serialize
dependency. by @spectria-limina in #592 - Expose
EllipseColliderShape
andRegularPolygonColliderShape
by @Jondolf in #596 - Documentation improvements by @Jondolf in #598
- Release 0.2.0 by @Jondolf in #599
New Contributors
- @unpairedbracket made their first contribution in #486
- @cBournhonesque made their first contribution in #480
- @Vrixyz made their first contribution in #383
- @ndarilek made their first contribution in #529
- @Lommix made their first contribution in #544
- @Hellzbellz123 made their first contribution in #497
- @johannesvollmer made their first contribution in #430
- @spectria-limina made their first contribution in #592
Full Changelog: v0.1.2...v0.2.0