diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 841af6756f3ba3..5ea65a8714eab7 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -31,9 +31,9 @@ use std::collections::BTreeMap; /// let dynamic_scene = builder.build(); /// ``` pub struct DynamicSceneBuilder<'w> { - entities: BTreeMap, + extracted_scene: BTreeMap, type_registry: AppTypeRegistry, - world: &'w World, + original_world: &'w World, } impl<'w> DynamicSceneBuilder<'w> { @@ -41,9 +41,9 @@ impl<'w> DynamicSceneBuilder<'w> { /// All components registered in that world's [`AppTypeRegistry`] resource will be extracted. pub fn from_world(world: &'w World) -> Self { Self { - entities: default(), + extracted_scene: default(), type_registry: world.resource::().clone(), - world, + original_world: world, } } @@ -51,16 +51,19 @@ impl<'w> DynamicSceneBuilder<'w> { /// Only components registered in the given [`AppTypeRegistry`] will be extracted. pub fn from_world_with_type_registry(world: &'w World, type_registry: AppTypeRegistry) -> Self { Self { - entities: default(), + extracted_scene: default(), type_registry, - world, + original_world: world, } } /// Consume the builder, producing a [`DynamicScene`]. + /// + /// To make sure the dynamic scene doesn't contain entities without any components, call + /// [`Self::remove_empty_entities`] before building the scene. pub fn build(self) -> DynamicScene { DynamicScene { - entities: self.entities.into_values().collect(), + entities: self.extracted_scene.into_values().collect(), } } @@ -71,6 +74,16 @@ impl<'w> DynamicSceneBuilder<'w> { self.extract_entities(std::iter::once(entity)) } + /// Despawns all enitities with no components. + /// + /// These were likely created because none of their components were present in the provided type registry upon extraction. + pub fn remove_empty_entities(&mut self) -> &mut Self { + self.extracted_scene + .retain(|_, entity| !entity.components.is_empty()); + + self + } + /// Extract entities from the builder's [`World`]. /// /// Re-extracting an entity that was already extracted will have no effect. @@ -102,7 +115,7 @@ impl<'w> DynamicSceneBuilder<'w> { for entity in entities { let index = entity.index(); - if self.entities.contains_key(&index) { + if self.extracted_scene.contains_key(&index) { continue; } @@ -111,21 +124,22 @@ impl<'w> DynamicSceneBuilder<'w> { components: Vec::new(), }; - for component_id in self.world.entity(entity).archetype().components() { + for component_id in self.original_world.entity(entity).archetype().components() { let reflect_component = self - .world + .original_world .components() .get_info(component_id) .and_then(|info| type_registry.get(info.type_id().unwrap())) .and_then(|registration| registration.data::()); if let Some(reflect_component) = reflect_component { - if let Some(component) = reflect_component.reflect(self.world, entity) { + if let Some(component) = reflect_component.reflect(self.original_world, entity) + { entry.components.push(component.clone_value()); } } } - self.entities.insert(index, entry); + self.extracted_scene.insert(index, entry); } drop(type_registry); @@ -270,4 +284,24 @@ mod tests { scene_entities.sort(); assert_eq!(scene_entities, [entity_a_b.index(), entity_a.index()]); } + + #[test] + fn remove_componentless_entity() { + let mut world = World::default(); + + let atr = AppTypeRegistry::default(); + atr.write().register::(); + world.insert_resource(atr); + + let entity_a = world.spawn(ComponentA).id(); + let entity_b = world.spawn(ComponentB).id(); + + let mut builder = DynamicSceneBuilder::from_world(&world); + builder.extract_entities([entity_a, entity_b].into_iter()); + builder.remove_empty_entities(); + let scene = builder.build(); + + assert_eq!(scene.entities.len(), 1); + assert_eq!(scene.entities[0].entity, entity_a.index()); + } }