Skip to content

Commit

Permalink
multiple worlds working + example
Browse files Browse the repository at this point in the history
example from dimforge#328

Co-authored-by: Anthony Tornetta
<25857049+AnthonyTornetta@users.noreply.github.com>
  • Loading branch information
Vrixyz committed Jun 26, 2024
1 parent 7a4ba5e commit 4fd9746
Showing 11 changed files with 661 additions and 415 deletions.
9 changes: 4 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -7,11 +7,10 @@
- `RapierContext` is now a `Component`
- Rapier now supports multiple worlds.
- Migration guide:
- `ResMut<mut RapierContext>` -> `RapierContextAccessMut`
- `Res<RapierContext>` -> `Query<&RapierContext>`
- `ResMut<RapierConfiguration>` -> `Query<&mut RapierConfiguration>`
- `Res<RapierConfiguration>` -> `Query<&RapierConfiguration>`
- you can reach out for the underlying configuration via .single
- `ResMut<mut RapierContext>` -> `DefaultRapierContextAccessMut`
- `Res<RapierContext>` -> `DefaultRapierContextAccess`
- `ResMut<RapierConfiguration>` -> `DefaultRapierConfigurationMut`
- `Res<RapierConfiguration>` -> `DefaultRapierConfiguration`

## v0.27.0-rc.1 (18 June 2024)

117 changes: 117 additions & 0 deletions bevy_rapier3d/examples/multi_world3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;

const N_WORLDS: usize = 2;

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

fn create_worlds(mut commands: Commands) {
for i in 0..N_WORLDS {
commands.spawn((RapierContext::default(), WorldId(i)));
}
}

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

#[derive(Component)]
pub struct WorldId(pub usize);

#[derive(Component)]
struct Platform {
starting_y: f32,
}

fn move_platforms(time: Res<Time>, mut query: Query<(&mut Transform, &Platform)>) {
for (mut transform, platform) in query.iter_mut() {
transform.translation.y = platform.starting_y + -time.elapsed_seconds().sin();
}
}

/// Demonstrates despawning an entity removing it from its world
// fn despawn_last(query: Query<(&PhysicsWorld, Entity)>, mut commands: Commands) {
// for (bw, entity) in query.iter() {
// if bw.world_id == N_WORLDS - 1 {
// commands.entity(entity).despawn_recursive();
// }
// }
// }

/// Demonstrates how easy it is to move one entity to another world.
// fn change_world(mut query: Query<&mut PhysicsWorld>) {
// for mut bw in query.iter_mut() {
// if bw.world_id == 1 {
// bw.world_id = 0;
// }
// }
// }

pub fn setup_physics(
context: Query<(Entity, &WorldId), With<RapierContext>>,
mut commands: Commands,
) {
for (context_entity, id) in context.iter() {
let id = id.0;

let color = [
Hsla::hsl(220.0, 1.0, 0.3),
Hsla::hsl(180.0, 1.0, 0.3),
Hsla::hsl(260.0, 1.0, 0.7),
][id % 3];

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

let starting_y = (id as f32) * -0.5 - ground_height;

let mut platforms = commands.spawn((
TransformBundle::from(Transform::from_xyz(0.0, starting_y, 0.0)),
Collider::cuboid(ground_size, ground_height, ground_size),
ColliderDebugColor(color),
RapierContextEntityLink(context_entity),
));
if id == 1 {
platforms.insert(Platform { starting_y });
}

/*
* Create the cube
*/

commands.spawn((
TransformBundle::from(Transform::from_xyz(0.0, 1.0 + id as f32 * 5.0, 0.0)),
RigidBody::Dynamic,
Collider::cuboid(0.5, 0.5, 0.5),
ColliderDebugColor(color),
RapierContextEntityLink(context_entity),
));
}
}
12 changes: 12 additions & 0 deletions src/plugin/context/systemparams/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
mod rapier_context_access;

use bevy::{ecs::query::QueryData, prelude::Entity};
pub use rapier_context_access::*;

use super::RapierContextEntityLink;

#[derive(QueryData)]
pub struct RapierEntity {
pub entity: Entity,
pub rapier_context_link: &'static RapierContextEntityLink,
}
Original file line number Diff line number Diff line change
@@ -2,8 +2,7 @@ use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use std::ops::{Deref, DerefMut};

use super::{DefaultRapierContext, RapierContext, RapierContextEntityLink};

use super::super::{DefaultRapierContext, RapierContext, RapierContextEntityLink};
/// Utility [`SystemParam`] to easily access the default world [`RapierContext`] immutably
#[derive(SystemParam)]
pub struct DefaultRapierContextAccess<'w, 's> {
@@ -50,25 +49,14 @@ impl<'w, 's> DerefMut for DefaultRapierContextAccessMut<'w, 's> {
#[derive(SystemParam)]
pub struct RapierContextAccess<'w, 's> {
rapier_context: Query<'w, 's, &'static RapierContext>,
rapier_context_link: Query<'w, 's, &'static RapierContextEntityLink>,
}

impl<'w, 's> RapierContextAccess<'w, 's> {
pub fn link(&self, entity: Entity) -> &RapierContextEntityLink {
self.rapier_context_link
.get(entity)
.expect("RapierContextAccess.link called on an entity without RapierContextEntityLink.")
}
pub fn follow_link(&self, link: RapierContextEntityLink) -> &'_ RapierContext {
pub fn context(&self, link: RapierContextEntityLink) -> &'_ RapierContext {
self.rapier_context
.get(link.0)
.expect("RapierContextEntityLink.0 refers to an entity without RapierContext.")
}

pub fn context(&self, entity: Entity) -> &'_ RapierContext {
let context_link = self.link(entity);
self.follow_link(*context_link)
}
}

impl<'w, 's> Deref for RapierContextAccess<'w, 's> {
@@ -83,35 +71,24 @@ impl<'w, 's> Deref for RapierContextAccess<'w, 's> {
#[derive(SystemParam)]
pub struct RapierContextAccessMut<'w, 's> {
pub rapier_context: Query<'w, 's, &'static mut RapierContext>,
pub rapier_context_link: Query<'w, 's, &'static RapierContextEntityLink>,
}

impl<'w, 's> RapierContextAccessMut<'w, 's> {
pub fn link(&self, entity: Entity) -> &RapierContextEntityLink {
self.rapier_context_link.get(entity).expect(
"RapierContextAccessMut.link called on an entity without RapierContextEntityLink.",
)
}
pub fn follow_link(&mut self, link: RapierContextEntityLink) -> &'_ mut RapierContext {
pub fn context(&mut self, link: RapierContextEntityLink) -> &'_ mut RapierContext {
self.rapier_context
.get_mut(link.0)
.expect("RapierContextEntityLink.0 refers to an entity without RapierContext.")
.into_inner()
}

pub fn context(&mut self, entity: Entity) -> &'_ mut RapierContext {
let context_link = self.link(entity);
self.follow_link(*context_link)
}
}

pub fn try_retrieve_context<'a>(
link: Option<&RapierContextEntityLink>,
context: &Query<(Entity, &mut RapierContext)>,
context: &Query<Entity, With<RapierContext>>,
) -> Result<Entity, Entity> {
link.map_or_else(
|| {
let context_entity = context.iter().next().unwrap().0;
let context_entity = context.iter().next().unwrap();
Err(context_entity)
},
|link| Ok(link.0),
2 changes: 1 addition & 1 deletion src/plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ pub use self::context::{
DefaultRapierContextAccess, DefaultRapierContextAccessMut, RapierContextAccess,
RapierContextAccessMut,
},
RapierContext,
RapierContext, RapierContextEntityLink,
};
pub use self::plugin::{NoUserData, PhysicsSet, RapierPhysicsPlugin, RapierTransformPropagateSet};

13 changes: 13 additions & 0 deletions src/plugin/plugin.rs
Original file line number Diff line number Diff line change
@@ -44,6 +44,18 @@ where
});
self
}
/// Specifies a default configuration for the default `RapierContext`
///
/// If [`None`], no world will be initialized, you are responsible of creating and maintaining
/// a [`RapierContext`] before creating any rapier entities (rigidbodies, colliders, joints),
/// and as long as any [`RapierContextEntityLink`] has a reference to its [`RapierContext`].
pub fn with_default_world(
mut self,
default_world_initialization: Option<RapierContextInitialization>,
) -> Self {
self.default_world_setup = default_world_initialization;
self
}

/// Specifies whether the plugin should setup each of its [`PhysicsStages`]
/// (`true`), or if the user will set them up later (`false`).
@@ -244,6 +256,7 @@ where
}
}

#[derive(Resource)]
pub struct RapierContextInitialization {
pub length_unit: f32,
}
18 changes: 13 additions & 5 deletions src/plugin/systems/character_controller.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::control::CharacterCollision;
use crate::dynamics::RapierRigidBodyHandle;
use crate::geometry::RapierColliderHandle;
use crate::plugin::context::RapierContextEntityLink;
use crate::plugin::RapierConfiguration;
use crate::plugin::RapierContextAccessMut;
use crate::prelude::KinematicCharacterController;
@@ -19,6 +20,7 @@ pub fn update_character_controls(
mut context_access: RapierContextAccessMut,
mut character_controllers: Query<(
Entity,
&RapierContextEntityLink,
&mut KinematicCharacterController,
Option<&mut KinematicCharacterControllerOutput>,
Option<&RapierColliderHandle>,
@@ -27,12 +29,18 @@ pub fn update_character_controls(
)>,
mut transforms: Query<&mut Transform>,
) {
for (entity, mut controller, output, collider_handle, body_handle, glob_transform) in
character_controllers.iter_mut()
for (
entity,
rapier_context_link,
mut controller,
output,
collider_handle,
body_handle,
glob_transform,
) in character_controllers.iter_mut()
{
let link = *context_access.link(entity);
let context = context_access.follow_link(link);
let config = config.get(link.0).unwrap();
let context = context_access.context(*rapier_context_link);
let config = config.get(rapier_context_link.0).unwrap();
if let (Some(raw_controller), Some(translation)) =
(controller.to_raw(), controller.translation)
{
Loading

0 comments on commit 4fd9746

Please sign in to comment.