bevy_scene/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc(
3    html_logo_url = "https://bevy.org/assets/icon.png",
4    html_favicon_url = "https://bevy.org/assets/icon.png"
5)]
6
7//! Provides scene definition, instantiation and serialization/deserialization.
8//!
9//! Scenes are collections of entities and their associated components that can be
10//! instantiated or removed from a world to allow composition. Scenes can be serialized/deserialized,
11//! for example to save part of the world state to a file.
12
13extern crate alloc;
14
15mod components;
16mod dynamic_scene;
17mod dynamic_scene_builder;
18mod reflect_utils;
19mod scene;
20mod scene_filter;
21mod scene_loader;
22mod scene_spawner;
23
24#[cfg(feature = "serialize")]
25pub mod serde;
26
27/// Rusty Object Notation, a crate used to serialize and deserialize bevy scenes.
28pub use bevy_asset::ron;
29
30pub use components::*;
31pub use dynamic_scene::*;
32pub use dynamic_scene_builder::*;
33pub use scene::*;
34pub use scene_filter::*;
35pub use scene_loader::*;
36pub use scene_spawner::*;
37
38/// The scene prelude.
39///
40/// This includes the most common types in this crate, re-exported for your convenience.
41pub mod prelude {
42    #[doc(hidden)]
43    pub use crate::{
44        DynamicScene, DynamicSceneBuilder, DynamicSceneRoot, Scene, SceneFilter, SceneRoot,
45        SceneSpawner,
46    };
47}
48
49use bevy_app::prelude::*;
50
51#[cfg(feature = "serialize")]
52use {bevy_asset::AssetApp, bevy_ecs::schedule::IntoScheduleConfigs};
53
54/// Plugin that provides scene functionality to an [`App`].
55#[derive(Default)]
56pub struct ScenePlugin;
57
58#[cfg(feature = "serialize")]
59impl Plugin for ScenePlugin {
60    fn build(&self, app: &mut App) {
61        app.init_asset::<DynamicScene>()
62            .init_asset::<Scene>()
63            .init_asset_loader::<SceneLoader>()
64            .init_resource::<SceneSpawner>()
65            .add_systems(SpawnScene, (scene_spawner, scene_spawner_system).chain());
66
67        // Register component hooks for DynamicSceneRoot
68        app.world_mut()
69            .register_component_hooks::<DynamicSceneRoot>()
70            .on_remove(|mut world, context| {
71                let Some(handle) = world.get::<DynamicSceneRoot>(context.entity) else {
72                    return;
73                };
74                let id = handle.id();
75                if let Some(&SceneInstance(scene_instance)) =
76                    world.get::<SceneInstance>(context.entity)
77                {
78                    let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else {
79                        return;
80                    };
81                    if let Some(instance_ids) = scene_spawner.spawned_dynamic_scenes.get_mut(&id) {
82                        instance_ids.remove(&scene_instance);
83                    }
84                    scene_spawner.unregister_instance(scene_instance);
85                }
86            });
87
88        // Register component hooks for SceneRoot
89        app.world_mut()
90            .register_component_hooks::<SceneRoot>()
91            .on_remove(|mut world, context| {
92                let Some(handle) = world.get::<SceneRoot>(context.entity) else {
93                    return;
94                };
95                let id = handle.id();
96                if let Some(&SceneInstance(scene_instance)) =
97                    world.get::<SceneInstance>(context.entity)
98                {
99                    let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else {
100                        return;
101                    };
102                    if let Some(instance_ids) = scene_spawner.spawned_scenes.get_mut(&id) {
103                        instance_ids.remove(&scene_instance);
104                    }
105                    scene_spawner.unregister_instance(scene_instance);
106                }
107            });
108    }
109}
110
111#[cfg(not(feature = "serialize"))]
112impl Plugin for ScenePlugin {
113    fn build(&self, _: &mut App) {}
114}
115
116#[cfg(test)]
117mod tests {
118    use bevy_app::App;
119    use bevy_asset::{AssetPlugin, Assets};
120    use bevy_ecs::{
121        component::Component,
122        entity::Entity,
123        entity_disabling::Internal,
124        hierarchy::{ChildOf, Children},
125        query::Allow,
126        reflect::{AppTypeRegistry, ReflectComponent},
127        world::World,
128    };
129    use bevy_reflect::Reflect;
130
131    use crate::{
132        DynamicScene, DynamicSceneBuilder, DynamicSceneRoot, Scene, ScenePlugin, SceneRoot,
133    };
134
135    #[derive(Component, Reflect, PartialEq, Debug)]
136    #[reflect(Component)]
137    struct Circle {
138        radius: f32,
139    }
140
141    #[derive(Component, Reflect, PartialEq, Debug)]
142    #[reflect(Component)]
143    struct Rectangle {
144        width: f32,
145        height: f32,
146    }
147
148    #[derive(Component, Reflect, PartialEq, Debug)]
149    #[reflect(Component)]
150    struct Triangle {
151        base: f32,
152        height: f32,
153    }
154
155    #[derive(Component, Reflect)]
156    #[reflect(Component)]
157    struct FinishLine;
158
159    #[test]
160    fn scene_spawns_and_respawns_after_change() {
161        let mut app = App::new();
162
163        app.add_plugins((AssetPlugin::default(), ScenePlugin))
164            .register_type::<Circle>()
165            .register_type::<Rectangle>()
166            .register_type::<Triangle>()
167            .register_type::<FinishLine>();
168
169        let scene_handle = app
170            .world_mut()
171            .resource_mut::<Assets<Scene>>()
172            .reserve_handle();
173
174        let scene_entity = app.world_mut().spawn(SceneRoot(scene_handle.clone())).id();
175        app.update();
176
177        assert!(app.world().entity(scene_entity).get::<Children>().is_none());
178
179        let mut scene_1 = Scene {
180            world: World::new(),
181        };
182        let root = scene_1.world.spawn_empty().id();
183        scene_1.world.spawn((
184            Rectangle {
185                width: 10.0,
186                height: 5.0,
187            },
188            FinishLine,
189            ChildOf(root),
190        ));
191        scene_1.world.spawn((Circle { radius: 7.0 }, ChildOf(root)));
192
193        app.world_mut()
194            .resource_mut::<Assets<Scene>>()
195            .insert(&scene_handle, scene_1)
196            .unwrap();
197
198        app.update();
199        // TODO: multiple updates to avoid debounced asset events. See comment on SceneSpawner::debounced_scene_asset_events
200        app.update();
201        app.update();
202        app.update();
203
204        let child_root = app
205            .world()
206            .entity(scene_entity)
207            .get::<Children>()
208            .and_then(|children| children.first().cloned())
209            .expect("There should be exactly one child on the scene root");
210        let children = app
211            .world()
212            .entity(child_root)
213            .get::<Children>()
214            .expect("The child of the scene root should itself have 2 children");
215        assert_eq!(children.len(), 2);
216
217        let finish_line = app.world().entity(children[0]);
218        assert_eq!(finish_line.archetype().component_count(), 3);
219        let (rectangle, _, child_of) =
220            finish_line.components::<(&Rectangle, &FinishLine, &ChildOf)>();
221        assert_eq!(
222            rectangle,
223            &Rectangle {
224                width: 10.0,
225                height: 5.0,
226            }
227        );
228        assert_eq!(child_of.0, child_root);
229
230        let circle = app.world().entity(children[1]);
231        assert_eq!(circle.archetype().component_count(), 2);
232        let (circle, child_of) = circle.components::<(&Circle, &ChildOf)>();
233        assert_eq!(circle, &Circle { radius: 7.0 });
234        assert_eq!(child_of.0, child_root);
235
236        // Now that we know our scene contains exactly what we expect, we will change the scene
237        // asset and ensure it contains the new scene results.
238
239        let mut scene_2 = Scene {
240            world: World::new(),
241        };
242        let root = scene_2.world.spawn_empty().id();
243        scene_2.world.spawn((
244            Triangle {
245                base: 1.0,
246                height: 2.0,
247            },
248            ChildOf(root),
249        ));
250
251        app.world_mut()
252            .resource_mut::<Assets<Scene>>()
253            .insert(&scene_handle, scene_2)
254            .unwrap();
255
256        app.update();
257        app.update();
258
259        let child_root = app
260            .world()
261            .entity(scene_entity)
262            .get::<Children>()
263            .and_then(|children| children.first().cloned())
264            .expect("There should be exactly one child on the scene root");
265        let children = app
266            .world()
267            .entity(child_root)
268            .get::<Children>()
269            .expect("The child of the scene root should itself have 2 children");
270        assert_eq!(children.len(), 1);
271
272        let triangle = app.world().entity(children[0]);
273        assert_eq!(triangle.archetype().component_count(), 2);
274        let (triangle, child_of) = triangle.components::<(&Triangle, &ChildOf)>();
275        assert_eq!(
276            triangle,
277            &Triangle {
278                base: 1.0,
279                height: 2.0,
280            }
281        );
282        assert_eq!(child_of.0, child_root);
283    }
284
285    #[test]
286    fn dynamic_scene_spawns_and_respawns_after_change() {
287        let mut app = App::new();
288
289        app.add_plugins((AssetPlugin::default(), ScenePlugin))
290            .register_type::<Circle>()
291            .register_type::<Rectangle>()
292            .register_type::<Triangle>()
293            .register_type::<FinishLine>();
294
295        let scene_handle = app
296            .world_mut()
297            .resource_mut::<Assets<DynamicScene>>()
298            .reserve_handle();
299
300        let scene_entity = app
301            .world_mut()
302            .spawn(DynamicSceneRoot(scene_handle.clone()))
303            .id();
304        app.update();
305
306        assert!(app.world().entity(scene_entity).get::<Children>().is_none());
307
308        let create_dynamic_scene = |mut scene: Scene, world: &World| {
309            scene
310                .world
311                .insert_resource(world.resource::<AppTypeRegistry>().clone());
312            let entities: Vec<Entity> = scene
313                .world
314                .query_filtered::<Entity, Allow<Internal>>()
315                .iter(&scene.world)
316                .collect();
317            DynamicSceneBuilder::from_world(&scene.world)
318                .extract_entities(entities.into_iter())
319                .build()
320        };
321
322        let mut scene_1 = Scene {
323            world: World::new(),
324        };
325        let root = scene_1.world.spawn_empty().id();
326        scene_1.world.spawn((
327            Rectangle {
328                width: 10.0,
329                height: 5.0,
330            },
331            FinishLine,
332            ChildOf(root),
333        ));
334        scene_1.world.spawn((Circle { radius: 7.0 }, ChildOf(root)));
335
336        let scene_1 = create_dynamic_scene(scene_1, app.world());
337        app.world_mut()
338            .resource_mut::<Assets<DynamicScene>>()
339            .insert(&scene_handle, scene_1)
340            .unwrap();
341
342        app.update();
343        // TODO: multiple updates to avoid debounced asset events. See comment on SceneSpawner::debounced_scene_asset_events
344        app.update();
345        app.update();
346        app.update();
347
348        let child_root = app
349            .world()
350            .entity(scene_entity)
351            .get::<Children>()
352            .and_then(|children| children.first().cloned())
353            .expect("There should be exactly one child on the scene root");
354        let children = app
355            .world()
356            .entity(child_root)
357            .get::<Children>()
358            .expect("The child of the scene root should itself have 2 children");
359        assert_eq!(children.len(), 2);
360
361        let finish_line = app.world().entity(children[0]);
362        assert_eq!(finish_line.archetype().component_count(), 3);
363        let (rectangle, _, child_of) =
364            finish_line.components::<(&Rectangle, &FinishLine, &ChildOf)>();
365        assert_eq!(
366            rectangle,
367            &Rectangle {
368                width: 10.0,
369                height: 5.0,
370            }
371        );
372        assert_eq!(child_of.0, child_root);
373
374        let circle = app.world().entity(children[1]);
375        assert_eq!(circle.archetype().component_count(), 2);
376        let (circle, child_of) = circle.components::<(&Circle, &ChildOf)>();
377        assert_eq!(circle, &Circle { radius: 7.0 });
378        assert_eq!(child_of.0, child_root);
379
380        // Now that we know our scene contains exactly what we expect, we will change the scene
381        // asset and ensure it contains the new scene results.
382
383        let mut scene_2 = Scene {
384            world: World::new(),
385        };
386        let root = scene_2.world.spawn_empty().id();
387        scene_2.world.spawn((
388            Triangle {
389                base: 1.0,
390                height: 2.0,
391            },
392            ChildOf(root),
393        ));
394
395        let scene_2 = create_dynamic_scene(scene_2, app.world());
396
397        app.world_mut()
398            .resource_mut::<Assets<DynamicScene>>()
399            .insert(&scene_handle, scene_2)
400            .unwrap();
401
402        app.update();
403        app.update();
404
405        let child_root = app
406            .world()
407            .entity(scene_entity)
408            .get::<Children>()
409            .and_then(|children| children.first().cloned())
410            .expect("There should be exactly one child on the scene root");
411        let children = app
412            .world()
413            .entity(child_root)
414            .get::<Children>()
415            .expect("The child of the scene root should itself have 2 children");
416        assert_eq!(children.len(), 1);
417
418        let triangle = app.world().entity(children[0]);
419        assert_eq!(triangle.archetype().component_count(), 2);
420        let (triangle, child_of) = triangle.components::<(&Triangle, &ChildOf)>();
421        assert_eq!(
422            triangle,
423            &Triangle {
424                base: 1.0,
425                height: 2.0,
426            }
427        );
428        assert_eq!(child_of.0, child_root);
429    }
430}