bevy_scene/
scene.rs

1use core::any::TypeId;
2
3use crate::reflect_utils::clone_reflect_value;
4use crate::{DynamicScene, SceneSpawnError};
5use bevy_asset::Asset;
6use bevy_ecs::{
7    component::ComponentCloneBehavior,
8    entity::{Entity, EntityHashMap, SceneEntityMapper},
9    entity_disabling::DefaultQueryFilters,
10    reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
11    relationship::RelationshipHookMode,
12    world::World,
13};
14use bevy_reflect::TypePath;
15
16/// A composition of [`World`] objects.
17///
18/// To spawn a scene, you can use either:
19/// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn)
20/// * adding the [`SceneRoot`](crate::components::SceneRoot) component to an entity.
21#[derive(Asset, TypePath, Debug)]
22pub struct Scene {
23    /// The world of the scene, containing its entities and resources.
24    pub world: World,
25}
26
27impl Scene {
28    /// Creates a new scene with the given world.
29    pub fn new(world: World) -> Self {
30        Self { world }
31    }
32
33    /// Create a new scene from a given dynamic scene.
34    pub fn from_dynamic_scene(
35        dynamic_scene: &DynamicScene,
36        type_registry: &AppTypeRegistry,
37    ) -> Result<Scene, SceneSpawnError> {
38        let mut world = World::new();
39        let mut entity_map = EntityHashMap::default();
40        dynamic_scene.write_to_world_with(&mut world, &mut entity_map, type_registry)?;
41
42        Ok(Self { world })
43    }
44
45    /// Clone the scene.
46    ///
47    /// This method will return a [`SceneSpawnError`] if a type either is not registered in the
48    /// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
49    pub fn clone_with(&self, type_registry: &AppTypeRegistry) -> Result<Scene, SceneSpawnError> {
50        let mut new_world = World::new();
51        let mut entity_map = EntityHashMap::default();
52        self.write_to_world_with(&mut new_world, &mut entity_map, type_registry)?;
53        Ok(Self { world: new_world })
54    }
55
56    /// Write the entities and their corresponding components to the given world.
57    ///
58    /// This method will return a [`SceneSpawnError`] if a type either is not registered in the
59    /// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
60    pub fn write_to_world_with(
61        &self,
62        world: &mut World,
63        entity_map: &mut EntityHashMap<Entity>,
64        type_registry: &AppTypeRegistry,
65    ) -> Result<(), SceneSpawnError> {
66        let type_registry = type_registry.read();
67
68        let self_dqf_id = self
69            .world
70            .components()
71            .get_resource_id(TypeId::of::<DefaultQueryFilters>());
72
73        // Resources archetype
74        for (component_id, resource_data) in self.world.storages().resources.iter() {
75            if Some(component_id) == self_dqf_id {
76                continue;
77            }
78            if !resource_data.is_present() {
79                continue;
80            }
81
82            let component_info = self
83                .world
84                .components()
85                .get_info(component_id)
86                .expect("component_ids in archetypes should have ComponentInfo");
87
88            let type_id = component_info
89                .type_id()
90                .expect("reflected resources must have a type_id");
91
92            let registration =
93                type_registry
94                    .get(type_id)
95                    .ok_or_else(|| SceneSpawnError::UnregisteredType {
96                        std_type_name: component_info.name().to_string(),
97                    })?;
98            let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| {
99                SceneSpawnError::UnregisteredResource {
100                    type_path: registration.type_info().type_path().to_string(),
101                }
102            })?;
103            reflect_resource.copy(&self.world, world, &type_registry);
104        }
105
106        // Ensure that all scene entities have been allocated in the destination
107        // world before handling components that may contain references that need mapping.
108        for archetype in self.world.archetypes().iter() {
109            for scene_entity in archetype.entities() {
110                entity_map
111                    .entry(scene_entity.id())
112                    .or_insert_with(|| world.spawn_empty().id());
113            }
114        }
115
116        for archetype in self.world.archetypes().iter() {
117            for scene_entity in archetype.entities() {
118                let entity = *entity_map
119                    .get(&scene_entity.id())
120                    .expect("should have previously spawned an entity");
121
122                for component_id in archetype.components() {
123                    let component_info = self
124                        .world
125                        .components()
126                        .get_info(component_id)
127                        .expect("component_ids in archetypes should have ComponentInfo");
128
129                    if *component_info.clone_behavior() == ComponentCloneBehavior::Ignore {
130                        continue;
131                    }
132
133                    let registration = type_registry
134                        .get(component_info.type_id().unwrap())
135                        .ok_or_else(|| SceneSpawnError::UnregisteredType {
136                            std_type_name: component_info.name().to_string(),
137                        })?;
138                    let reflect_component =
139                        registration.data::<ReflectComponent>().ok_or_else(|| {
140                            SceneSpawnError::UnregisteredComponent {
141                                type_path: registration.type_info().type_path().to_string(),
142                            }
143                        })?;
144
145                    let Some(component) = reflect_component
146                        .reflect(self.world.entity(scene_entity.id()))
147                        .map(|component| {
148                            clone_reflect_value(component.as_partial_reflect(), registration)
149                        })
150                    else {
151                        continue;
152                    };
153
154                    // If this component references entities in the scene,
155                    // update them to the entities in the world.
156                    SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
157                        reflect_component.apply_or_insert_mapped(
158                            &mut world.entity_mut(entity),
159                            component.as_partial_reflect(),
160                            &type_registry,
161                            mapper,
162                            RelationshipHookMode::Skip,
163                        );
164                    });
165                }
166            }
167        }
168
169        Ok(())
170    }
171}