Skip to main content

bevy_scene/
dynamic_scene.rs

1use crate::{DynamicSceneBuilder, Scene, SceneSpawnError};
2use bevy_asset::Asset;
3use bevy_ecs::reflect::{ReflectMapEntities, ReflectResource};
4use bevy_ecs::{
5    entity::{Entity, EntityHashMap, SceneEntityMapper},
6    reflect::{AppTypeRegistry, ReflectComponent},
7    world::World,
8};
9use bevy_reflect::{PartialReflect, TypePath};
10
11use crate::reflect_utils::clone_reflect_value;
12use bevy_ecs::component::ComponentCloneBehavior;
13use bevy_ecs::relationship::RelationshipHookMode;
14
15#[cfg(feature = "serialize")]
16use {crate::serde::SceneSerializer, bevy_reflect::TypeRegistry, serde::Serialize};
17
18/// A collection of serializable resources and dynamic entities.
19///
20/// Each dynamic entity in the collection contains its own run-time defined set of components.
21/// To spawn a dynamic scene, you can use either:
22/// * [`SceneSpawner::spawn_dynamic`](crate::SceneSpawner::spawn_dynamic)
23/// * adding the [`DynamicSceneRoot`](crate::components::DynamicSceneRoot) component to an entity.
24/// * using the [`DynamicSceneBuilder`] to construct a `DynamicScene` from `World`.
25#[derive(Asset, TypePath, Default)]
26pub struct DynamicScene {
27    /// Resources stored in the dynamic scene.
28    pub resources: Vec<Box<dyn PartialReflect>>,
29    /// Entities contained in the dynamic scene.
30    pub entities: Vec<DynamicEntity>,
31}
32
33/// A reflection-powered serializable representation of an entity and its components.
34pub struct DynamicEntity {
35    /// The identifier of the entity, unique within a scene (and the world it may have been generated from).
36    ///
37    /// Components that reference this entity must consistently use this identifier.
38    pub entity: Entity,
39    /// A vector of boxed components that belong to the given entity and
40    /// implement the [`PartialReflect`] trait.
41    pub components: Vec<Box<dyn PartialReflect>>,
42}
43
44impl DynamicScene {
45    /// Create a new dynamic scene from a given scene.
46    pub fn from_scene(scene: &Scene) -> Self {
47        Self::from_world(&scene.world)
48    }
49
50    /// Create a new dynamic scene from a given world.
51    pub fn from_world(world: &World) -> Self {
52        DynamicSceneBuilder::from_world(world)
53            .extract_entities(
54                // we do this instead of a query, in order to completely sidestep default query filters.
55                // while we could use `Allow<_>`, this wouldn't account for custom disabled components
56                world
57                    .archetypes()
58                    .iter()
59                    .flat_map(bevy_ecs::archetype::Archetype::entities)
60                    .map(bevy_ecs::archetype::ArchetypeEntity::id),
61            )
62            .extract_resources()
63            .build()
64    }
65
66    /// Write the resources, the dynamic entities, and their corresponding components to the given world.
67    ///
68    /// This method will return a [`SceneSpawnError`] if a type either is not registered
69    /// in the provided [`AppTypeRegistry`] resource, or doesn't reflect the
70    /// [`Component`](bevy_ecs::component::Component) or [`Resource`](bevy_ecs::prelude::Resource) trait.
71    pub fn write_to_world_with(
72        &self,
73        world: &mut World,
74        entity_map: &mut EntityHashMap<Entity>,
75        type_registry: &AppTypeRegistry,
76    ) -> Result<(), SceneSpawnError> {
77        let type_registry = type_registry.read();
78
79        // First ensure that every entity in the scene has a corresponding world
80        // entity in the entity map.
81        for scene_entity in &self.entities {
82            // Fetch the entity with the given entity id from the `entity_map`
83            // or spawn a new entity with a transiently unique id if there is
84            // no corresponding entry.
85            entity_map
86                .entry(scene_entity.entity)
87                .or_insert_with(|| world.spawn_empty().id());
88        }
89
90        for scene_entity in &self.entities {
91            // Fetch the entity with the given entity id from the `entity_map`.
92            let entity = *entity_map
93                .get(&scene_entity.entity)
94                .expect("should have previously spawned an empty entity");
95
96            // Apply/ add each component to the given entity.
97            for component in &scene_entity.components {
98                let type_info = component.get_represented_type_info().ok_or_else(|| {
99                    SceneSpawnError::NoRepresentedType {
100                        type_path: component.reflect_type_path().to_string(),
101                    }
102                })?;
103                let registration = type_registry.get(type_info.type_id()).ok_or_else(|| {
104                    SceneSpawnError::UnregisteredButReflectedType {
105                        type_path: type_info.type_path().to_string(),
106                    }
107                })?;
108                let reflect_component =
109                    registration.data::<ReflectComponent>().ok_or_else(|| {
110                        SceneSpawnError::UnregisteredComponent {
111                            type_path: type_info.type_path().to_string(),
112                        }
113                    })?;
114
115                {
116                    let component_id = reflect_component.register_component(world);
117                    // SAFETY: we registered the component above. the info exists
118                    #[expect(unsafe_code, reason = "this is faster")]
119                    let component_info =
120                        unsafe { world.components().get_info_unchecked(component_id) };
121                    if matches!(
122                        *component_info.clone_behavior(),
123                        ComponentCloneBehavior::Ignore
124                    ) {
125                        continue;
126                    }
127                }
128
129                SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
130                    reflect_component.apply_or_insert_mapped(
131                        &mut world.entity_mut(entity),
132                        component.as_partial_reflect(),
133                        &type_registry,
134                        mapper,
135                        RelationshipHookMode::Skip,
136                    );
137                });
138            }
139        }
140
141        // Insert resources after all entities have been added to the world.
142        // This ensures the entities are available for the resources to reference during mapping.
143        for resource in &self.resources {
144            let type_info = resource.get_represented_type_info().ok_or_else(|| {
145                SceneSpawnError::NoRepresentedType {
146                    type_path: resource.reflect_type_path().to_string(),
147                }
148            })?;
149            let registration = type_registry.get(type_info.type_id()).ok_or_else(|| {
150                SceneSpawnError::UnregisteredButReflectedType {
151                    type_path: type_info.type_path().to_string(),
152                }
153            })?;
154            let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| {
155                SceneSpawnError::UnregisteredResource {
156                    type_path: type_info.type_path().to_string(),
157                }
158            })?;
159
160            // If this component references entities in the scene, update
161            // them to the entities in the world.
162            let mut cloned_resource;
163            let partial_reflect_resource = if let Some(map_entities) =
164                registration.data::<ReflectMapEntities>()
165            {
166                cloned_resource = clone_reflect_value(resource.as_partial_reflect(), registration);
167                SceneEntityMapper::world_scope(entity_map, world, |_, mapper| {
168                    map_entities.map_entities(cloned_resource.as_partial_reflect_mut(), mapper);
169                });
170                cloned_resource.as_partial_reflect()
171            } else {
172                resource.as_partial_reflect()
173            };
174
175            // If the world already contains an instance of the given resource
176            // just apply the (possibly) new value, otherwise insert the resource
177            reflect_resource.apply_or_insert(world, partial_reflect_resource, &type_registry);
178        }
179
180        Ok(())
181    }
182
183    /// Write the resources, the dynamic entities, and their corresponding components to the given world.
184    ///
185    /// This method will return a [`SceneSpawnError`] if a type either is not registered
186    /// in the world's [`AppTypeRegistry`] resource, or doesn't reflect the
187    /// [`Component`](bevy_ecs::component::Component) trait.
188    pub fn write_to_world(
189        &self,
190        world: &mut World,
191        entity_map: &mut EntityHashMap<Entity>,
192    ) -> Result<(), SceneSpawnError> {
193        let registry = world.resource::<AppTypeRegistry>().clone();
194        self.write_to_world_with(world, entity_map, &registry)
195    }
196
197    // TODO: move to AssetSaver when it is implemented
198    /// Serialize this dynamic scene into the official Bevy scene format (`.scn` / `.scn.ron`).
199    ///
200    /// The Bevy scene format is based on [Rusty Object Notation (RON)]. It describes the scene
201    /// in a human-friendly format. To deserialize the scene, use the [`SceneLoader`].
202    ///
203    /// [`SceneLoader`]: crate::SceneLoader
204    /// [Rusty Object Notation (RON)]: https://crates.io/crates/ron
205    #[cfg(feature = "serialize")]
206    pub fn serialize(&self, registry: &TypeRegistry) -> Result<String, ron::Error> {
207        serialize_ron(SceneSerializer::new(self, registry))
208    }
209}
210
211/// Serialize a given Rust data structure into rust object notation (ron).
212#[cfg(feature = "serialize")]
213pub fn serialize_ron<S>(serialize: S) -> Result<String, ron::Error>
214where
215    S: Serialize,
216{
217    let pretty_config = ron::ser::PrettyConfig::default()
218        .indentor("  ".to_string())
219        .new_line("\n".to_string());
220    ron::ser::to_string_pretty(&serialize, pretty_config)
221}
222
223#[cfg(test)]
224mod tests {
225    use bevy_ecs::{
226        component::Component,
227        entity::{Entity, EntityHashMap, EntityMapper, MapEntities},
228        hierarchy::ChildOf,
229        reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
230        resource::Resource,
231        world::World,
232    };
233
234    use bevy_reflect::Reflect;
235
236    use crate::dynamic_scene::DynamicScene;
237    use crate::dynamic_scene_builder::DynamicSceneBuilder;
238
239    #[derive(Resource, Reflect, MapEntities, Debug)]
240    #[reflect(Resource, MapEntities)]
241    struct TestResource {
242        #[entities]
243        entity_a: Entity,
244        #[entities]
245        entity_b: Entity,
246    }
247
248    #[test]
249    fn resource_entity_map_maps_entities() {
250        let type_registry = AppTypeRegistry::default();
251        type_registry.write().register::<TestResource>();
252
253        let mut source_world = World::new();
254        source_world.insert_resource(type_registry.clone());
255
256        let original_entity_a = source_world.spawn_empty().id();
257        let original_entity_b = source_world.spawn_empty().id();
258
259        source_world.insert_resource(TestResource {
260            entity_a: original_entity_a,
261            entity_b: original_entity_b,
262        });
263
264        // Write the scene.
265        let scene = DynamicSceneBuilder::from_world(&source_world)
266            .extract_resources()
267            .extract_entity(original_entity_a)
268            .extract_entity(original_entity_b)
269            .build();
270
271        let mut entity_map = EntityHashMap::default();
272        let mut destination_world = World::new();
273        destination_world.insert_resource(type_registry);
274
275        scene
276            .write_to_world(&mut destination_world, &mut entity_map)
277            .unwrap();
278
279        let &from_entity_a = entity_map.get(&original_entity_a).unwrap();
280        let &from_entity_b = entity_map.get(&original_entity_b).unwrap();
281
282        let test_resource = destination_world.get_resource::<TestResource>().unwrap();
283        assert_eq!(from_entity_a, test_resource.entity_a);
284        assert_eq!(from_entity_b, test_resource.entity_b);
285    }
286
287    #[test]
288    fn components_not_defined_in_scene_should_not_be_affected_by_scene_entity_map() {
289        // Testing that scene reloading applies EntityMap correctly to MapEntities components.
290
291        // First, we create a simple world with a parent and a child relationship
292        let mut world = World::new();
293        world.init_resource::<AppTypeRegistry>();
294        world
295            .resource_mut::<AppTypeRegistry>()
296            .write()
297            .register::<ChildOf>();
298        let original_parent_entity = world.spawn_empty().id();
299        let original_child_entity = world.spawn_empty().id();
300        world
301            .entity_mut(original_parent_entity)
302            .add_child(original_child_entity);
303
304        // We then write this relationship to a new scene, and then write that scene back to the
305        // world to create another parent and child relationship
306        let scene = DynamicSceneBuilder::from_world(&world)
307            .extract_entity(original_parent_entity)
308            .extract_entity(original_child_entity)
309            .build();
310        let mut entity_map = EntityHashMap::default();
311        scene.write_to_world(&mut world, &mut entity_map).unwrap();
312
313        let &from_scene_parent_entity = entity_map.get(&original_parent_entity).unwrap();
314        let &from_scene_child_entity = entity_map.get(&original_child_entity).unwrap();
315
316        // We then add the parent from the scene as a child of the original child
317        // Hierarchy should look like:
318        // Original Parent <- Original Child <- Scene Parent <- Scene Child
319        world
320            .entity_mut(original_child_entity)
321            .add_child(from_scene_parent_entity);
322
323        // We then reload the scene to make sure that from_scene_parent_entity's parent component
324        // isn't updated with the entity map, since this component isn't defined in the scene.
325        // With [`bevy_ecs::hierarchy`], this can cause serious errors and malformed hierarchies.
326        scene.write_to_world(&mut world, &mut entity_map).unwrap();
327
328        assert_eq!(
329            original_parent_entity,
330            world
331                .get_entity(original_child_entity)
332                .unwrap()
333                .get::<ChildOf>()
334                .unwrap()
335                .parent(),
336            "something about reloading the scene is touching entities with the same scene Ids"
337        );
338        assert_eq!(
339            original_child_entity,
340            world
341                .get_entity(from_scene_parent_entity)
342                .unwrap()
343                .get::<ChildOf>()
344                .unwrap()
345                .parent(),
346            "something about reloading the scene is touching components not defined in the scene but on entities defined in the scene"
347        );
348        assert_eq!(
349            from_scene_parent_entity,
350            world
351                .get_entity(from_scene_child_entity)
352                .unwrap()
353                .get::<ChildOf>()
354                .expect("something is wrong with this test, and the scene components don't have a parent/child relationship")
355                .parent(),
356            "something is wrong with this test or the code reloading scenes since the relationship between scene entities is broken"
357        );
358    }
359
360    // Regression test for https://github.com/bevyengine/bevy/issues/14300
361    // Fails before the fix in https://github.com/bevyengine/bevy/pull/15405
362    #[test]
363    fn no_panic_in_map_entities_after_pending_entity_in_hook() {
364        #[derive(Default, Component, Reflect)]
365        #[reflect(Component)]
366        struct A;
367
368        #[derive(Component, Reflect)]
369        #[reflect(Component)]
370        struct B(pub Entity);
371
372        impl MapEntities for B {
373            fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
374                self.0 = entity_mapper.get_mapped(self.0);
375            }
376        }
377
378        let reg = AppTypeRegistry::default();
379        {
380            let mut reg_write = reg.write();
381            reg_write.register::<A>();
382            reg_write.register::<B>();
383        }
384
385        let mut scene_world = World::new();
386        scene_world.insert_resource(reg.clone());
387        scene_world.spawn((B(Entity::PLACEHOLDER), A));
388        let scene = DynamicScene::from_world(&scene_world);
389
390        let mut dst_world = World::new();
391        dst_world
392            .register_component_hooks::<A>()
393            .on_add(|mut world, _| {
394                world.commands().spawn_empty();
395            });
396        dst_world.insert_resource(reg.clone());
397
398        // Should not panic.
399        // Prior to fix, the `Entities::alloc` call in
400        // `EntityMapper::map_entity` would panic due to pending entities from the observer
401        // not having been flushed.
402        scene
403            .write_to_world(&mut dst_world, &mut Default::default())
404            .unwrap();
405    }
406}