Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_scene/src/lib.rs
9550 views
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
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
pub use components::*;
28
pub use dynamic_scene::*;
29
pub use dynamic_scene_builder::*;
30
pub use scene::*;
31
pub use scene_filter::*;
32
pub use scene_loader::*;
33
pub use scene_spawner::*;
34
35
/// The scene prelude.
36
///
37
/// This includes the most common types in this crate, re-exported for your convenience.
38
pub mod prelude {
39
#[doc(hidden)]
40
pub use crate::{
41
DynamicScene, DynamicSceneBuilder, DynamicSceneRoot, Scene, SceneFilter, SceneRoot,
42
SceneSpawner,
43
};
44
}
45
46
use bevy_app::prelude::*;
47
48
#[cfg(feature = "serialize")]
49
use {bevy_asset::AssetApp, bevy_ecs::schedule::IntoScheduleConfigs};
50
51
/// Plugin that provides scene functionality to an [`App`].
52
#[derive(Default)]
53
pub struct ScenePlugin;
54
55
#[cfg(feature = "serialize")]
56
impl Plugin for ScenePlugin {
57
fn build(&self, app: &mut App) {
58
app.init_asset::<DynamicScene>()
59
.init_asset::<Scene>()
60
.init_asset_loader::<SceneLoader>()
61
.init_resource::<SceneSpawner>()
62
.add_systems(
63
SpawnScene,
64
(scene_spawner, scene_spawner_system)
65
.chain()
66
.in_set(SceneSpawnerSystems::Spawn),
67
);
68
69
// Register component hooks for DynamicSceneRoot
70
app.world_mut()
71
.register_component_hooks::<DynamicSceneRoot>()
72
.on_remove(|mut world, context| {
73
let Some(handle) = world.get::<DynamicSceneRoot>(context.entity) else {
74
return;
75
};
76
let id = handle.id();
77
if let Some(&SceneInstance(scene_instance)) =
78
world.get::<SceneInstance>(context.entity)
79
{
80
let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else {
81
return;
82
};
83
if let Some(instance_ids) = scene_spawner.spawned_dynamic_scenes.get_mut(&id) {
84
instance_ids.remove(&scene_instance);
85
}
86
scene_spawner.unregister_instance(scene_instance);
87
}
88
});
89
90
// Register component hooks for SceneRoot
91
app.world_mut()
92
.register_component_hooks::<SceneRoot>()
93
.on_remove(|mut world, context| {
94
let Some(handle) = world.get::<SceneRoot>(context.entity) else {
95
return;
96
};
97
let id = handle.id();
98
if let Some(&SceneInstance(scene_instance)) =
99
world.get::<SceneInstance>(context.entity)
100
{
101
let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else {
102
return;
103
};
104
if let Some(instance_ids) = scene_spawner.spawned_scenes.get_mut(&id) {
105
instance_ids.remove(&scene_instance);
106
}
107
scene_spawner.unregister_instance(scene_instance);
108
}
109
});
110
}
111
}
112
113
#[cfg(not(feature = "serialize"))]
114
impl Plugin for ScenePlugin {
115
fn build(&self, _: &mut App) {}
116
}
117
118
#[cfg(test)]
119
mod tests {
120
use bevy_app::App;
121
use bevy_asset::{AssetPlugin, Assets};
122
use bevy_ecs::{
123
component::Component,
124
entity::Entity,
125
hierarchy::{ChildOf, Children},
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::<ChildOf>()
165
.register_type::<Children>()
166
.register_type::<Circle>()
167
.register_type::<Rectangle>()
168
.register_type::<Triangle>()
169
.register_type::<FinishLine>();
170
171
let scene_handle = app
172
.world_mut()
173
.resource_mut::<Assets<Scene>>()
174
.reserve_handle();
175
176
let scene_entity = app.world_mut().spawn(SceneRoot(scene_handle.clone())).id();
177
app.update();
178
179
assert!(app.world().entity(scene_entity).get::<Children>().is_none());
180
181
let mut scene_1 = Scene {
182
world: World::new(),
183
};
184
let root = scene_1.world.spawn_empty().id();
185
scene_1.world.spawn((
186
Rectangle {
187
width: 10.0,
188
height: 5.0,
189
},
190
FinishLine,
191
ChildOf(root),
192
));
193
scene_1.world.spawn((Circle { radius: 7.0 }, ChildOf(root)));
194
195
app.world_mut()
196
.resource_mut::<Assets<Scene>>()
197
.insert(&scene_handle, scene_1)
198
.unwrap();
199
200
app.update();
201
// TODO: multiple updates to avoid debounced asset events. See comment on SceneSpawner::debounced_scene_asset_events
202
app.update();
203
app.update();
204
app.update();
205
206
let child_root = app
207
.world()
208
.entity(scene_entity)
209
.get::<Children>()
210
.and_then(|children| children.first().cloned())
211
.expect("There should be exactly one child on the scene root");
212
let children = app
213
.world()
214
.entity(child_root)
215
.get::<Children>()
216
.expect("The child of the scene root should itself have 2 children");
217
assert_eq!(children.len(), 2);
218
219
let finish_line = app.world().entity(children[0]);
220
assert_eq!(finish_line.archetype().component_count(), 3);
221
let (rectangle, _, child_of) =
222
finish_line.components::<(&Rectangle, &FinishLine, &ChildOf)>();
223
assert_eq!(
224
rectangle,
225
&Rectangle {
226
width: 10.0,
227
height: 5.0,
228
}
229
);
230
assert_eq!(child_of.0, child_root);
231
232
let circle = app.world().entity(children[1]);
233
assert_eq!(circle.archetype().component_count(), 2);
234
let (circle, child_of) = circle.components::<(&Circle, &ChildOf)>();
235
assert_eq!(circle, &Circle { radius: 7.0 });
236
assert_eq!(child_of.0, child_root);
237
238
// Now that we know our scene contains exactly what we expect, we will change the scene
239
// asset and ensure it contains the new scene results.
240
241
let mut scene_2 = Scene {
242
world: World::new(),
243
};
244
let root = scene_2.world.spawn_empty().id();
245
scene_2.world.spawn((
246
Triangle {
247
base: 1.0,
248
height: 2.0,
249
},
250
ChildOf(root),
251
));
252
253
app.world_mut()
254
.resource_mut::<Assets<Scene>>()
255
.insert(&scene_handle, scene_2)
256
.unwrap();
257
258
app.update();
259
app.update();
260
261
let child_root = app
262
.world()
263
.entity(scene_entity)
264
.get::<Children>()
265
.and_then(|children| children.first().cloned())
266
.expect("There should be exactly one child on the scene root");
267
let children = app
268
.world()
269
.entity(child_root)
270
.get::<Children>()
271
.expect("The child of the scene root should itself have 2 children");
272
assert_eq!(children.len(), 1);
273
274
let triangle = app.world().entity(children[0]);
275
assert_eq!(triangle.archetype().component_count(), 2);
276
let (triangle, child_of) = triangle.components::<(&Triangle, &ChildOf)>();
277
assert_eq!(
278
triangle,
279
&Triangle {
280
base: 1.0,
281
height: 2.0,
282
}
283
);
284
assert_eq!(child_of.0, child_root);
285
}
286
287
#[test]
288
fn dynamic_scene_spawns_and_respawns_after_change() {
289
let mut app = App::new();
290
291
app.add_plugins((AssetPlugin::default(), ScenePlugin))
292
.register_type::<ChildOf>()
293
.register_type::<Children>()
294
.register_type::<Circle>()
295
.register_type::<Rectangle>()
296
.register_type::<Triangle>()
297
.register_type::<FinishLine>();
298
299
let scene_handle = app
300
.world_mut()
301
.resource_mut::<Assets<DynamicScene>>()
302
.reserve_handle();
303
304
let scene_entity = app
305
.world_mut()
306
.spawn(DynamicSceneRoot(scene_handle.clone()))
307
.id();
308
app.update();
309
310
assert!(app.world().entity(scene_entity).get::<Children>().is_none());
311
312
let create_dynamic_scene = |mut scene: Scene, world: &World| {
313
scene
314
.world
315
.insert_resource(world.resource::<AppTypeRegistry>().clone());
316
let entities: Vec<Entity> = scene.world.query::<Entity>().iter(&scene.world).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