Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_scene/src/scene_spawner.rs
6598 views
1
use crate::{DynamicScene, Scene};
2
use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
3
use bevy_ecs::{
4
entity::{Entity, EntityHashMap},
5
event::{EntityEvent, EventCursor, Events},
6
hierarchy::ChildOf,
7
reflect::AppTypeRegistry,
8
resource::Resource,
9
world::{Mut, World},
10
};
11
use bevy_platform::collections::{HashMap, HashSet};
12
use bevy_reflect::Reflect;
13
use bevy_utils::prelude::DebugName;
14
use thiserror::Error;
15
use uuid::Uuid;
16
17
use crate::{DynamicSceneRoot, SceneRoot};
18
use bevy_derive::{Deref, DerefMut};
19
use bevy_ecs::{
20
change_detection::ResMut,
21
prelude::{Changed, Component, Without},
22
system::{Commands, Query},
23
};
24
25
/// Triggered on a scene's parent entity when [`crate::SceneInstance`] becomes ready to use.
26
///
27
/// See also [`On`], [`SceneSpawner::instance_is_ready`].
28
///
29
/// [`On`]: bevy_ecs::observer::On
30
#[derive(Clone, Copy, Debug, Eq, PartialEq, EntityEvent, Reflect)]
31
#[reflect(Debug, PartialEq, Clone)]
32
pub struct SceneInstanceReady {
33
/// Instance which has been spawned.
34
pub instance_id: InstanceId,
35
}
36
37
/// Information about a scene instance.
38
#[derive(Debug)]
39
struct InstanceInfo {
40
/// Mapping of entities from the scene world to the instance world.
41
entity_map: EntityHashMap<Entity>,
42
/// The parent to attach this instance to.
43
parent: Option<Entity>,
44
}
45
46
/// Unique id identifying a scene instance.
47
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Reflect)]
48
#[reflect(Debug, PartialEq, Hash, Clone)]
49
pub struct InstanceId(Uuid);
50
51
impl InstanceId {
52
fn new() -> Self {
53
InstanceId(Uuid::new_v4())
54
}
55
}
56
57
/// Handles spawning and despawning scenes in the world, either synchronously or batched through the [`scene_spawner_system`].
58
///
59
/// Synchronous methods: (Scene operations will take effect immediately)
60
/// - [`spawn_sync`](Self::spawn_sync)
61
/// - [`spawn_dynamic_sync`](Self::spawn_dynamic_sync)
62
/// - [`despawn_sync`](Self::despawn_sync)
63
/// - [`despawn_dynamic_sync`](Self::despawn_dynamic_sync)
64
/// - [`despawn_instance_sync`](Self::despawn_instance_sync)
65
/// - [`update_spawned_scenes`](Self::update_spawned_scenes)
66
/// - [`update_spawned_dynamic_scenes`](Self::update_spawned_dynamic_scenes)
67
/// - [`spawn_queued_scenes`](Self::spawn_queued_scenes)
68
/// - [`despawn_queued_scenes`](Self::despawn_queued_scenes)
69
/// - [`despawn_queued_instances`](Self::despawn_queued_instances)
70
///
71
/// Deferred methods: (Scene operations will be processed when the [`scene_spawner_system`] is run)
72
/// - [`spawn_dynamic`](Self::spawn_dynamic)
73
/// - [`spawn_dynamic_as_child`](Self::spawn_dynamic_as_child)
74
/// - [`spawn`](Self::spawn)
75
/// - [`spawn_as_child`](Self::spawn_as_child)
76
/// - [`despawn`](Self::despawn)
77
/// - [`despawn_dynamic`](Self::despawn_dynamic)
78
/// - [`despawn_instance`](Self::despawn_instance)
79
#[derive(Default, Resource)]
80
pub struct SceneSpawner {
81
pub(crate) spawned_scenes: HashMap<AssetId<Scene>, HashSet<InstanceId>>,
82
pub(crate) spawned_dynamic_scenes: HashMap<AssetId<DynamicScene>, HashSet<InstanceId>>,
83
spawned_instances: HashMap<InstanceId, InstanceInfo>,
84
scene_asset_event_reader: EventCursor<AssetEvent<Scene>>,
85
// TODO: temp fix for https://github.com/bevyengine/bevy/issues/12756 effect on scenes
86
// To handle scene hot reloading, they are unloaded/reloaded on asset modifications.
87
// When loading several subassets of a scene as is common with gltf, they each trigger a complete asset load,
88
// and each will trigger either a created or modified event for the parent asset. This causes the scene to be
89
// unloaded, losing its initial setup, and reloaded without it.
90
// Debouncing scene asset events let us ignore events that happen less than SCENE_ASSET_AGE_THRESHOLD frames
91
// apart and not reload the scene in those cases as it's unlikely to be an actual asset change.
92
debounced_scene_asset_events: HashMap<AssetId<Scene>, u32>,
93
dynamic_scene_asset_event_reader: EventCursor<AssetEvent<DynamicScene>>,
94
// TODO: temp fix for https://github.com/bevyengine/bevy/issues/12756 effect on scenes
95
// See debounced_scene_asset_events
96
debounced_dynamic_scene_asset_events: HashMap<AssetId<DynamicScene>, u32>,
97
scenes_to_spawn: Vec<(Handle<Scene>, InstanceId, Option<Entity>)>,
98
dynamic_scenes_to_spawn: Vec<(Handle<DynamicScene>, InstanceId, Option<Entity>)>,
99
scenes_to_despawn: Vec<AssetId<Scene>>,
100
dynamic_scenes_to_despawn: Vec<AssetId<DynamicScene>>,
101
instances_to_despawn: Vec<InstanceId>,
102
instances_ready: Vec<(InstanceId, Option<Entity>)>,
103
}
104
105
/// Errors that can occur when spawning a scene.
106
#[derive(Error, Debug)]
107
pub enum SceneSpawnError {
108
/// Scene contains an unregistered component type.
109
#[error("scene contains the unregistered component `{type_path}`. consider adding `#[reflect(Component)]` to your type")]
110
UnregisteredComponent {
111
/// Type of the unregistered component.
112
type_path: String,
113
},
114
/// Scene contains an unregistered resource type.
115
#[error("scene contains the unregistered resource `{type_path}`. consider adding `#[reflect(Resource)]` to your type")]
116
UnregisteredResource {
117
/// Type of the unregistered resource.
118
type_path: String,
119
},
120
/// Scene contains an unregistered type.
121
#[error(
122
"scene contains the unregistered type `{std_type_name}`. \
123
consider reflecting it with `#[derive(Reflect)]` \
124
and registering the type using `app.register_type::<T>()`"
125
)]
126
UnregisteredType {
127
/// The [type name](std::any::type_name) for the unregistered type.
128
std_type_name: DebugName,
129
},
130
/// Scene contains an unregistered type which has a `TypePath`.
131
#[error(
132
"scene contains the reflected type `{type_path}` but it was not found in the type registry. \
133
consider registering the type using `app.register_type::<T>()``"
134
)]
135
UnregisteredButReflectedType {
136
/// The unregistered type.
137
type_path: String,
138
},
139
/// Scene contains a proxy without a represented type.
140
#[error("scene contains dynamic type `{type_path}` without a represented type. consider changing this using `set_represented_type`.")]
141
NoRepresentedType {
142
/// The dynamic instance type.
143
type_path: String,
144
},
145
/// Dynamic scene with the given id does not exist.
146
#[error("scene does not exist")]
147
NonExistentScene {
148
/// Id of the non-existent dynamic scene.
149
id: AssetId<DynamicScene>,
150
},
151
/// Scene with the given id does not exist.
152
#[error("scene does not exist")]
153
NonExistentRealScene {
154
/// Id of the non-existent scene.
155
id: AssetId<Scene>,
156
},
157
}
158
159
impl SceneSpawner {
160
/// Schedule the spawn of a new instance of the provided dynamic scene.
161
pub fn spawn_dynamic(&mut self, id: impl Into<Handle<DynamicScene>>) -> InstanceId {
162
let instance_id = InstanceId::new();
163
self.dynamic_scenes_to_spawn
164
.push((id.into(), instance_id, None));
165
instance_id
166
}
167
168
/// Schedule the spawn of a new instance of the provided dynamic scene as a child of `parent`.
169
pub fn spawn_dynamic_as_child(
170
&mut self,
171
id: impl Into<Handle<DynamicScene>>,
172
parent: Entity,
173
) -> InstanceId {
174
let instance_id = InstanceId::new();
175
self.dynamic_scenes_to_spawn
176
.push((id.into(), instance_id, Some(parent)));
177
instance_id
178
}
179
180
/// Schedule the spawn of a new instance of the provided scene.
181
pub fn spawn(&mut self, id: impl Into<Handle<Scene>>) -> InstanceId {
182
let instance_id = InstanceId::new();
183
self.scenes_to_spawn.push((id.into(), instance_id, None));
184
instance_id
185
}
186
187
/// Schedule the spawn of a new instance of the provided scene as a child of `parent`.
188
pub fn spawn_as_child(&mut self, id: impl Into<Handle<Scene>>, parent: Entity) -> InstanceId {
189
let instance_id = InstanceId::new();
190
self.scenes_to_spawn
191
.push((id.into(), instance_id, Some(parent)));
192
instance_id
193
}
194
195
/// Schedule the despawn of all instances of the provided scene.
196
pub fn despawn(&mut self, id: impl Into<AssetId<Scene>>) {
197
self.scenes_to_despawn.push(id.into());
198
}
199
200
/// Schedule the despawn of all instances of the provided dynamic scene.
201
pub fn despawn_dynamic(&mut self, id: impl Into<AssetId<DynamicScene>>) {
202
self.dynamic_scenes_to_despawn.push(id.into());
203
}
204
205
/// Schedule the despawn of a scene instance, removing all its entities from the world.
206
///
207
/// Note: this will despawn _all_ entities associated with this instance, including those
208
/// that have been removed from the scene hierarchy. To despawn _only_ entities still in the hierarchy,
209
/// despawn the relevant root entity directly.
210
pub fn despawn_instance(&mut self, instance_id: InstanceId) {
211
self.instances_to_despawn.push(instance_id);
212
}
213
214
/// This will remove all records of this instance, without despawning any entities.
215
pub fn unregister_instance(&mut self, instance_id: InstanceId) {
216
self.spawned_instances.remove(&instance_id);
217
}
218
219
/// Immediately despawns all instances of a scene.
220
pub fn despawn_sync(
221
&mut self,
222
world: &mut World,
223
id: impl Into<AssetId<Scene>>,
224
) -> Result<(), SceneSpawnError> {
225
if let Some(instance_ids) = self.spawned_scenes.remove(&id.into()) {
226
for instance_id in instance_ids {
227
self.despawn_instance_sync(world, &instance_id);
228
}
229
}
230
Ok(())
231
}
232
233
/// Immediately despawns all instances of a dynamic scene.
234
pub fn despawn_dynamic_sync(
235
&mut self,
236
world: &mut World,
237
id: impl Into<AssetId<DynamicScene>>,
238
) -> Result<(), SceneSpawnError> {
239
if let Some(instance_ids) = self.spawned_dynamic_scenes.remove(&id.into()) {
240
for instance_id in instance_ids {
241
self.despawn_instance_sync(world, &instance_id);
242
}
243
}
244
Ok(())
245
}
246
247
/// Immediately despawns a scene instance, removing all its entities from the world.
248
pub fn despawn_instance_sync(&mut self, world: &mut World, instance_id: &InstanceId) {
249
if let Some(mut instance) = self.spawned_instances.remove(instance_id) {
250
Self::despawn_instance_internal(world, &mut instance);
251
}
252
}
253
254
fn despawn_instance_internal(world: &mut World, instance: &mut InstanceInfo) {
255
for &entity in instance.entity_map.values() {
256
if let Ok(entity_mut) = world.get_entity_mut(entity) {
257
entity_mut.despawn();
258
};
259
}
260
// Just make sure if we reuse `InstanceInfo` for something, we don't reuse the despawned entities.
261
instance.entity_map.clear();
262
}
263
264
/// Immediately spawns a new instance of the provided dynamic scene.
265
pub fn spawn_dynamic_sync(
266
&mut self,
267
world: &mut World,
268
id: impl Into<AssetId<DynamicScene>>,
269
) -> Result<InstanceId, SceneSpawnError> {
270
let mut entity_map = EntityHashMap::default();
271
let id = id.into();
272
Self::spawn_dynamic_internal(world, id, &mut entity_map)?;
273
let instance_id = InstanceId::new();
274
self.spawned_instances.insert(
275
instance_id,
276
InstanceInfo {
277
entity_map,
278
parent: None,
279
},
280
);
281
let spawned = self.spawned_dynamic_scenes.entry(id).or_default();
282
spawned.insert(instance_id);
283
// We trigger `SceneInstanceReady` events after processing all scenes
284
// SceneSpawner may not be available in the observer.
285
self.instances_ready.push((instance_id, None));
286
Ok(instance_id)
287
}
288
289
fn spawn_dynamic_internal(
290
world: &mut World,
291
id: AssetId<DynamicScene>,
292
entity_map: &mut EntityHashMap<Entity>,
293
) -> Result<(), SceneSpawnError> {
294
world.resource_scope(|world, scenes: Mut<Assets<DynamicScene>>| {
295
let scene = scenes
296
.get(id)
297
.ok_or(SceneSpawnError::NonExistentScene { id })?;
298
299
scene.write_to_world(world, entity_map)
300
})
301
}
302
303
/// Immediately spawns a new instance of the provided scene.
304
pub fn spawn_sync(
305
&mut self,
306
world: &mut World,
307
id: impl Into<AssetId<Scene>>,
308
) -> Result<InstanceId, SceneSpawnError> {
309
let mut entity_map = EntityHashMap::default();
310
let id = id.into();
311
Self::spawn_sync_internal(world, id, &mut entity_map)?;
312
let instance_id = InstanceId::new();
313
self.spawned_instances.insert(
314
instance_id,
315
InstanceInfo {
316
entity_map,
317
parent: None,
318
},
319
);
320
let spawned = self.spawned_scenes.entry(id).or_default();
321
spawned.insert(instance_id);
322
// We trigger `SceneInstanceReady` events after processing all scenes
323
// SceneSpawner may not be available in the observer.
324
self.instances_ready.push((instance_id, None));
325
Ok(instance_id)
326
}
327
328
fn spawn_sync_internal(
329
world: &mut World,
330
id: AssetId<Scene>,
331
entity_map: &mut EntityHashMap<Entity>,
332
) -> Result<(), SceneSpawnError> {
333
world.resource_scope(|world, scenes: Mut<Assets<Scene>>| {
334
let scene = scenes
335
.get(id)
336
.ok_or(SceneSpawnError::NonExistentRealScene { id })?;
337
338
scene.write_to_world_with(
339
world,
340
entity_map,
341
&world.resource::<AppTypeRegistry>().clone(),
342
)
343
})
344
}
345
346
/// Iterate through all instances of the provided scenes and update those immediately.
347
///
348
/// Useful for updating already spawned scene instances after their corresponding scene has been
349
/// modified.
350
pub fn update_spawned_scenes(
351
&mut self,
352
world: &mut World,
353
scene_ids: &[AssetId<Scene>],
354
) -> Result<(), SceneSpawnError> {
355
for id in scene_ids {
356
if let Some(spawned_instances) = self.spawned_scenes.get(id) {
357
for instance_id in spawned_instances {
358
if let Some(instance_info) = self.spawned_instances.get_mut(instance_id) {
359
// Despawn the scene before respawning it. This is a very heavy operation,
360
// but otherwise, entities may be left behind, or be left in an otherwise
361
// invalid state (e.g., invalid relationships).
362
Self::despawn_instance_internal(world, instance_info);
363
Self::spawn_sync_internal(world, *id, &mut instance_info.entity_map)?;
364
Self::set_scene_instance_parent_sync(world, instance_info);
365
// We trigger `SceneInstanceReady` events after processing all scenes
366
// SceneSpawner may not be available in the observer.
367
self.instances_ready
368
.push((*instance_id, instance_info.parent));
369
}
370
}
371
}
372
}
373
Ok(())
374
}
375
376
/// Iterate through all instances of the provided dynamic scenes and update those immediately.
377
///
378
/// Useful for updating already spawned scene instances after their corresponding dynamic scene
379
/// has been modified.
380
pub fn update_spawned_dynamic_scenes(
381
&mut self,
382
world: &mut World,
383
scene_ids: &[AssetId<DynamicScene>],
384
) -> Result<(), SceneSpawnError> {
385
for id in scene_ids {
386
if let Some(spawned_instances) = self.spawned_dynamic_scenes.get(id) {
387
for instance_id in spawned_instances {
388
if let Some(instance_info) = self.spawned_instances.get_mut(instance_id) {
389
// Despawn the scene before respawning it. This is a very heavy operation,
390
// but otherwise, entities may be left behind, or be left in an otherwise
391
// invalid state (e.g., invalid relationships).
392
Self::despawn_instance_internal(world, instance_info);
393
Self::spawn_dynamic_internal(world, *id, &mut instance_info.entity_map)?;
394
Self::set_scene_instance_parent_sync(world, instance_info);
395
// We trigger `SceneInstanceReady` events after processing all scenes
396
// SceneSpawner may not be available in the observer.
397
self.instances_ready
398
.push((*instance_id, instance_info.parent));
399
}
400
}
401
}
402
}
403
Ok(())
404
}
405
406
/// Immediately despawns all scenes scheduled for despawn by despawning their instances.
407
pub fn despawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> {
408
let scenes_to_despawn = core::mem::take(&mut self.scenes_to_despawn);
409
for scene_handle in scenes_to_despawn {
410
self.despawn_sync(world, scene_handle)?;
411
}
412
let scenes_to_despawn = core::mem::take(&mut self.dynamic_scenes_to_despawn);
413
for scene_handle in scenes_to_despawn {
414
self.despawn_dynamic_sync(world, scene_handle)?;
415
}
416
Ok(())
417
}
418
419
/// Immediately despawns all scene instances scheduled for despawn.
420
pub fn despawn_queued_instances(&mut self, world: &mut World) {
421
let instances_to_despawn = core::mem::take(&mut self.instances_to_despawn);
422
423
for instance_id in instances_to_despawn {
424
self.despawn_instance_sync(world, &instance_id);
425
}
426
}
427
428
/// Immediately spawns all scenes scheduled for spawn.
429
pub fn spawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> {
430
let scenes_to_spawn = core::mem::take(&mut self.dynamic_scenes_to_spawn);
431
432
for (handle, instance_id, parent) in scenes_to_spawn {
433
let mut entity_map = EntityHashMap::default();
434
435
match Self::spawn_dynamic_internal(world, handle.id(), &mut entity_map) {
436
Ok(_) => {
437
let instance_info = InstanceInfo { entity_map, parent };
438
Self::set_scene_instance_parent_sync(world, &instance_info);
439
440
self.spawned_instances.insert(instance_id, instance_info);
441
let spawned = self.spawned_dynamic_scenes.entry(handle.id()).or_default();
442
spawned.insert(instance_id);
443
// We trigger `SceneInstanceReady` events after processing all scenes
444
// SceneSpawner may not be available in the observer.
445
self.instances_ready.push((instance_id, parent));
446
}
447
Err(SceneSpawnError::NonExistentScene { .. }) => {
448
self.dynamic_scenes_to_spawn
449
.push((handle, instance_id, parent));
450
}
451
Err(err) => return Err(err),
452
}
453
}
454
455
let scenes_to_spawn = core::mem::take(&mut self.scenes_to_spawn);
456
457
for (scene_handle, instance_id, parent) in scenes_to_spawn {
458
let mut entity_map = EntityHashMap::default();
459
460
match Self::spawn_sync_internal(world, scene_handle.id(), &mut entity_map) {
461
Ok(_) => {
462
let instance_info = InstanceInfo { entity_map, parent };
463
Self::set_scene_instance_parent_sync(world, &instance_info);
464
465
self.spawned_instances.insert(instance_id, instance_info);
466
let spawned = self.spawned_scenes.entry(scene_handle.id()).or_default();
467
spawned.insert(instance_id);
468
469
// We trigger `SceneInstanceReady` events after processing all scenes
470
// SceneSpawner may not be available in the observer.
471
self.instances_ready.push((instance_id, parent));
472
}
473
Err(SceneSpawnError::NonExistentRealScene { .. }) => {
474
self.scenes_to_spawn
475
.push((scene_handle, instance_id, parent));
476
}
477
Err(err) => return Err(err),
478
}
479
}
480
481
Ok(())
482
}
483
484
fn set_scene_instance_parent_sync(world: &mut World, instance: &InstanceInfo) {
485
let Some(parent) = instance.parent else {
486
return;
487
};
488
for &entity in instance.entity_map.values() {
489
// Add the `ChildOf` component to the scene root, and update the `Children` component of
490
// the scene parent
491
if !world
492
.get_entity(entity)
493
.ok()
494
// This will filter only the scene root entity, as all other from the
495
// scene have a parent
496
// Entities that wouldn't exist anymore are also skipped
497
// this case shouldn't happen anyway
498
.is_none_or(|entity| entity.contains::<ChildOf>())
499
{
500
world.entity_mut(parent).add_child(entity);
501
}
502
}
503
}
504
505
fn trigger_scene_ready_events(&mut self, world: &mut World) {
506
for (instance_id, parent) in self.instances_ready.drain(..) {
507
if let Some(parent) = parent {
508
// Defer via commands otherwise SceneSpawner is not available in the observer.
509
world
510
.commands()
511
.trigger_targets(SceneInstanceReady { instance_id }, parent);
512
} else {
513
// Defer via commands otherwise SceneSpawner is not available in the observer.
514
world.commands().trigger(SceneInstanceReady { instance_id });
515
}
516
}
517
}
518
519
/// Check that a scene instance spawned previously is ready to use
520
pub fn instance_is_ready(&self, instance_id: InstanceId) -> bool {
521
self.spawned_instances.contains_key(&instance_id)
522
}
523
524
/// Get an iterator over the entities in an instance, once it's spawned.
525
///
526
/// Before the scene is spawned, the iterator will be empty. Use [`Self::instance_is_ready`]
527
/// to check if the instance is ready.
528
pub fn iter_instance_entities(
529
&'_ self,
530
instance_id: InstanceId,
531
) -> impl Iterator<Item = Entity> + '_ {
532
self.spawned_instances
533
.get(&instance_id)
534
.map(|instance| instance.entity_map.values())
535
.into_iter()
536
.flatten()
537
.copied()
538
}
539
}
540
541
/// System that handles scheduled scene instance spawning and despawning through a [`SceneSpawner`].
542
pub fn scene_spawner_system(world: &mut World) {
543
world.resource_scope(|world, mut scene_spawner: Mut<SceneSpawner>| {
544
// remove any loading instances where parent is deleted
545
let is_parent_alive = |parent: &Option<Entity>| {
546
parent
547
.map(|parent| world.get_entity(parent).is_ok())
548
.unwrap_or(true) // If we don't have a parent, then consider the parent alive.
549
};
550
scene_spawner
551
.dynamic_scenes_to_spawn
552
.retain(|(_, _, parent)| is_parent_alive(parent));
553
scene_spawner
554
.scenes_to_spawn
555
.retain(|(_, _, parent)| is_parent_alive(parent));
556
557
let scene_asset_events = world.resource::<Events<AssetEvent<Scene>>>();
558
let dynamic_scene_asset_events = world.resource::<Events<AssetEvent<DynamicScene>>>();
559
let scene_spawner = &mut *scene_spawner;
560
561
let mut updated_spawned_scenes = Vec::new();
562
for event in scene_spawner
563
.scene_asset_event_reader
564
.read(scene_asset_events)
565
{
566
match event {
567
AssetEvent::Added { id } => {
568
scene_spawner.debounced_scene_asset_events.insert(*id, 0);
569
}
570
AssetEvent::Modified { id } => {
571
if scene_spawner
572
.debounced_scene_asset_events
573
.insert(*id, 0)
574
.is_none()
575
&& scene_spawner.spawned_scenes.contains_key(id)
576
{
577
updated_spawned_scenes.push(*id);
578
}
579
}
580
_ => {}
581
}
582
}
583
let mut updated_spawned_dynamic_scenes = Vec::new();
584
for event in scene_spawner
585
.dynamic_scene_asset_event_reader
586
.read(dynamic_scene_asset_events)
587
{
588
match event {
589
AssetEvent::Added { id } => {
590
scene_spawner
591
.debounced_dynamic_scene_asset_events
592
.insert(*id, 0);
593
}
594
AssetEvent::Modified { id } => {
595
if scene_spawner
596
.debounced_dynamic_scene_asset_events
597
.insert(*id, 0)
598
.is_none()
599
&& scene_spawner.spawned_dynamic_scenes.contains_key(id)
600
{
601
updated_spawned_dynamic_scenes.push(*id);
602
}
603
}
604
_ => {}
605
}
606
}
607
608
scene_spawner.despawn_queued_scenes(world).unwrap();
609
scene_spawner.despawn_queued_instances(world);
610
scene_spawner
611
.spawn_queued_scenes(world)
612
.unwrap_or_else(|err| panic!("{}", err));
613
scene_spawner
614
.update_spawned_scenes(world, &updated_spawned_scenes)
615
.unwrap();
616
scene_spawner
617
.update_spawned_dynamic_scenes(world, &updated_spawned_dynamic_scenes)
618
.unwrap();
619
scene_spawner.trigger_scene_ready_events(world);
620
621
const SCENE_ASSET_AGE_THRESHOLD: u32 = 2;
622
for asset_id in scene_spawner.debounced_scene_asset_events.clone().keys() {
623
let age = scene_spawner
624
.debounced_scene_asset_events
625
.get(asset_id)
626
.unwrap();
627
if *age > SCENE_ASSET_AGE_THRESHOLD {
628
scene_spawner.debounced_scene_asset_events.remove(asset_id);
629
} else {
630
scene_spawner
631
.debounced_scene_asset_events
632
.insert(*asset_id, *age + 1);
633
}
634
}
635
for asset_id in scene_spawner
636
.debounced_dynamic_scene_asset_events
637
.clone()
638
.keys()
639
{
640
let age = scene_spawner
641
.debounced_dynamic_scene_asset_events
642
.get(asset_id)
643
.unwrap();
644
if *age > SCENE_ASSET_AGE_THRESHOLD {
645
scene_spawner
646
.debounced_dynamic_scene_asset_events
647
.remove(asset_id);
648
} else {
649
scene_spawner
650
.debounced_dynamic_scene_asset_events
651
.insert(*asset_id, *age + 1);
652
}
653
}
654
});
655
}
656
657
/// [`InstanceId`] of a spawned scene. It can be used with the [`SceneSpawner`] to
658
/// interact with the spawned scene.
659
#[derive(Component, Deref, DerefMut)]
660
pub struct SceneInstance(pub(crate) InstanceId);
661
662
/// System that will spawn scenes from the [`SceneRoot`] and [`DynamicSceneRoot`] components.
663
pub fn scene_spawner(
664
mut commands: Commands,
665
mut scene_to_spawn: Query<
666
(Entity, &SceneRoot, Option<&mut SceneInstance>),
667
(Changed<SceneRoot>, Without<DynamicSceneRoot>),
668
>,
669
mut dynamic_scene_to_spawn: Query<
670
(Entity, &DynamicSceneRoot, Option<&mut SceneInstance>),
671
(Changed<DynamicSceneRoot>, Without<SceneRoot>),
672
>,
673
mut scene_spawner: ResMut<SceneSpawner>,
674
) {
675
for (entity, scene, instance) in &mut scene_to_spawn {
676
let new_instance = scene_spawner.spawn_as_child(scene.0.clone(), entity);
677
if let Some(mut old_instance) = instance {
678
scene_spawner.despawn_instance(**old_instance);
679
*old_instance = SceneInstance(new_instance);
680
} else {
681
commands.entity(entity).insert(SceneInstance(new_instance));
682
}
683
}
684
for (entity, dynamic_scene, instance) in &mut dynamic_scene_to_spawn {
685
let new_instance = scene_spawner.spawn_dynamic_as_child(dynamic_scene.0.clone(), entity);
686
if let Some(mut old_instance) = instance {
687
scene_spawner.despawn_instance(**old_instance);
688
*old_instance = SceneInstance(new_instance);
689
} else {
690
commands.entity(entity).insert(SceneInstance(new_instance));
691
}
692
}
693
}
694
695
#[cfg(test)]
696
mod tests {
697
use bevy_app::App;
698
use bevy_asset::{AssetPlugin, AssetServer, Handle};
699
use bevy_ecs::{
700
component::Component,
701
hierarchy::Children,
702
observer::On,
703
prelude::ReflectComponent,
704
query::With,
705
system::{Commands, Query, Res, ResMut, RunSystemOnce},
706
};
707
use bevy_reflect::Reflect;
708
709
use crate::{DynamicSceneBuilder, DynamicSceneRoot, ScenePlugin};
710
711
use super::*;
712
use crate::{DynamicScene, SceneSpawner};
713
use bevy_app::ScheduleRunnerPlugin;
714
use bevy_asset::Assets;
715
use bevy_ecs::{
716
entity::Entity,
717
prelude::{AppTypeRegistry, World},
718
};
719
720
#[derive(Component, Reflect, Default)]
721
#[reflect(Component)]
722
struct ComponentA {
723
pub x: f32,
724
pub y: f32,
725
}
726
727
#[test]
728
fn spawn_and_delete() {
729
let mut app = App::new();
730
731
app.add_plugins(ScheduleRunnerPlugin::default())
732
.add_plugins(AssetPlugin::default())
733
.add_plugins(ScenePlugin);
734
app.update();
735
736
let mut scene_world = World::new();
737
738
// create a new DynamicScene manually
739
let type_registry = app.world().resource::<AppTypeRegistry>().clone();
740
scene_world.insert_resource(type_registry);
741
scene_world.spawn(ComponentA { x: 3.0, y: 4.0 });
742
let scene = DynamicScene::from_world(&scene_world);
743
let scene_handle = app
744
.world_mut()
745
.resource_mut::<Assets<DynamicScene>>()
746
.add(scene);
747
748
// spawn the scene as a child of `entity` using `DynamicSceneRoot`
749
let entity = app
750
.world_mut()
751
.spawn(DynamicSceneRoot(scene_handle.clone()))
752
.id();
753
754
// run the app's schedule once, so that the scene gets spawned
755
app.update();
756
757
// make sure that the scene was added as a child of the root entity
758
let (scene_entity, scene_component_a) = app
759
.world_mut()
760
.query::<(Entity, &ComponentA)>()
761
.single(app.world())
762
.unwrap();
763
assert_eq!(scene_component_a.x, 3.0);
764
assert_eq!(scene_component_a.y, 4.0);
765
assert_eq!(
766
app.world().entity(entity).get::<Children>().unwrap().len(),
767
1
768
);
769
770
// let's try to delete the scene
771
let mut scene_spawner = app.world_mut().resource_mut::<SceneSpawner>();
772
scene_spawner.despawn_dynamic(&scene_handle);
773
774
// run the scene spawner system to despawn the scene
775
app.update();
776
777
// the scene entity does not exist anymore
778
assert!(app.world().get_entity(scene_entity).is_err());
779
780
// the root entity does not have any children anymore
781
assert!(app.world().entity(entity).get::<Children>().is_none());
782
}
783
784
#[derive(Reflect, Component, Debug, PartialEq, Eq, Clone, Copy, Default)]
785
#[reflect(Component)]
786
struct A(usize);
787
788
#[test]
789
fn clone_dynamic_entities() {
790
let mut world = World::default();
791
792
// setup
793
let atr = AppTypeRegistry::default();
794
atr.write().register::<A>();
795
world.insert_resource(atr);
796
world.insert_resource(Assets::<DynamicScene>::default());
797
798
// start test
799
world.spawn(A(42));
800
801
assert_eq!(world.query::<&A>().iter(&world).len(), 1);
802
803
// clone only existing entity
804
let mut scene_spawner = SceneSpawner::default();
805
let entity = world
806
.query_filtered::<Entity, With<A>>()
807
.single(&world)
808
.unwrap();
809
let scene = DynamicSceneBuilder::from_world(&world)
810
.extract_entity(entity)
811
.build();
812
813
let scene_id = world.resource_mut::<Assets<DynamicScene>>().add(scene);
814
let instance_id = scene_spawner
815
.spawn_dynamic_sync(&mut world, &scene_id)
816
.unwrap();
817
818
// verify we spawned exactly one new entity with our expected component
819
assert_eq!(world.query::<&A>().iter(&world).len(), 2);
820
821
// verify that we can get this newly-spawned entity by the instance ID
822
let new_entity = scene_spawner
823
.iter_instance_entities(instance_id)
824
.next()
825
.unwrap();
826
827
// verify this is not the original entity
828
assert_ne!(entity, new_entity);
829
830
// verify this new entity contains the same data as the original entity
831
let [old_a, new_a] = world
832
.query::<&A>()
833
.get_many(&world, [entity, new_entity])
834
.unwrap();
835
assert_eq!(old_a, new_a);
836
}
837
838
#[derive(Component, Reflect, Default)]
839
#[reflect(Component)]
840
struct ComponentF;
841
842
#[derive(Resource, Default)]
843
struct TriggerCount(u32);
844
845
fn setup() -> App {
846
let mut app = App::new();
847
app.add_plugins((AssetPlugin::default(), ScenePlugin));
848
app.init_resource::<TriggerCount>();
849
850
app.register_type::<ComponentF>();
851
app.world_mut().spawn(ComponentF);
852
app.world_mut().spawn(ComponentF);
853
854
app
855
}
856
857
fn build_scene(app: &mut App) -> Handle<Scene> {
858
app.world_mut()
859
.run_system_once(
860
|world: &World,
861
type_registry: Res<'_, AppTypeRegistry>,
862
asset_server: Res<'_, AssetServer>| {
863
asset_server.add(
864
Scene::from_dynamic_scene(&DynamicScene::from_world(world), &type_registry)
865
.unwrap(),
866
)
867
},
868
)
869
.expect("Failed to run scene builder system.")
870
}
871
872
fn build_dynamic_scene(app: &mut App) -> Handle<DynamicScene> {
873
app.world_mut()
874
.run_system_once(|world: &World, asset_server: Res<'_, AssetServer>| {
875
asset_server.add(DynamicScene::from_world(world))
876
})
877
.expect("Failed to run dynamic scene builder system.")
878
}
879
880
fn observe_trigger(app: &mut App, scene_id: InstanceId, scene_entity: Option<Entity>) {
881
// Add observer
882
app.world_mut().add_observer(
883
move |event: On<SceneInstanceReady>,
884
scene_spawner: Res<SceneSpawner>,
885
mut trigger_count: ResMut<TriggerCount>| {
886
assert_eq!(
887
event.event().instance_id,
888
scene_id,
889
"`SceneInstanceReady` contains the wrong `InstanceId`"
890
);
891
assert_eq!(
892
event.entity(),
893
scene_entity.unwrap_or(Entity::PLACEHOLDER),
894
"`SceneInstanceReady` triggered on the wrong parent entity"
895
);
896
assert!(
897
scene_spawner.instance_is_ready(event.event().instance_id),
898
"`InstanceId` is not ready"
899
);
900
trigger_count.0 += 1;
901
},
902
);
903
904
// Check observer is triggered once.
905
app.update();
906
app.world_mut()
907
.run_system_once(|trigger_count: Res<TriggerCount>| {
908
assert_eq!(
909
trigger_count.0, 1,
910
"wrong number of `SceneInstanceReady` triggers"
911
);
912
})
913
.unwrap();
914
}
915
916
#[test]
917
fn observe_scene() {
918
let mut app = setup();
919
920
// Build scene.
921
let scene = build_scene(&mut app);
922
923
// Spawn scene.
924
let scene_id = app
925
.world_mut()
926
.run_system_once(move |mut scene_spawner: ResMut<'_, SceneSpawner>| {
927
scene_spawner.spawn(scene.clone())
928
})
929
.unwrap();
930
931
// Check trigger.
932
observe_trigger(&mut app, scene_id, None);
933
}
934
935
#[test]
936
fn observe_dynamic_scene() {
937
let mut app = setup();
938
939
// Build scene.
940
let scene = build_dynamic_scene(&mut app);
941
942
// Spawn scene.
943
let scene_id = app
944
.world_mut()
945
.run_system_once(move |mut scene_spawner: ResMut<'_, SceneSpawner>| {
946
scene_spawner.spawn_dynamic(scene.clone())
947
})
948
.unwrap();
949
950
// Check trigger.
951
observe_trigger(&mut app, scene_id, None);
952
}
953
954
#[test]
955
fn observe_scene_as_child() {
956
let mut app = setup();
957
958
// Build scene.
959
let scene = build_scene(&mut app);
960
961
// Spawn scene as child.
962
let (scene_id, scene_entity) = app
963
.world_mut()
964
.run_system_once(
965
move |mut commands: Commands<'_, '_>,
966
mut scene_spawner: ResMut<'_, SceneSpawner>| {
967
let entity = commands.spawn_empty().id();
968
let id = scene_spawner.spawn_as_child(scene.clone(), entity);
969
(id, entity)
970
},
971
)
972
.unwrap();
973
974
// Check trigger.
975
observe_trigger(&mut app, scene_id, Some(scene_entity));
976
}
977
978
#[test]
979
fn observe_dynamic_scene_as_child() {
980
let mut app = setup();
981
982
// Build scene.
983
let scene = build_dynamic_scene(&mut app);
984
985
// Spawn scene as child.
986
let (scene_id, scene_entity) = app
987
.world_mut()
988
.run_system_once(
989
move |mut commands: Commands<'_, '_>,
990
mut scene_spawner: ResMut<'_, SceneSpawner>| {
991
let entity = commands.spawn_empty().id();
992
let id = scene_spawner.spawn_dynamic_as_child(scene.clone(), entity);
993
(id, entity)
994
},
995
)
996
.unwrap();
997
998
// Check trigger.
999
observe_trigger(&mut app, scene_id, Some(scene_entity));
1000
}
1001
1002
#[test]
1003
fn despawn_scene() {
1004
let mut app = App::new();
1005
app.add_plugins((AssetPlugin::default(), ScenePlugin));
1006
app.register_type::<ComponentF>();
1007
1008
let asset_server = app.world().resource::<AssetServer>();
1009
1010
// Build scene.
1011
let scene = asset_server.add(DynamicScene::default());
1012
let count = 10;
1013
1014
// Checks the number of scene instances stored in `SceneSpawner`.
1015
let check = |world: &mut World, expected_count: usize| {
1016
let scene_spawner = world.resource::<SceneSpawner>();
1017
assert_eq!(
1018
scene_spawner.spawned_dynamic_scenes[&scene.id()].len(),
1019
expected_count
1020
);
1021
assert_eq!(scene_spawner.spawned_instances.len(), expected_count);
1022
};
1023
1024
// Spawn scene.
1025
for _ in 0..count {
1026
app.world_mut()
1027
.spawn((ComponentF, DynamicSceneRoot(scene.clone())));
1028
}
1029
1030
app.update();
1031
check(app.world_mut(), count);
1032
1033
// Despawn scene.
1034
app.world_mut()
1035
.run_system_once(
1036
|mut commands: Commands, query: Query<Entity, With<ComponentF>>| {
1037
for entity in query.iter() {
1038
commands.entity(entity).despawn();
1039
}
1040
},
1041
)
1042
.unwrap();
1043
1044
app.update();
1045
check(app.world_mut(), 0);
1046
}
1047
1048
#[test]
1049
fn scene_child_order_preserved_when_archetype_order_mismatched() {
1050
let mut app = App::new();
1051
1052
app.add_plugins(ScheduleRunnerPlugin::default())
1053
.add_plugins(AssetPlugin::default())
1054
.add_plugins(ScenePlugin)
1055
.register_type::<ComponentA>()
1056
.register_type::<ComponentF>();
1057
app.update();
1058
1059
let mut scene_world = World::new();
1060
let root = scene_world.spawn_empty().id();
1061
let temporary_root = scene_world.spawn_empty().id();
1062
// Spawn entities with different parent first before parenting them to the actual root, allowing us
1063
// to decouple child order from archetype-creation-order
1064
let child1 = scene_world
1065
.spawn((ChildOf(temporary_root), ComponentA { x: 1.0, y: 1.0 }))
1066
.id();
1067
let child2 = scene_world
1068
.spawn((ChildOf(temporary_root), ComponentA { x: 2.0, y: 2.0 }))
1069
.id();
1070
// the "first" child is intentionally spawned with a different component to force it into a "newer" archetype,
1071
// meaning it will be iterated later in the spawn code.
1072
let child0 = scene_world
1073
.spawn((ChildOf(temporary_root), ComponentF))
1074
.id();
1075
1076
scene_world
1077
.entity_mut(root)
1078
.add_children(&[child0, child1, child2]);
1079
1080
let scene = Scene::new(scene_world);
1081
let scene_handle = app.world_mut().resource_mut::<Assets<Scene>>().add(scene);
1082
1083
let spawned = app.world_mut().spawn(SceneRoot(scene_handle.clone())).id();
1084
1085
app.update();
1086
let world = app.world_mut();
1087
1088
let spawned_root = world.entity(spawned).get::<Children>().unwrap()[1];
1089
let children = world.entity(spawned_root).get::<Children>().unwrap();
1090
assert_eq!(children.len(), 3);
1091
assert!(world.entity(children[0]).get::<ComponentF>().is_some());
1092
assert_eq!(
1093
world.entity(children[1]).get::<ComponentA>().unwrap().x,
1094
1.0
1095
);
1096
assert_eq!(
1097
world.entity(children[2]).get::<ComponentA>().unwrap().x,
1098
2.0
1099
);
1100
}
1101
}
1102
1103