Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_scene/src/spawn.rs
30635 views
1
use crate::{
2
ResolvedSceneRoot, Scene, SceneList, SceneListPatch, ScenePatch, ScenePatchInstance,
3
SpawnSceneError,
4
};
5
use alloc::sync::Arc;
6
use bevy_asset::{AssetEvent, AssetServer, Assets, Handle};
7
use bevy_ecs::{
8
bundle::BundleScratch, message::MessageCursor, prelude::*, relationship::Relationship,
9
};
10
use bevy_platform::collections::HashMap;
11
use tracing::error;
12
13
/// Adds scene spawning functionality to [`World`].
14
pub trait WorldSceneExt {
15
/// Spawns the given [`Scene`] immediately. This will resolve the Scene (using [`Scene::resolve`]). If that fails (for example, if there are dependencies that have not been
16
/// loaded yet), it will return a [`SpawnSceneError`]. If resolving the [`Scene`] is successful, the scene will be spawned.
17
///
18
/// If resolving and spawning is successful, it will return a new [`EntityWorldMut`] containing the full contents of the spawned scene.
19
///
20
/// See [`Scene`] for the features of the scene system (and how to use it).
21
///
22
/// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`World::queue_spawn_scene`].
23
/// Note that the `.bsn` file format is not yet released.
24
///
25
/// ```
26
/// # use bevy_app::App;
27
/// # use bevy_scene::{prelude::*, ScenePlugin};
28
/// # use bevy_ecs::prelude::*;
29
/// # use bevy_asset::AssetPlugin;
30
/// # use bevy_app::TaskPoolPlugin;
31
/// # let mut app = App::new();
32
/// # app.add_plugins((
33
/// # TaskPoolPlugin::default(),
34
/// # AssetPlugin::default(),
35
/// # ScenePlugin::default(),
36
/// # ));
37
/// # let world = app.world_mut();
38
/// #[derive(Component, Default, Clone)]
39
/// struct Score(usize);
40
///
41
/// #[derive(Component, Default, Clone)]
42
/// struct Sword;
43
///
44
/// #[derive(Component, Default, Clone)]
45
/// struct Shield;
46
///
47
/// world.spawn_scene(bsn! {
48
/// #Player
49
/// Score(0)
50
/// Children [
51
/// Sword,
52
/// Shield,
53
/// ]
54
/// }).unwrap();
55
/// ```
56
fn spawn_scene<S: Scene>(&mut self, scene: S) -> Result<EntityWorldMut<'_>, SpawnSceneError>;
57
58
/// Queues the `scene` to be spawned. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned
59
/// after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.
60
///
61
/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.
62
///
63
/// See [`Scene`] for the features of the scene system (and how to use it).
64
///
65
/// ```
66
/// # use bevy_app::App;
67
/// # use bevy_scene::{prelude::*, ScenePlugin};
68
/// # use bevy_ecs::prelude::*;
69
/// # use bevy_asset::AssetPlugin;
70
/// # use bevy_app::TaskPoolPlugin;
71
/// # let mut app = App::new();
72
/// # app.add_plugins((
73
/// # TaskPoolPlugin::default(),
74
/// # AssetPlugin::default(),
75
/// # ScenePlugin::default(),
76
/// # ));
77
/// # let world = app.world_mut();
78
/// #[derive(Component, Default, Clone)]
79
/// struct Score(usize);
80
///
81
/// #[derive(Component, Default, Clone)]
82
/// struct Sword;
83
///
84
/// #[derive(Component, Default, Clone)]
85
/// struct Shield;
86
///
87
/// // This scene includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn"
88
/// // is fully loaded.
89
/// world.queue_spawn_scene(bsn! {
90
/// :"player.bsn"
91
/// #Player
92
/// Score(0)
93
/// Children [
94
/// Sword,
95
/// Shield,
96
/// ]
97
/// });
98
/// ```
99
fn queue_spawn_scene<S: Scene>(&mut self, scene: S) -> EntityWorldMut<'_>;
100
101
/// Spawns the given [`SceneList`] immediately. This will resolve the scene list (using [`SceneList::resolve_list`]). If that fails (for example, if there are dependencies that have not been
102
/// loaded yet), it will return a [`SpawnSceneError`]. If resolving the [`SceneList`] is successful, the scene list will be spawned.
103
///
104
/// If resolving and spawning is successful, it will return a [`Vec<Entity>`] containing each entity described in the [`SceneList`].
105
///
106
/// See [`Scene`] for the features of the scene system (and how to use it).
107
///
108
/// If your scene list has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`World::queue_spawn_scene_list`].
109
/// Note that the `.bsn` file format is not yet released.
110
///
111
/// ```
112
/// # use bevy_app::App;
113
/// # use bevy_scene::{prelude::*, ScenePlugin};
114
/// # use bevy_ecs::prelude::*;
115
/// # use bevy_asset::AssetPlugin;
116
/// # use bevy_app::TaskPoolPlugin;
117
/// # let mut app = App::new();
118
/// # app.add_plugins((
119
/// # TaskPoolPlugin::default(),
120
/// # AssetPlugin::default(),
121
/// # ScenePlugin::default(),
122
/// # ));
123
/// # let world = app.world_mut();
124
/// #[derive(Component, FromTemplate)]
125
/// enum Team {
126
/// #[default]
127
/// Red,
128
/// Blue,
129
/// }
130
///
131
/// world.spawn_scene_list(bsn_list! {
132
/// (
133
/// #Player1
134
/// Team::Red
135
/// ),
136
/// (
137
/// #Player2
138
/// Team::Blue
139
/// )
140
/// }).unwrap();
141
/// ```
142
// PERF: ideally this is an iterator
143
fn spawn_scene_list<L: SceneList>(&mut self, scenes: L)
144
-> Result<Vec<Entity>, SpawnSceneError>;
145
146
/// Queues the `scene_list` to be spawned. This will evaluate the `scene_list`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved
147
/// and spawned after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.
148
///
149
/// If the dependencies are already loaded (or there are no dependencies), then the scene list will be spawned this frame.
150
/// ```
151
/// # use bevy_app::App;
152
/// # use bevy_scene::{prelude::*, ScenePlugin};
153
/// # use bevy_ecs::prelude::*;
154
/// # use bevy_asset::AssetPlugin;
155
/// # use bevy_app::TaskPoolPlugin;
156
/// # let mut app = App::new();
157
/// # app.add_plugins((
158
/// # TaskPoolPlugin::default(),
159
/// # AssetPlugin::default(),
160
/// # ScenePlugin::default(),
161
/// # ));
162
/// # let world = app.world_mut();
163
/// #[derive(Component, FromTemplate)]
164
/// enum Team {
165
/// #[default]
166
/// Red,
167
/// Blue,
168
/// }
169
/// // This scene list includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn"
170
/// // is loaded.
171
/// world.queue_spawn_scene_list(bsn_list! [
172
/// (
173
/// :"player.bsn"
174
/// #Player1
175
/// Team::Red
176
/// ),
177
/// (
178
/// :"player.bsn"
179
/// #Player2
180
/// Team::Blue
181
/// )
182
/// ]);
183
/// ```
184
fn queue_spawn_scene_list<L: SceneList>(&mut self, scenes: L);
185
}
186
187
impl WorldSceneExt for World {
188
fn spawn_scene<S: Scene>(&mut self, scene: S) -> Result<EntityWorldMut<'_>, SpawnSceneError> {
189
let assets = self.resource::<AssetServer>();
190
let mut patch = ScenePatch::load(assets, scene);
191
patch.resolve(assets, self.resource::<Assets<ScenePatch>>())?;
192
patch.spawn(self)
193
}
194
195
fn queue_spawn_scene<S: Scene>(&mut self, scene: S) -> EntityWorldMut<'_> {
196
let assets = self.resource::<AssetServer>();
197
let patch = ScenePatch::load(assets, scene);
198
let handle = assets.add(patch);
199
self.spawn(ScenePatchInstance(handle))
200
}
201
202
fn spawn_scene_list<L: SceneList>(
203
&mut self,
204
scenes: L,
205
) -> Result<Vec<Entity>, SpawnSceneError> {
206
let assets = self.resource::<AssetServer>();
207
let mut patch = SceneListPatch::load(assets, scenes);
208
patch.resolve(assets, self.resource::<Assets<ScenePatch>>())?;
209
patch.spawn(self)
210
}
211
212
fn queue_spawn_scene_list<L: SceneList>(&mut self, scenes: L) {
213
let assets = self.resource::<AssetServer>();
214
let patch = SceneListPatch::load(assets, scenes);
215
let handle = assets.add(patch);
216
self.resource_mut::<QueuedScenes>()
217
.scene_list_spawns
218
.push(handle);
219
}
220
}
221
222
/// Adds scene spawning functionality to [`Commands`].
223
pub trait CommandsSceneExt {
224
/// Spawns the given [`Scene`] as soon as [`Commands`] are applied. This will resolve the Scene (using [`Scene::resolve`]). If that fails (for example, if there are dependencies that have not been
225
/// loaded yet), it will log a [`SpawnSceneError`] as an error. If resolving the [`Scene`] is successful, the scene will be spawned.
226
///
227
/// This is essentially a [`Command`] that runs [`World::spawn_scene`].
228
///
229
/// See [`Scene`] for the features of the scene system (and how to use it).
230
///
231
/// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`Commands::queue_spawn_scene`].
232
/// Note that the `.bsn` file format is not yet released.
233
///
234
/// ```
235
/// # use bevy_scene::prelude::*;
236
/// # use bevy_ecs::prelude::*;
237
/// # let mut world = World::new();
238
/// # let mut commands = world.commands();
239
/// #[derive(Component, Default, Clone)]
240
/// struct Score(usize);
241
///
242
/// #[derive(Component, Default, Clone)]
243
/// struct Sword;
244
///
245
/// #[derive(Component, Default, Clone)]
246
/// struct Shield;
247
///
248
/// commands.spawn_scene(bsn! {
249
/// #Player
250
/// Score(0)
251
/// Children [
252
/// Sword,
253
/// Shield,
254
/// ]
255
/// });
256
/// ```
257
fn spawn_scene<S: Scene>(&mut self, scene: S) -> EntityCommands<'_>;
258
259
/// Queues the `scene` to be spawned. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned
260
/// after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.
261
///
262
/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.
263
///
264
/// See [`Scene`] for the features of the scene system (and how to use it).
265
///
266
/// ```
267
/// # use bevy_scene::prelude::*;
268
/// # use bevy_ecs::prelude::*;
269
/// # let mut world = World::new();
270
/// # let mut commands = world.commands();
271
/// #[derive(Component, Default, Clone)]
272
/// struct Score(usize);
273
///
274
/// #[derive(Component, Default, Clone)]
275
/// struct Sword;
276
///
277
/// #[derive(Component, Default, Clone)]
278
/// struct Shield;
279
///
280
/// // This scene includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn"
281
/// // is fully loaded.
282
/// commands.queue_spawn_scene(bsn! {
283
/// :"player.bsn"
284
/// #Player
285
/// Score(0)
286
/// Children [
287
/// Sword,
288
/// Shield,
289
/// ]
290
/// });
291
/// ```
292
fn queue_spawn_scene<S: Scene>(&mut self, scene: S) -> EntityCommands<'_>;
293
294
/// Spawns the given [`SceneList`] as soon as [`Commands`] are applied. This will resolve the scene list (using [`SceneList::resolve_list`]). If that fails (for example, if there are dependencies that have not been
295
/// loaded yet), it will log a [`SpawnSceneError`] as an error. If resolving the [`Scene`] is successful, the scene list will be spawned.
296
///
297
/// This is essentially a [`Command`] that performs [`World::spawn_scene_list`].
298
///
299
/// See [`Scene`] for the features of the scene system (and how to use it).
300
///
301
/// If your scene list has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`Commands::queue_spawn_scene_list`].
302
///
303
/// ```
304
/// # use bevy_scene::prelude::*;
305
/// # use bevy_ecs::prelude::*;
306
/// # let mut world = World::new();
307
/// # let mut commands = world.commands();
308
/// #[derive(Component, FromTemplate)]
309
/// enum Team {
310
/// #[default]
311
/// Red,
312
/// Blue,
313
/// }
314
///
315
/// // Note that the .bsn file format is not yet released.
316
/// commands.spawn_scene_list(bsn_list! {
317
/// (
318
/// :"player.bsn"
319
/// #Player1
320
/// Team::Red
321
/// ),
322
/// (
323
/// :"player.bsn"
324
/// #Player2
325
/// Team::Blue
326
/// )
327
/// });
328
/// ```
329
fn spawn_scene_list<L: SceneList>(&mut self, scenes: L);
330
331
/// Queues the `scene_list` to be spawned. This will evaluate the `scene_list`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved
332
/// and spawned after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.
333
///
334
/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.
335
///
336
/// ```
337
/// # use bevy_scene::prelude::*;
338
/// # use bevy_ecs::prelude::*;
339
/// # let mut world = World::new();
340
/// # let mut commands = world.commands();
341
/// #[derive(Component, FromTemplate)]
342
/// enum Team {
343
/// #[default]
344
/// Red,
345
/// Blue,
346
/// }
347
///
348
/// // This scene list includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn"
349
/// // is loaded.
350
/// commands.queue_spawn_scene_list(bsn_list! [
351
/// (
352
/// :"player.bsn"
353
/// #Player1
354
/// Team::Red
355
/// ),
356
/// (
357
/// :"player.bsn"
358
/// #Player2
359
/// Team::Blue
360
/// )
361
/// ]);
362
/// ```
363
fn queue_spawn_scene_list<L: SceneList>(&mut self, scenes: L);
364
}
365
366
impl<'w, 's> CommandsSceneExt for Commands<'w, 's> {
367
fn spawn_scene<S: Scene>(&mut self, scene: S) -> EntityCommands<'_> {
368
let mut entity_commands = self.spawn_empty();
369
let id = entity_commands.id();
370
entity_commands.commands().queue(move |world: &mut World| {
371
if let Ok(mut entity) = world.get_entity_mut(id)
372
&& let Err(err) = entity.apply_scene(scene)
373
{
374
error!("{err}");
375
}
376
});
377
entity_commands
378
}
379
380
fn queue_spawn_scene<S: Scene>(&mut self, scene: S) -> EntityCommands<'_> {
381
let mut entity_commands = self.spawn_empty();
382
let id = entity_commands.id();
383
entity_commands.commands().queue(move |world: &mut World| {
384
if let Ok(mut entity) = world.get_entity_mut(id) {
385
entity.queue_apply_scene(scene);
386
}
387
});
388
entity_commands
389
}
390
391
fn spawn_scene_list<L: SceneList>(&mut self, scenes: L) {
392
self.queue(move |world: &mut World| {
393
if let Err(err) = world.spawn_scene_list(scenes) {
394
error!("{err}");
395
}
396
});
397
}
398
399
fn queue_spawn_scene_list<L: SceneList>(&mut self, scenes: L) {
400
self.queue(move |world: &mut World| {
401
world.queue_spawn_scene_list(scenes);
402
});
403
}
404
}
405
406
/// Adds scene functionality to [`EntityWorldMut`].
407
pub trait EntityWorldMutSceneExt {
408
/// Spawns a [`SceneList`], where each entity is related to the current entity using [`RelationshipTarget::Relationship`].
409
///
410
/// This will evaluate the `scene_list`'s dependencies (via [`SceneList::register_dependencies`]) and queue it to be resolved
411
/// and spawned after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.
412
///
413
/// If the dependencies are already loaded (or there are no dependencies), then the scene list will be spawned this frame.
414
///
415
/// ```
416
/// # use bevy_app::App;
417
/// # use bevy_scene::{prelude::*, ScenePlugin};
418
/// # use bevy_ecs::prelude::*;
419
/// # use bevy_asset::AssetPlugin;
420
/// # use bevy_app::TaskPoolPlugin;
421
/// # let mut app = App::new();
422
/// # app.add_plugins((
423
/// # TaskPoolPlugin::default(),
424
/// # AssetPlugin::default(),
425
/// # ScenePlugin::default(),
426
/// # ));
427
/// # let world = app.world_mut();
428
/// #[derive(Component, FromTemplate)]
429
/// enum Team {
430
/// #[default]
431
/// Red,
432
/// Blue,
433
/// }
434
///
435
/// world.spawn_empty().queue_spawn_related_scenes::<Children>(bsn_list! {
436
/// (
437
/// #Player1
438
/// Team::Red
439
/// ),
440
/// (
441
/// #Player2
442
/// Team::Blue
443
/// )
444
/// });
445
/// ```
446
fn queue_spawn_related_scenes<T: RelationshipTarget>(self, scenes: impl SceneList) -> Self;
447
448
/// Applies the given [`Scene`] to the current entity immediately. This will resolve the Scene (using [`Scene::resolve`]). If that fails (for example, if there are dependencies that have not been
449
/// loaded yet), it will return a [`SpawnSceneError`]. If resolving the [`Scene`] is successful, the scene will be spawned.
450
///
451
/// If resolving and spawning is successful, the entity will contain the full contents of the spawned scene.
452
///
453
/// This will write directly on top of any existing components on the entity. [`Scene`] is generally used as a spawning mechanism, so for most things, prefer using [`World::spawn_scene`].
454
///
455
/// See [`Scene`] for the features of the scene system (and how to use it).
456
///
457
/// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`World::queue_spawn_scene`].
458
/// Note that the .bsn file format is not yet released.
459
fn apply_scene<S: Scene>(&mut self, scene: S) -> Result<(), SpawnSceneError>;
460
461
/// Queues the `scene` to be applied. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned
462
/// after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.
463
///
464
/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.
465
/// This will write directly on top of any existing components on the entity. [`Scene`] is generally used as a spawning mechanism, so for most things, prefer using [`World::queue_spawn_scene`].
466
///
467
/// See [`Scene`] for the features of the scene system (and how to use it).
468
fn queue_apply_scene<S: Scene>(&mut self, scene: S);
469
}
470
471
impl EntityWorldMutSceneExt for EntityWorldMut<'_> {
472
fn queue_spawn_related_scenes<T: RelationshipTarget>(mut self, scenes: impl SceneList) -> Self {
473
let assets = self.resource::<AssetServer>();
474
let patch = SceneListPatch::load(assets, scenes);
475
let handle = assets.add(patch);
476
let entity = self.id();
477
self.resource_mut::<QueuedScenes>()
478
.related_scene_list_spawns
479
.push((
480
RelatedSceneListSpawn {
481
entity,
482
insert: |entity, target| {
483
entity.insert(
484
<<T as RelationshipTarget>::Relationship as Relationship>::from(target),
485
);
486
},
487
},
488
handle,
489
));
490
self
491
}
492
493
fn apply_scene<S: Scene>(&mut self, scene: S) -> Result<(), SpawnSceneError> {
494
let assets = self.resource::<AssetServer>();
495
let mut patch = ScenePatch::load(assets, scene);
496
patch.resolve(assets, self.resource::<Assets<ScenePatch>>())?;
497
patch.apply(self)
498
}
499
500
fn queue_apply_scene<S: Scene>(&mut self, scene: S) {
501
let assets = self.resource::<AssetServer>();
502
let patch = ScenePatch::load(assets, scene);
503
let handle = assets.add(patch);
504
self.insert(ScenePatchInstance(handle));
505
}
506
}
507
508
/// Adds scene functionality to [`EntityWorldMut`].
509
pub trait EntityCommandsSceneExt {
510
/// Spawns a [`SceneList`], where each entity is related to the current entity using [`RelationshipTarget::Relationship`].
511
///
512
/// This will evaluate the `scene_list`'s dependencies (via [`SceneList::register_dependencies`]) and queue it to be resolved
513
/// and spawned after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.
514
///
515
/// If the dependencies are already loaded (or there are no dependencies), then the scene list will be spawned this frame.
516
///
517
/// ```
518
/// # use bevy_app::App;
519
/// # use bevy_scene::prelude::*;
520
/// # use bevy_ecs::prelude::*;
521
/// # use bevy_asset::AssetPlugin;
522
/// # use bevy_app::TaskPoolPlugin;
523
/// # let mut app = App::new();
524
/// # let mut commands = app.world_mut().commands();
525
/// #[derive(Component, FromTemplate)]
526
/// enum Team {
527
/// #[default]
528
/// Red,
529
/// Blue,
530
/// }
531
///
532
/// commands.spawn_empty().queue_spawn_related_scenes::<Children>(bsn_list! {
533
/// (
534
/// #Player1
535
/// Team::Red
536
/// ),
537
/// (
538
/// #Player2
539
/// Team::Blue
540
/// )
541
/// });
542
/// ```
543
fn queue_spawn_related_scenes<T: RelationshipTarget>(
544
&mut self,
545
scenes: impl SceneList,
546
) -> &mut Self;
547
548
/// Applies the given [`Scene`] to the current entity as soon as [`Commands`] are applied. This will resolve the Scene (using [`Scene::resolve`]). If that fails (for example, if there are dependencies that have not been
549
/// loaded yet), it will log a [`SpawnSceneError`] as an error. If resolving the [`Scene`] is successful, the scene will be spawned.
550
///
551
/// If resolving and spawning is successful, the entity will contain the full contents of the spawned scene.
552
///
553
/// This will write directly on top of any existing components on the entity. [`Scene`] is generally used as a spawning mechanism, so for most things, prefer using [`Commands::spawn_scene`].
554
///
555
/// See [`Scene`] for the features of the scene system (and how to use it).
556
///
557
/// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`Commands::spawn_scene`].
558
/// Note that the .bsn file format is not yet released.
559
fn apply_scene<S: Scene>(&mut self, scene: S) -> &mut Self;
560
561
/// Queues the `scene` to be applied. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned
562
/// after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.
563
///
564
/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.
565
/// This will write directly on top of any existing components on the entity. [`Scene`] is generally used as a spawning mechanism, so for most things, prefer using [`Commands::queue_spawn_scene`].
566
///
567
/// See [`Scene`] for the features of the scene system (and how to use it).
568
fn queue_apply_scene<S: Scene>(&mut self, scene: S) -> &mut Self;
569
}
570
571
impl EntityCommandsSceneExt for EntityCommands<'_> {
572
fn queue_spawn_related_scenes<T: RelationshipTarget>(
573
&mut self,
574
scenes: impl SceneList,
575
) -> &mut Self {
576
self.queue(move |entity: EntityWorldMut| {
577
entity.queue_spawn_related_scenes::<T>(scenes);
578
});
579
self
580
}
581
582
fn apply_scene<S: Scene>(&mut self, scene: S) -> &mut Self {
583
self.queue(move |mut entity: EntityWorldMut| entity.apply_scene(scene));
584
self
585
}
586
587
fn queue_apply_scene<S: Scene>(&mut self, scene: S) -> &mut Self {
588
self.queue(move |mut entity: EntityWorldMut| entity.queue_apply_scene(scene));
589
self
590
}
591
}
592
593
/// A [`System`] that resolves [`ScenePatch`] and [`SceneListPatch`] assets whose dependencies have been fully loaded.
594
pub fn resolve_scene_patches(
595
mut events: MessageReader<AssetEvent<ScenePatch>>,
596
mut list_events: MessageReader<AssetEvent<SceneListPatch>>,
597
assets: Res<AssetServer>,
598
mut patches: ResMut<Assets<ScenePatch>>,
599
mut list_patches: ResMut<Assets<SceneListPatch>>,
600
mut waiting: ResMut<WaitingScenes>,
601
) {
602
for event in events.read() {
603
match *event {
604
AssetEvent::LoadedWithDependencies { id } => {
605
if let Some(scene) = patches.get_mut(id).and_then(|mut p| p.scene.take()) {
606
match ResolvedSceneRoot::resolve(scene, &assets, &patches) {
607
Ok(resolved) => {
608
let mut patch = patches.get_mut(id).unwrap();
609
patch.resolved = Some(Arc::new(resolved));
610
}
611
Err(err) => error!("Failed to resolve scene {id}: {err}"),
612
}
613
}
614
}
615
AssetEvent::Removed { id } => {
616
if let Some(waiting_entities) = waiting.scene_entities.remove(&id)
617
&& !waiting_entities.is_empty()
618
{
619
error!(
620
"Failed to spawn entities waiting for scene {id:?} because it was removed: {waiting_entities:?}"
621
);
622
}
623
}
624
_ => {}
625
}
626
}
627
for event in list_events.read() {
628
match *event {
629
AssetEvent::LoadedWithDependencies { id } => {
630
if let Some(mut list_patch) = list_patches.get_mut(id)
631
&& let Err(err) = list_patch.resolve(&assets, &patches)
632
{
633
error!("Failed to resolve scene list {id}: {err}");
634
}
635
}
636
AssetEvent::Removed { id } => {
637
if let Some(waiting_scene_lists) = waiting.scene_list_spawns.remove(&id)
638
&& waiting_scene_lists > 0
639
{
640
error!(
641
"Failed to spawn scene list {id:?} {waiting_scene_lists} times because it was removed."
642
);
643
}
644
645
if let Some(waiting_related) = waiting.related_list_entities.remove(&id)
646
&& !waiting_related.is_empty()
647
{
648
let waiting_entities =
649
waiting_related.iter().map(|r| r.entity).collect::<Vec<_>>();
650
error!(
651
"Failed to spawn related entities for scene list {id:?} because it was removed: {waiting_entities:?}"
652
);
653
}
654
}
655
_ => {}
656
}
657
}
658
}
659
660
/// A [`Resource`] that tracks entities / scenes that have been queued to spawn.
661
#[derive(Resource, Default)]
662
pub struct QueuedScenes {
663
new_scene_entities: Vec<Entity>,
664
related_scene_list_spawns: Vec<(RelatedSceneListSpawn, Handle<SceneListPatch>)>,
665
scene_list_spawns: Vec<Handle<SceneListPatch>>,
666
}
667
668
/// A [`Resource`] that tracks entities / scenes that are waiting for an asset to load
669
#[derive(Resource, Default)]
670
pub struct WaitingScenes {
671
scene_entities: HashMap<Handle<ScenePatch>, Vec<Entity>>,
672
related_list_entities: HashMap<Handle<SceneListPatch>, Vec<RelatedSceneListSpawn>>,
673
scene_list_spawns: HashMap<Handle<SceneListPatch>, usize>,
674
}
675
676
pub(crate) struct RelatedSceneListSpawn {
677
entity: Entity,
678
insert: fn(&mut EntityWorldMut, target: Entity),
679
}
680
681
/// An [`Observer`] system that queues newly added [`ScenePatchInstance`] entities.
682
pub fn on_add_scene_patch_instance(
683
add: On<Add, ScenePatchInstance>,
684
mut queued_scenes: ResMut<QueuedScenes>,
685
) {
686
queued_scenes.new_scene_entities.push(add.entity);
687
}
688
689
/// A system that spawns queued scenes when they are loaded.
690
pub fn spawn_queued(
691
world: &mut World,
692
scene_patch_instances: &mut QueryState<&ScenePatchInstance>,
693
mut queued: Local<QueuedScenes>,
694
mut bundle_scratch: Local<BundleScratch>,
695
mut reader: Local<MessageCursor<AssetEvent<ScenePatch>>>,
696
mut list_reader: Local<MessageCursor<AssetEvent<SceneListPatch>>>,
697
) {
698
core::mem::swap(&mut *world.resource_mut::<QueuedScenes>(), &mut queued);
699
world.resource_scope(|world, mut list_patches: Mut<Assets<SceneListPatch>>| {
700
world.resource_scope(|world, mut waiting: Mut<WaitingScenes>| {
701
loop {
702
if queued.is_empty() {
703
break;
704
}
705
queued.spawn_queued(
706
world,
707
&mut waiting,
708
scene_patch_instances,
709
&mut bundle_scratch,
710
&list_patches,
711
);
712
}
713
714
world.resource_scope(|world, events: Mut<Messages<AssetEvent<ScenePatch>>>| {
715
for event in reader.read(&events) {
716
let patches = world.resource::<Assets<ScenePatch>>();
717
if let AssetEvent::LoadedWithDependencies { id } = event
718
&& let Some(resolved) = patches.get(*id).and_then(|p| p.resolved.clone())
719
&& let Some(entities) = waiting.scene_entities.remove(id)
720
{
721
for entity in entities {
722
if let Ok(mut entity_mut) = world.get_entity_mut(entity)
723
&& let Err(err) =
724
resolved.apply(&mut entity_mut, &mut bundle_scratch)
725
{
726
error!(
727
"Failed to apply scene (id: {}) to entity {entity}: {}",
728
id, err
729
);
730
}
731
}
732
}
733
}
734
});
735
world.resource_scope(
736
|world, list_events: Mut<Messages<AssetEvent<SceneListPatch>>>| {
737
for event in list_reader.read(&list_events) {
738
if let AssetEvent::LoadedWithDependencies { id } = event
739
&& let Some(list_patch) = list_patches.get_mut(*id)
740
{
741
if let Some(scene_list_spawns) =
742
waiting.related_list_entities.remove(id)
743
{
744
for scene_list_spawn in scene_list_spawns {
745
let result = list_patch.spawn_with(world, |entity| {
746
(scene_list_spawn.insert)(entity, scene_list_spawn.entity);
747
});
748
749
if let Err(err) = result {
750
error!("Failed to spawn scene list (id: {}): {}", id, err);
751
}
752
}
753
}
754
755
if let Some(waiting_list_spawns) = waiting.scene_list_spawns.remove(id)
756
{
757
for _ in 0..waiting_list_spawns {
758
let result = list_patch.spawn(world);
759
if let Err(err) = result {
760
error!("Failed to spawn scene list (id: {}): {}", id, err);
761
}
762
}
763
}
764
}
765
}
766
},
767
);
768
});
769
});
770
}
771
772
impl QueuedScenes {
773
fn is_empty(&self) -> bool {
774
self.new_scene_entities.is_empty()
775
&& self.related_scene_list_spawns.is_empty()
776
&& self.scene_list_spawns.is_empty()
777
}
778
779
fn spawn_queued(
780
&mut self,
781
world: &mut World,
782
waiting_scenes: &mut WaitingScenes,
783
scene_patch_instances: &mut QueryState<&ScenePatchInstance>,
784
bundle_scratch: &mut BundleScratch,
785
list_patches: &Assets<SceneListPatch>,
786
) {
787
for entity in core::mem::take(&mut self.new_scene_entities) {
788
let Ok(handle) = scene_patch_instances.get(world, entity).map(|h| &h.0) else {
789
continue;
790
};
791
let patches = world.resource::<Assets<ScenePatch>>();
792
if let Some(resolved) = patches.get(handle).and_then(|p| p.resolved.clone()) {
793
let mut entity_mut = world.get_entity_mut(entity).unwrap();
794
if let Err(err) = resolved.apply(&mut entity_mut, bundle_scratch) {
795
let scene_patch_instance = scene_patch_instances.get(world, entity).unwrap();
796
let handle = &scene_patch_instance.0;
797
let id = handle.id();
798
let path = handle.path();
799
error!(
800
"Failed to apply scene (id: {id}, path: {path:?}) to \
801
entity {entity}: {err}",
802
);
803
}
804
} else {
805
let entities = waiting_scenes
806
.scene_entities
807
.entry(handle.clone())
808
.or_default();
809
entities.push(entity);
810
}
811
}
812
813
for (scene_list_spawn, handle) in core::mem::take(&mut self.related_scene_list_spawns) {
814
if let Some(list_patch) = list_patches.get(&handle) {
815
let result = list_patch.spawn_with(world, |entity| {
816
(scene_list_spawn.insert)(entity, scene_list_spawn.entity);
817
});
818
819
if let Err(err) = result {
820
error!(
821
"Failed to spawn scene list (id: {}, path: {:?}): {}",
822
handle.id(),
823
handle.path(),
824
err
825
);
826
}
827
} else {
828
let entities = waiting_scenes
829
.related_list_entities
830
.entry(handle)
831
.or_default();
832
entities.push(scene_list_spawn);
833
}
834
}
835
836
for handle in core::mem::take(&mut self.scene_list_spawns) {
837
if let Some(list_patch) = list_patches.get(&handle) {
838
let result = list_patch.spawn(world);
839
if let Err(err) = result {
840
error!(
841
"Failed to spawn scene list (id: {}, path: {:?}): {}",
842
handle.id(),
843
handle.path(),
844
err
845
);
846
}
847
} else {
848
let count = waiting_scenes.scene_list_spawns.entry(handle).or_default();
849
*count += 1;
850
}
851
}
852
}
853
}
854
855