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
7extern 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
27pub 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
38pub 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#[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 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 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 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 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 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 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}