diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 1de7202bfab4e..7a292aaf69666 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -78,8 +78,8 @@ impl DynamicScene { /// Write the dynamic entities and their corresponding components to the given world. /// - /// This method will return a `SceneSpawnError` if either a type is not registered - /// or doesn't reflect the `Component` trait. + /// This method will return a [`SceneSpawnError`] if a type either is not registered + /// or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait. pub fn write_to_world( &self, world: &mut World, diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 4cd871fa7283a..1e67402a11ad9 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,6 +1,13 @@ -use bevy_ecs::world::World; +use bevy_app::AppTypeRegistry; +use bevy_ecs::{ + entity::EntityMap, + reflect::{ReflectComponent, ReflectMapEntities}, + world::World, +}; use bevy_reflect::TypeUuid; +use crate::{InstanceInfo, SceneSpawnError}; + /// To spawn a scene, you can use either: /// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn) /// * adding the [`SceneBundle`](crate::SceneBundle) to an entity @@ -17,4 +24,68 @@ impl Scene { pub fn new(world: World) -> Self { Self { world } } + + /// Clone the scene. + /// + /// This method will return a [`SceneSpawnError`] if a type either is not registered in the + /// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait. + pub fn clone_with(&self, type_registry: &AppTypeRegistry) -> Result { + let mut new_world = World::new(); + self.write_to_world_with(&mut new_world, type_registry)?; + Ok(Self { world: new_world }) + } + + /// Write the entities and their corresponding components to the given world. + /// + /// This method will return a [`SceneSpawnError`] if a type either is not registered in the + /// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait. + pub fn write_to_world_with( + &self, + world: &mut World, + type_registry: &AppTypeRegistry, + ) -> Result { + let mut instance_info = InstanceInfo { + entity_map: EntityMap::default(), + }; + + let type_registry = type_registry.read(); + for archetype in self.world.archetypes().iter() { + for scene_entity in archetype.entities() { + let entity = *instance_info + .entity_map + .entry(*scene_entity) + .or_insert_with(|| world.spawn().id()); + for component_id in archetype.components() { + let component_info = self + .world + .components() + .get_info(component_id) + .expect("component_ids in archetypes should have ComponentInfo"); + + let reflect_component = type_registry + .get(component_info.type_id().unwrap()) + .ok_or_else(|| SceneSpawnError::UnregisteredType { + type_name: component_info.name().to_string(), + }) + .and_then(|registration| { + registration.data::().ok_or_else(|| { + SceneSpawnError::UnregisteredComponent { + type_name: component_info.name().to_string(), + } + }) + })?; + reflect_component.copy(&self.world, world, *scene_entity, entity); + } + } + } + for registration in type_registry.iter() { + if let Some(map_entities_reflect) = registration.data::() { + map_entities_reflect + .map_entities(world, &instance_info.entity_map) + .unwrap(); + } + } + + Ok(instance_info) + } } diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index d9bfcb213687e..73eaa93e4cd18 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -4,7 +4,6 @@ use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_ecs::{ entity::{Entity, EntityMap}, event::{Events, ManualEventReader}, - reflect::{ReflectComponent, ReflectMapEntities}, system::{Command, Resource}, world::{Mut, World}, }; @@ -13,9 +12,11 @@ use bevy_utils::{tracing::error, HashMap}; use thiserror::Error; use uuid::Uuid; +/// Informations about a scene instance. #[derive(Debug)] -struct InstanceInfo { - entity_map: EntityMap, +pub struct InstanceInfo { + /// Mapping of entities from the scene world to the instance world. + pub entity_map: EntityMap, } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] @@ -161,11 +162,6 @@ impl SceneSpawner { scene_handle: Handle, instance_id: InstanceId, ) -> Result { - let mut instance_info = InstanceInfo { - entity_map: EntityMap::default(), - }; - let type_registry = world.resource::().clone(); - let type_registry = type_registry.read(); world.resource_scope(|world, scenes: Mut>| { let scene = scenes @@ -174,42 +170,9 @@ impl SceneSpawner { handle: scene_handle.clone(), })?; - for archetype in scene.world.archetypes().iter() { - for scene_entity in archetype.entities() { - let entity = *instance_info - .entity_map - .entry(*scene_entity) - .or_insert_with(|| world.spawn().id()); - for component_id in archetype.components() { - let component_info = scene - .world - .components() - .get_info(component_id) - .expect("component_ids in archetypes should have ComponentInfo"); - - let reflect_component = type_registry - .get(component_info.type_id().unwrap()) - .ok_or_else(|| SceneSpawnError::UnregisteredType { - type_name: component_info.name().to_string(), - }) - .and_then(|registration| { - registration.data::().ok_or_else(|| { - SceneSpawnError::UnregisteredComponent { - type_name: component_info.name().to_string(), - } - }) - })?; - reflect_component.copy(&scene.world, world, *scene_entity, entity); - } - } - } - for registration in type_registry.iter() { - if let Some(map_entities_reflect) = registration.data::() { - map_entities_reflect - .map_entities(world, &instance_info.entity_map) - .unwrap(); - } - } + let instance_info = + scene.write_to_world_with(world, &world.resource::().clone())?; + self.spawned_instances.insert(instance_id, instance_info); let spawned = self .spawned_scenes