Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_scene/src/lib.rs
9350 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::<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.world.query::<Entity>().iter(&scene.world).collect();
313
DynamicSceneBuilder::from_world(&scene.world)
314
.extract_entities(entities.into_iter())
315
.build()
316
};
317
318
let mut scene_1 = Scene {
319
world: World::new(),
320
};
321
let root = scene_1.world.spawn_empty().id();
322
scene_1.world.spawn((
323
Rectangle {
324
width: 10.0,
325
height: 5.0,
326
},
327
FinishLine,
328
ChildOf(root),
329
));
330
scene_1.world.spawn((Circle { radius: 7.0 }, ChildOf(root)));
331
332
let scene_1 = create_dynamic_scene(scene_1, app.world());
333
app.world_mut()
334
.resource_mut::<Assets<DynamicScene>>()
335
.insert(&scene_handle, scene_1)
336
.unwrap();
337
338
app.update();
339
// TODO: multiple updates to avoid debounced asset events. See comment on SceneSpawner::debounced_scene_asset_events
340
app.update();
341
app.update();
342
app.update();
343
344
let child_root = app
345
.world()
346
.entity(scene_entity)
347
.get::<Children>()
348
.and_then(|children| children.first().cloned())
349
.expect("There should be exactly one child on the scene root");
350
let children = app
351
.world()
352
.entity(child_root)
353
.get::<Children>()
354
.expect("The child of the scene root should itself have 2 children");
355
assert_eq!(children.len(), 2);
356
357
let finish_line = app.world().entity(children[0]);
358
assert_eq!(finish_line.archetype().component_count(), 3);
359
let (rectangle, _, child_of) =
360
finish_line.components::<(&Rectangle, &FinishLine, &ChildOf)>();
361
assert_eq!(
362
rectangle,
363
&Rectangle {
364
width: 10.0,
365
height: 5.0,
366
}
367
);
368
assert_eq!(child_of.0, child_root);
369
370
let circle = app.world().entity(children[1]);
371
assert_eq!(circle.archetype().component_count(), 2);
372
let (circle, child_of) = circle.components::<(&Circle, &ChildOf)>();
373
assert_eq!(circle, &Circle { radius: 7.0 });
374
assert_eq!(child_of.0, child_root);
375
376
// Now that we know our scene contains exactly what we expect, we will change the scene
377
// asset and ensure it contains the new scene results.
378
379
let mut scene_2 = Scene {
380
world: World::new(),
381
};
382
let root = scene_2.world.spawn_empty().id();
383
scene_2.world.spawn((
384
Triangle {
385
base: 1.0,
386
height: 2.0,
387
},
388
ChildOf(root),
389
));
390
391
let scene_2 = create_dynamic_scene(scene_2, app.world());
392
393
app.world_mut()
394
.resource_mut::<Assets<DynamicScene>>()
395
.insert(&scene_handle, scene_2)
396
.unwrap();
397
398
app.update();
399
app.update();
400
401
let child_root = app
402
.world()
403
.entity(scene_entity)
404
.get::<Children>()
405
.and_then(|children| children.first().cloned())
406
.expect("There should be exactly one child on the scene root");
407
let children = app
408
.world()
409
.entity(child_root)
410
.get::<Children>()
411
.expect("The child of the scene root should itself have 2 children");
412
assert_eq!(children.len(), 1);
413
414
let triangle = app.world().entity(children[0]);
415
assert_eq!(triangle.archetype().component_count(), 2);
416
let (triangle, child_of) = triangle.components::<(&Triangle, &ChildOf)>();
417
assert_eq!(
418
triangle,
419
&Triangle {
420
base: 1.0,
421
height: 2.0,
422
}
423
);
424
assert_eq!(child_of.0, child_root);
425
}
426
}
427
428