Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_scene/src/lib.rs
6598 views
1
#![cfg_attr(docsrs, feature(doc_auto_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
13
extern crate alloc;
14
15
mod components;
16
mod dynamic_scene;
17
mod dynamic_scene_builder;
18
mod reflect_utils;
19
mod scene;
20
mod scene_filter;
21
mod scene_loader;
22
mod scene_spawner;
23
24
#[cfg(feature = "serialize")]
25
pub mod serde;
26
27
/// Rusty Object Notation, a crate used to serialize and deserialize bevy scenes.
28
pub use bevy_asset::ron;
29
30
pub use components::*;
31
pub use dynamic_scene::*;
32
pub use dynamic_scene_builder::*;
33
pub use scene::*;
34
pub use scene_filter::*;
35
pub use scene_loader::*;
36
pub use scene_spawner::*;
37
38
/// The scene prelude.
39
///
40
/// This includes the most common types in this crate, re-exported for your convenience.
41
pub mod prelude {
42
#[doc(hidden)]
43
pub use crate::{
44
DynamicScene, DynamicSceneBuilder, DynamicSceneRoot, Scene, SceneFilter, SceneRoot,
45
SceneSpawner,
46
};
47
}
48
49
use bevy_app::prelude::*;
50
51
#[cfg(feature = "serialize")]
52
use {bevy_asset::AssetApp, bevy_ecs::schedule::IntoScheduleConfigs};
53
54
/// Plugin that provides scene functionality to an [`App`].
55
#[derive(Default)]
56
pub struct ScenePlugin;
57
58
#[cfg(feature = "serialize")]
59
impl 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"))]
112
impl Plugin for ScenePlugin {
113
fn build(&self, _: &mut App) {}
114
}
115
116
#[cfg(test)]
117
mod 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
}
431
432