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