Skip to content

Commit

Permalink
Merge pull request bevyengine#28 from blamelessgames/hierarchy-spawning
Browse files Browse the repository at this point in the history
spawning physics entities using bevy hierarchy
  • Loading branch information
sebcrozet committed Nov 19, 2020
2 parents 854b4ff + 639c06b commit e92ebfa
Showing 1 changed file with 125 additions and 19 deletions.
144 changes: 125 additions & 19 deletions src/physics/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,139 @@ use rapier::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase};
use rapier::math::Isometry;
use rapier::pipeline::PhysicsPipeline;

// TODO: right now we only support one collider attached to one body.
// This should be extanded to multiple bodies.
// The reason why we build the body and the collider in the same system is
// because systems run in parallel. This implies that if the collider creation
// system runs before the body creation system, then it won't be able to create
// the colliders because the related rigid-body handles don't exist yet. This
// causes things to be initialized during multiple frames instead of just one.
/// System responsible for creating a Rapier rigid-body and collider from their
/// builder resources.
pub fn create_body_and_collider_system(
mut commands: Commands,
mut bodies: ResMut<RigidBodySet>,
mut colliders: ResMut<ColliderSet>,
mut entity_maps: ResMut<EntityMaps>,
entity: Entity,
body_builder: &RigidBodyBuilder,
collider_builder: &ColliderBuilder,
standalone_body_query: Query<Without<ColliderBuilder, (Entity, &RigidBodyBuilder)>>,
body_and_collider_query: Query<(Entity, &RigidBodyBuilder, &ColliderBuilder)>,
parented_collider_query: Query<Without<RigidBodyBuilder, (Entity, &Parent, &ColliderBuilder)>>,
) {
let handle = bodies.insert(body_builder.build());
commands.insert_one(entity, RigidBodyHandleComponent::from(handle));
commands.remove_one::<RigidBodyBuilder>(entity);
entity_maps.bodies.insert(entity, handle);

let handle = colliders.insert(collider_builder.build(), handle, &mut bodies);
commands.insert_one(entity, ColliderHandleComponent::from(handle));
commands.remove_one::<ColliderBuilder>(entity);
entity_maps.colliders.insert(entity, handle);
for (entity, body_builder) in standalone_body_query.iter() {
let handle = bodies.insert(body_builder.build());
commands.insert_one(entity, RigidBodyHandleComponent::from(handle));
commands.remove_one::<RigidBodyBuilder>(entity);
entity_maps.bodies.insert(entity, handle);
}

for (entity, body_builder, collider_builder) in body_and_collider_query.iter() {
let handle = bodies.insert(body_builder.build());
commands.insert_one(entity, RigidBodyHandleComponent::from(handle));
commands.remove_one::<RigidBodyBuilder>(entity);
entity_maps.bodies.insert(entity, handle);

let handle = colliders.insert(collider_builder.build(), handle, &mut bodies);
commands.insert_one(entity, ColliderHandleComponent::from(handle));
commands.remove_one::<ColliderBuilder>(entity);
entity_maps.colliders.insert(entity, handle);
}

for (entity, parent, collider_builder) in parented_collider_query.iter() {
if let Some(body_handle) = entity_maps.bodies.get(&parent.0) {
let handle = colliders.insert(collider_builder.build(), *body_handle, &mut bodies);
commands.insert_one(entity, ColliderHandleComponent::from(handle));
commands.remove_one::<ColliderBuilder>(entity);
entity_maps.colliders.insert(entity, handle);
} // warn here? panic here? do nothing?
}
}

#[test]
fn test_create_body_and_collider_system() {
use bevy::ecs::Schedule;

let mut resources = Resources::default();
resources.insert(RigidBodySet::new());
resources.insert(ColliderSet::new());
resources.insert(EntityMaps::default());

let mut world = World::new();
let body_and_collider_entity =
world.spawn((RigidBodyBuilder::new_dynamic(), ColliderBuilder::ball(1.0)));

let body_only_entity = world.spawn((
RigidBodyBuilder::new_static(),
Parent(body_and_collider_entity),
));

let parented_collider_entity_1 =
world.spawn((Parent(body_and_collider_entity), ColliderBuilder::ball(0.5)));

let parented_collider_entity_2 =
world.spawn((Parent(body_only_entity), ColliderBuilder::ball(0.25)));

let mut schedule = Schedule::default();
schedule.add_stage("physics_test");
schedule.add_system_to_stage("physics_test", create_body_and_collider_system.system());
schedule.run(&mut world, &mut resources);

let body_set = resources.get::<RigidBodySet>().unwrap();
let collider_set = resources.get::<ColliderSet>().unwrap();
let entity_maps = resources.get::<EntityMaps>().unwrap();

// body attached alongside collider
let attached_body_handle = world
.get::<RigidBodyHandleComponent>(body_and_collider_entity)
.unwrap()
.handle();
assert_eq!(
entity_maps.bodies[&body_and_collider_entity],
attached_body_handle
);
assert!(body_set.get(attached_body_handle).unwrap().is_dynamic());

// collider attached from same entity
let collider_handle = world
.get::<ColliderHandleComponent>(body_and_collider_entity)
.unwrap()
.handle();
assert_eq!(
entity_maps.colliders[&body_and_collider_entity],
collider_handle
);
let collider = collider_set.get(collider_handle).unwrap();
assert_eq!(attached_body_handle, collider.parent());
assert_eq!(collider.shape().as_ball().unwrap().radius, 1.0);

// collider attached to child entity of body with collider
let collider_handle = world
.get::<ColliderHandleComponent>(parented_collider_entity_1)
.unwrap()
.handle();
assert_eq!(
entity_maps.colliders[&parented_collider_entity_1],
collider_handle
);
let collider = collider_set.get(collider_handle).unwrap();
assert_eq!(attached_body_handle, collider.parent());
assert_eq!(collider.shape().as_ball().unwrap().radius, 0.5);

// standalone body with no collider, jointed to the attached body
let standalone_body_handle = world
.get::<RigidBodyHandleComponent>(body_only_entity)
.unwrap()
.handle();
assert_eq!(
entity_maps.bodies[&body_only_entity],
standalone_body_handle
);
assert!(body_set.get(standalone_body_handle).unwrap().is_static());

// collider attached to child entity of standlone body
let collider_handle = world
.get::<ColliderHandleComponent>(parented_collider_entity_2)
.unwrap()
.handle();
assert_eq!(
entity_maps.colliders[&parented_collider_entity_2],
collider_handle
);
let collider = collider_set.get(collider_handle).unwrap();
assert_eq!(standalone_body_handle, collider.parent());
assert_eq!(collider.shape().as_ball().unwrap().radius, 0.25);
}

/// System responsible for creating Rapier joints from their builder resources.
Expand Down

0 comments on commit e92ebfa

Please sign in to comment.