Skip to content
Open
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
126 changes: 126 additions & 0 deletions bevy_rapier3d/examples/change_contexts3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;

const N_CONTEXTS: usize = 5;
const WORLD_CHANGE_DELAY_SEC: f32 = 3.0;

#[derive(Component)]
/// Denotes which object(s) to change the world of
struct ChangeWorld;

#[derive(Resource, Debug)]
struct Contexts(Vec<Entity>);

fn main() {
App::new()
.insert_resource(ClearColor(Color::linear_rgb(
0xF9 as f32 / 255.0,
0xF9 as f32 / 255.0,
0xFF as f32 / 255.0,
)))
.add_plugins((
DefaultPlugins,
RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(),
))
.add_systems(Startup, (setup_graphics, setup_physics))
.add_systems(Update, change_world)
.run();
}

fn change_world(
mut query: Query<&mut RapierContextEntityLink, With<ChangeWorld>>,
time: Res<Time>,
worlds: Res<Contexts>,
) {
for mut context_entity_link in query.iter_mut() {
let idx = (worlds.0.len() - 1).min((time.elapsed_secs() / WORLD_CHANGE_DELAY_SEC) as usize);

// Prevent needless change detection
if context_entity_link.0 != worlds.0[idx] {
context_entity_link.0 = worlds.0[idx];
info!(
"Changing context to {:?} ({}/{N_CONTEXTS})",
context_entity_link.0,
idx + 1
)
}
}
}

fn setup_graphics(mut commands: Commands) {
commands.spawn((
Camera3d::default(),
Transform::from_xyz(0.0, 3.0, -10.0).looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y),
));
}

pub fn setup_physics(
mut commands: Commands,
q_default_context: Query<Entity, With<DefaultRapierContext>>,
) {
let default_context = q_default_context
.single()
.expect("This will automatically be created");

let mut contexts = Contexts(vec![default_context]);

for i in 1..N_CONTEXTS {
// Each context can have its own rapier configuration
let mut config = RapierConfiguration::new(1.0);
if i + 1 == N_CONTEXTS {
info!("The last context will have opposite gravity");
config.gravity = -config.gravity;
}
let context_ent = commands
.spawn((RapierContextSimulation::default(), config))
.id();
contexts.0.push(context_ent);
}

for (i, &world_ent) in contexts.0.iter().enumerate() {
let color = [
Color::hsl(220.0, 1.0, 0.3),
Color::hsl(180.0, 1.0, 0.3),
Color::hsl(260.0, 1.0, 0.7),
][i % 3];

/*
* Ground
*/
let ground_size = 5.1;
let ground_height = 0.1;

commands.spawn((
Transform::from_xyz(0.0, (i as f32) * -0.5 - ground_height, 0.0),
Collider::cuboid(ground_size, ground_height, ground_size),
ColliderDebugColor(color.into()),
RigidBody::Fixed,
RapierContextEntityLink(world_ent),
));
}

/*
* Create the cube
*
* The child is just there to show that physics world changes will also change the children.
*/
commands
.spawn((
// This will spawn in the default world, since no RapierContextEntityLink was added
Transform::from_xyz(0.0, 3.0, 0.0),
RigidBody::Dynamic,
ChangeWorld,
))
.with_children(|p| {
p.spawn((
Transform::from_xyz(0.0, 0.0, 0.0),
Collider::cuboid(0.5, 0.5, 0.5),
ColliderDebugColor(Color::hsl(260.0, 1.0, 0.7).into()),
));
});

info!("Spawning cube in default world (1/{N_CONTEXTS})");

commands.insert_resource(contexts);
}
33 changes: 29 additions & 4 deletions src/plugin/systems/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ pub fn sync_removals(
&mut RapierContextJoints,
&mut RapierRigidBodySet,
)>,
// Sometimes a Remove immediately followed by Add happens. These `q_has_*` queries prevent that immediate Add
// from being removed by this system by verifying it's still removed.
(
q_has_rigidbody_handle,
q_has_collider_handle,
q_has_multibody_joint_handle,
q_has_impulse_joint_handle,
): (
Query<(), With<RapierRigidBodyHandle>>,
Query<(), With<RapierColliderHandle>>,
Query<(), With<RapierMultibodyJointHandle>>,
Query<(), With<RapierImpulseJointHandle>>,
),
mut removed_bodies: RemovedComponents<RapierRigidBodyHandle>,
mut removed_colliders: RemovedComponents<RapierColliderHandle>,
mut removed_impulse_joints: RemovedComponents<RapierImpulseJointHandle>,
Expand All @@ -48,7 +61,10 @@ pub fn sync_removals(
/*
* Rigid-bodies removal detection.
*/
for entity in removed_bodies.read() {
for entity in removed_bodies
.read()
.filter(|e| !q_has_rigidbody_handle.contains(*e))
{
let Some(((mut context, mut context_colliders, mut joints, mut rigidbody_set), handle)) =
find_context(&mut context_writer, |res| res.3.entity2body.remove(&entity))
else {
Expand Down Expand Up @@ -90,7 +106,10 @@ pub fn sync_removals(
/*
* Collider removal detection.
*/
for entity in removed_colliders.read() {
for entity in removed_colliders
.read()
.filter(|e| !q_has_collider_handle.contains(*e))
{
let Some(((mut context, mut context_colliders, _, mut rigidbody_set), handle)) =
find_context(&mut context_writer, |res| {
res.1.entity2collider.remove(&entity)
Expand Down Expand Up @@ -138,7 +157,10 @@ pub fn sync_removals(
/*
* Impulse joint removal detection.
*/
for entity in removed_impulse_joints.read() {
for entity in removed_impulse_joints
.read()
.filter(|e| !q_has_impulse_joint_handle.contains(*e))
{
let Some(((_, _, mut joints, _), handle)) = find_context(&mut context_writer, |res| {
res.2.entity2impulse_joint.remove(&entity)
}) else {
Expand All @@ -159,7 +181,10 @@ pub fn sync_removals(
/*
* Multibody joint removal detection.
*/
for entity in removed_multibody_joints.read() {
for entity in removed_multibody_joints
.read()
.filter(|e| !q_has_multibody_joint_handle.contains(*e))
{
let Some(((_, _, mut joints, _), handle)) = find_context(&mut context_writer, |res| {
res.2.entity2multibody_joint.remove(&entity)
}) else {
Expand Down