Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_scene/src/scene_spawner.rs
9358 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,
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.update();
752
753
let mut scene_world = World::new();
754
755
// create a new DynamicScene manually
756
let type_registry = app.world().resource::<AppTypeRegistry>().clone();
757
scene_world.insert_resource(type_registry);
758
scene_world.spawn(ComponentA { x: 3.0, y: 4.0 });
759
let scene = DynamicScene::from_world(&scene_world);
760
let scene_handle = app
761
.world_mut()
762
.resource_mut::<Assets<DynamicScene>>()
763
.add(scene);
764
765
// spawn the scene as a child of `entity` using `DynamicSceneRoot`
766
let entity = app
767
.world_mut()
768
.spawn(DynamicSceneRoot(scene_handle.clone()))
769
.id();
770
771
// run the app's schedule once, so that the scene gets spawned
772
app.update();
773
774
// make sure that the scene was added as a child of the root entity
775
let (scene_entity, scene_component_a) = app
776
.world_mut()
777
.query::<(Entity, &ComponentA)>()
778
.single(app.world())
779
.unwrap();
780
assert_eq!(scene_component_a.x, 3.0);
781
assert_eq!(scene_component_a.y, 4.0);
782
assert_eq!(
783
app.world().entity(entity).get::<Children>().unwrap().len(),
784
1
785
);
786
787
// let's try to delete the scene
788
let mut scene_spawner = app.world_mut().resource_mut::<SceneSpawner>();
789
scene_spawner.despawn_dynamic(&scene_handle);
790
791
// run the scene spawner system to despawn the scene
792
app.update();
793
794
// the scene entity does not exist anymore
795
assert!(app.world().get_entity(scene_entity).is_err());
796
797
// the root entity does not have any children anymore
798
assert!(app.world().entity(entity).get::<Children>().is_none());
799
}
800
801
#[derive(Reflect, Component, Debug, PartialEq, Eq, Clone, Copy, Default)]
802
#[reflect(Component)]
803
struct A(usize);
804
805
#[test]
806
fn clone_dynamic_entities() {
807
let mut world = World::default();
808
809
// setup
810
let atr = AppTypeRegistry::default();
811
atr.write().register::<A>();
812
world.insert_resource(atr);
813
world.insert_resource(Assets::<DynamicScene>::default());
814
815
// start test
816
world.spawn(A(42));
817
818
assert_eq!(world.query::<&A>().iter(&world).len(), 1);
819
820
// clone only existing entity
821
let mut scene_spawner = SceneSpawner::default();
822
let entity = world
823
.query_filtered::<Entity, With<A>>()
824
.single(&world)
825
.unwrap();
826
let scene = DynamicSceneBuilder::from_world(&world)
827
.extract_entity(entity)
828
.build();
829
830
let scene_id = world.resource_mut::<Assets<DynamicScene>>().add(scene);
831
let instance_id = scene_spawner
832
.spawn_dynamic_sync(&mut world, &scene_id)
833
.unwrap();
834
835
// verify we spawned exactly one new entity with our expected component
836
assert_eq!(world.query::<&A>().iter(&world).len(), 2);
837
838
// verify that we can get this newly-spawned entity by the instance ID
839
let new_entity = scene_spawner
840
.iter_instance_entities(instance_id)
841
.next()
842
.unwrap();
843
844
// verify this is not the original entity
845
assert_ne!(entity, new_entity);
846
847
// verify this new entity contains the same data as the original entity
848
let [old_a, new_a] = world
849
.query::<&A>()
850
.get_many(&world, [entity, new_entity])
851
.unwrap();
852
assert_eq!(old_a, new_a);
853
}
854
855
#[derive(Component, Reflect, Default)]
856
#[reflect(Component)]
857
struct ComponentF;
858
859
#[derive(Resource, Default)]
860
struct TriggerCount(u32);
861
862
fn setup() -> App {
863
let mut app = App::new();
864
app.add_plugins((AssetPlugin::default(), ScenePlugin));
865
app.init_resource::<TriggerCount>();
866
867
app.register_type::<ComponentF>();
868
app.world_mut().spawn(ComponentF);
869
app.world_mut().spawn(ComponentF);
870
871
app
872
}
873
874
fn build_scene(app: &mut App) -> Handle<Scene> {
875
app.world_mut()
876
.run_system_once(
877
|world: &World,
878
type_registry: Res<'_, AppTypeRegistry>,
879
asset_server: Res<'_, AssetServer>| {
880
asset_server.add(
881
Scene::from_dynamic_scene(&DynamicScene::from_world(world), &type_registry)
882
.unwrap(),
883
)
884
},
885
)
886
.expect("Failed to run scene builder system.")
887
}
888
889
fn build_dynamic_scene(app: &mut App) -> Handle<DynamicScene> {
890
app.world_mut()
891
.run_system_once(|world: &World, asset_server: Res<'_, AssetServer>| {
892
asset_server.add(DynamicScene::from_world(world))
893
})
894
.expect("Failed to run dynamic scene builder system.")
895
}
896
897
fn observe_trigger(app: &mut App, scene_id: InstanceId, scene_entity: Option<Entity>) {
898
// Add observer
899
app.world_mut().add_observer(
900
move |event: On<SceneInstanceReady>,
901
scene_spawner: Res<SceneSpawner>,
902
mut trigger_count: ResMut<TriggerCount>| {
903
assert_eq!(
904
event.event().instance_id,
905
scene_id,
906
"`SceneInstanceReady` contains the wrong `InstanceId`"
907
);
908
assert_eq!(
909
event.event_target(),
910
scene_entity.unwrap_or(Entity::PLACEHOLDER),
911
"`SceneInstanceReady` triggered on the wrong parent entity"
912
);
913
assert!(
914
scene_spawner.instance_is_ready(event.event().instance_id),
915
"`InstanceId` is not ready"
916
);
917
trigger_count.0 += 1;
918
},
919
);
920
921
// Check observer is triggered once.
922
app.update();
923
app.world_mut()
924
.run_system_once(|trigger_count: Res<TriggerCount>| {
925
assert_eq!(
926
trigger_count.0, 1,
927
"wrong number of `SceneInstanceReady` triggers"
928
);
929
})
930
.unwrap();
931
}
932
933
#[test]
934
fn observe_scene() {
935
let mut app = setup();
936
937
// Build scene.
938
let scene = build_scene(&mut app);
939
940
// Spawn scene.
941
let scene_id = app
942
.world_mut()
943
.run_system_once(move |mut scene_spawner: ResMut<'_, SceneSpawner>| {
944
scene_spawner.spawn(scene.clone())
945
})
946
.unwrap();
947
948
// Check trigger.
949
observe_trigger(&mut app, scene_id, None);
950
}
951
952
#[test]
953
fn observe_dynamic_scene() {
954
let mut app = setup();
955
956
// Build scene.
957
let scene = build_dynamic_scene(&mut app);
958
959
// Spawn scene.
960
let scene_id = app
961
.world_mut()
962
.run_system_once(move |mut scene_spawner: ResMut<'_, SceneSpawner>| {
963
scene_spawner.spawn_dynamic(scene.clone())
964
})
965
.unwrap();
966
967
// Check trigger.
968
observe_trigger(&mut app, scene_id, None);
969
}
970
971
#[test]
972
fn observe_scene_as_child() {
973
let mut app = setup();
974
975
// Build scene.
976
let scene = build_scene(&mut app);
977
978
// Spawn scene as child.
979
let (scene_id, scene_entity) = app
980
.world_mut()
981
.run_system_once(
982
move |mut commands: Commands<'_, '_>,
983
mut scene_spawner: ResMut<'_, SceneSpawner>| {
984
let entity = commands.spawn_empty().id();
985
let id = scene_spawner.spawn_as_child(scene.clone(), entity);
986
(id, entity)
987
},
988
)
989
.unwrap();
990
991
// Check trigger.
992
observe_trigger(&mut app, scene_id, Some(scene_entity));
993
}
994
995
#[test]
996
fn observe_dynamic_scene_as_child() {
997
let mut app = setup();
998
999
// Build scene.
1000
let scene = build_dynamic_scene(&mut app);
1001
1002
// Spawn scene as child.
1003
let (scene_id, scene_entity) = app
1004
.world_mut()
1005
.run_system_once(
1006
move |mut commands: Commands<'_, '_>,
1007
mut scene_spawner: ResMut<'_, SceneSpawner>| {
1008
let entity = commands.spawn_empty().id();
1009
let id = scene_spawner.spawn_dynamic_as_child(scene.clone(), entity);
1010
(id, entity)
1011
},
1012
)
1013
.unwrap();
1014
1015
// Check trigger.
1016
observe_trigger(&mut app, scene_id, Some(scene_entity));
1017
}
1018
1019
#[test]
1020
fn despawn_scene() {
1021
let mut app = App::new();
1022
app.add_plugins((AssetPlugin::default(), ScenePlugin));
1023
app.register_type::<ComponentF>();
1024
1025
let asset_server = app.world().resource::<AssetServer>();
1026
1027
// Build scene.
1028
let scene = asset_server.add(DynamicScene::default());
1029
let count = 10;
1030
1031
// Checks the number of scene instances stored in `SceneSpawner`.
1032
let check = |world: &mut World, expected_count: usize| {
1033
let scene_spawner = world.resource::<SceneSpawner>();
1034
assert_eq!(
1035
scene_spawner.spawned_dynamic_scenes[&scene.id()].len(),
1036
expected_count
1037
);
1038
assert_eq!(scene_spawner.spawned_instances.len(), expected_count);
1039
};
1040
1041
// Spawn scene.
1042
for _ in 0..count {
1043
app.world_mut()
1044
.spawn((ComponentF, DynamicSceneRoot(scene.clone())));
1045
}
1046
1047
app.update();
1048
check(app.world_mut(), count);
1049
1050
// Despawn scene.
1051
app.world_mut()
1052
.run_system_once(
1053
|mut commands: Commands, query: Query<Entity, With<ComponentF>>| {
1054
for entity in query.iter() {
1055
commands.entity(entity).despawn();
1056
}
1057
},
1058
)
1059
.unwrap();
1060
1061
app.update();
1062
check(app.world_mut(), 0);
1063
}
1064
1065
#[test]
1066
fn scene_child_order_preserved_when_archetype_order_mismatched() {
1067
let mut app = App::new();
1068
1069
app.add_plugins(ScheduleRunnerPlugin::default())
1070
.add_plugins(AssetPlugin::default())
1071
.add_plugins(ScenePlugin)
1072
.register_type::<ComponentA>()
1073
.register_type::<ComponentF>();
1074
app.update();
1075
1076
let mut scene_world = World::new();
1077
let root = scene_world.spawn_empty().id();
1078
let temporary_root = scene_world.spawn_empty().id();
1079
// Spawn entities with different parent first before parenting them to the actual root, allowing us
1080
// to decouple child order from archetype-creation-order
1081
let child1 = scene_world
1082
.spawn((ChildOf(temporary_root), ComponentA { x: 1.0, y: 1.0 }))
1083
.id();
1084
let child2 = scene_world
1085
.spawn((ChildOf(temporary_root), ComponentA { x: 2.0, y: 2.0 }))
1086
.id();
1087
// the "first" child is intentionally spawned with a different component to force it into a "newer" archetype,
1088
// meaning it will be iterated later in the spawn code.
1089
let child0 = scene_world
1090
.spawn((ChildOf(temporary_root), ComponentF))
1091
.id();
1092
1093
scene_world
1094
.entity_mut(root)
1095
.add_children(&[child0, child1, child2]);
1096
1097
let scene = Scene::new(scene_world);
1098
let scene_handle = app.world_mut().resource_mut::<Assets<Scene>>().add(scene);
1099
1100
let spawned = app.world_mut().spawn(SceneRoot(scene_handle.clone())).id();
1101
1102
app.update();
1103
let world = app.world_mut();
1104
1105
let spawned_root = world.entity(spawned).get::<Children>().unwrap()[1];
1106
let children = world.entity(spawned_root).get::<Children>().unwrap();
1107
assert_eq!(children.len(), 3);
1108
assert!(world.entity(children[0]).get::<ComponentF>().is_some());
1109
assert_eq!(
1110
world.entity(children[1]).get::<ComponentA>().unwrap().x,
1111
1.0
1112
);
1113
assert_eq!(
1114
world.entity(children[2]).get::<ComponentA>().unwrap().x,
1115
2.0
1116
);
1117
}
1118
}
1119
1120