Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions migration-guides/0.5-to-main.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ since the latest release. These guides are evolving and may not be polished yet.
See [migration-guides/README.md](./README.md) and existing entries for information about Avian's
migration guide process and what to put here.

## `ReadRigidBody` and `WriteRigidBody`
## `ReadRigidBodyForces` and `WriteRigidBodyForces`

PR [#908](https://github.com/avianphysics/avian/pull/908) introduced two new traits: `ReadRigidBody` and `WriteRigidBody`, and `RigidyBodyForces` is now defined as:
PR [#908](https://github.com/avianphysics/avian/pull/908) introduced two new traits: `ReadRigidBodyForces` and `WriteRigidBodyForces`, and `RigidyBodyForces` is now defined as:

```rust
pub trait RigidBodyForces: ReadRigidBodyForces + WriteRigidBodyForces {}
```

In most cases this should just work, but if it doesn't, you can replace your implementation for `RigidBodyForces` with both `ReadRigidBodyForces` and `WriteRigidBodyForces` where it is used / needed. Both traits are required to implement `RigidBodyForces`, but you can implement them separately.

## `SleepBody` and `WakeBody`

The `SleepBody` and `WakeBody` commands now return an error when applied for an entity that doesn't exist
or doesn't belong to an island. Previously, they logged a warning instead.
1 change: 0 additions & 1 deletion src/dynamics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ pub mod prelude {
PhysicsLengthUnit, SolverPlugin, SolverPlugins,
islands::{
IslandPlugin, IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands,
WakeUpBody,
},
schedule::{
SolverSchedulePlugin, SolverSet, SolverSystems, SubstepCount, SubstepSchedule,
Expand Down
6 changes: 1 addition & 5 deletions src/dynamics/solver/islands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@
// https://github.com/erincatto/box2d/blob/df9787b59e4480135fbd73d275f007b5d931a83f/src/island.c#L57

mod sleeping;

#[expect(deprecated)]
pub use sleeping::{
IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands, WakeUpBody,
};
pub use sleeping::{IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands};

use bevy::{
ecs::{entity_disabling::Disabled, lifecycle::HookContext, world::DeferredWorld},
Expand Down
118 changes: 61 additions & 57 deletions src/dynamics/solver/islands/sleeping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use bevy::{
ecs::{
entity::Entity,
entity_disabling::Disabled,
error::Result,
lifecycle::{HookContext, Insert, Replace},
observer::On,
query::{Changed, Has, Or, With, Without},
Expand All @@ -21,7 +22,6 @@ use bevy::{
},
world::{DeferredWorld, Mut, Ref, World},
},
log::warn,
prelude::{Deref, DerefMut},
time::Time,
};
Expand Down Expand Up @@ -100,7 +100,7 @@ fn sleep_on_add_sleeping(mut world: DeferredWorld, ctx: HookContext) {
return;
}

world.commands().queue(SleepBody(ctx.entity));
world.commands().queue_silenced(SleepBody(ctx.entity));
}

fn wake_on_remove_sleeping(mut world: DeferredWorld, ctx: HookContext) {
Expand All @@ -119,7 +119,7 @@ fn wake_on_remove_sleeping(mut world: DeferredWorld, ctx: HookContext) {
return;
}

world.commands().queue(WakeBody(ctx.entity));
world.commands().queue_silenced(WakeBody(ctx.entity));
}

fn wake_on_replace_rigid_body(
Expand Down Expand Up @@ -293,46 +293,52 @@ struct CachedBodySleepingSystemState(
/// A [`Command`] that forces a [`RigidBody`] and its [`PhysicsIsland`][super::PhysicsIsland] to be [`Sleeping`].
pub struct SleepBody(pub Entity);

impl Command for SleepBody {
fn apply(self, world: &mut World) {
if let Some(island_id) = world
.get::<BodyIslandNode>(self.0)
.map(|node| node.island_id)
{
world.try_resource_scope(|world, mut state: Mut<CachedBodySleepingSystemState>| {
let (
mut body_islands,
body_colliders,
mut islands,
mut contact_graph,
mut joint_graph,
) = state.0.get_mut(world);

let Some(island) = islands.get_mut(island_id) else {
return;
};

// The island must be split before it can be woken up.
// Note that this is expensive.
if island.constraints_removed > 0 {
islands.split_island(
island_id,
&mut body_islands,
&body_colliders,
&mut contact_graph,
&mut joint_graph,
);
}

// The ID of the body's island might have changed due to the split,
// so we need to retrieve it again.
let island_id = body_islands.get(self.0).map(|node| node.island_id).unwrap();
impl Command<Result> for SleepBody {
fn apply(self, world: &mut World) -> Result {
if let Ok(entity) = world.get_entity(self.0) {
if let Some(island_id) = entity.get::<BodyIslandNode>().map(|node| node.island_id) {
world.try_resource_scope(|world, mut state: Mut<CachedBodySleepingSystemState>| {
let (
mut body_islands,
body_colliders,
mut islands,
mut contact_graph,
mut joint_graph,
) = state.0.get_mut(world);

let Some(island) = islands.get_mut(island_id) else {
return;
};

// The island must be split before it can be woken up.
// Note that this is expensive.
if island.constraints_removed > 0 {
islands.split_island(
island_id,
&mut body_islands,
&body_colliders,
&mut contact_graph,
&mut joint_graph,
);
}

// Sleep the island.
SleepIslands(vec![island_id]).apply(world);
});
// The ID of the body's island might have changed due to the split,
// so we need to retrieve it again.
let island_id = body_islands.get(self.0).map(|node| node.island_id).unwrap();

// Sleep the island.
SleepIslands(vec![island_id]).apply(world);
});
Ok(())
} else {
Err(format!(
"Tried to sleep entity {:?} that is not a body or does not belong to an island",
self.0
)
.into())
}
} else {
warn!("Tried to sleep body {:?} that does not exist", self.0);
Err(format!("Tried to sleep entity {:?} that does not exist", self.0).into())
}
}
}
Expand Down Expand Up @@ -442,27 +448,25 @@ struct CachedIslandWakingSystemState(
/// A [`Command`] that wakes up a [`RigidBody`] and its [`PhysicsIsland`](super::PhysicsIsland) if it is [`Sleeping`].
pub struct WakeBody(pub Entity);

impl Command for WakeBody {
fn apply(self, world: &mut World) {
if let Some(body_island) = world.get::<BodyIslandNode>(self.0) {
WakeIslands(vec![body_island.island_id]).apply(world);
impl Command<Result> for WakeBody {
fn apply(self, world: &mut World) -> Result {
if let Ok(entity) = world.get_entity(self.0) {
if let Some(body_island) = entity.get::<BodyIslandNode>() {
WakeIslands(vec![body_island.island_id]).apply(world);
Ok(())
} else {
Err(format!(
"Tried to wake entity {:?} that is not a body or does not belong to an island",
self.0
)
.into())
}
} else {
warn!("Tried to wake body {:?} that does not exist", self.0);
Err(format!("Tried to wake entity {:?} that does not exist", self.0).into())
}
}
}

/// A deprecated alias for [`WakeBody`].
#[deprecated(since = "0.4.0", note = "Renamed to `WakeBody`.")]
pub struct WakeUpBody(pub Entity);

#[expect(deprecated)]
impl Command for WakeUpBody {
fn apply(self, world: &mut World) {
WakeBody(self.0).apply(world);
}
}

/// A [`Command`] that wakes up the [`PhysicsIsland`](super::PhysicsIsland)s with the given IDs if they are sleeping.
pub struct WakeIslands(pub Vec<IslandId>);

Expand Down