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