Skip to content

Commit aa5322e

Browse files
authored
BSN: scene.spawn() system ergonomics (#23868)
# Objective Spawning a scene on startup is a common pattern. Lets make it easier to do so! ## Solution - Add `SpawnSystem` and `SpawnListSystem` traits that are implemented for functions that return scenes / scene lists, and return a system that spawns the scene / handles errors. ### Before ```rust fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .run(); } fn setup(world: &mut World) -> Result { world.spawn_scene_list(bsn_list![Camera2d, ui()])?; Ok(()) } ``` ### After ```rust fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, scene.spawn()) .run(); } fn scene() -> impl SceneList { bsn_list![Camera2d, ui()] } ``` This cuts out some boilerplate. It also further encourages people to define standalone "scene functions" rather than embedding them in code, which is generally a good pattern.
1 parent c847aaf commit aa5322e

6 files changed

Lines changed: 50 additions & 19 deletions

File tree

crates/bevy_scene/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ pub mod prelude {
509509
pub use crate::{
510510
bsn, bsn_list, on, template_value, CommandsSceneExt, EntityCommandsSceneExt,
511511
EntityWorldMutSceneExt, PatchFromTemplate, PatchTemplate, Scene, SceneList,
512-
ScenePatchInstance, WorldSceneExt,
512+
ScenePatchInstance, SpawnListSystem, SpawnSystem, WorldSceneExt,
513513
};
514514
}
515515

@@ -523,13 +523,15 @@ mod scene;
523523
mod scene_list;
524524
mod scene_patch;
525525
mod spawn;
526+
mod spawn_system;
526527

527528
pub use bevy_scene_macros::*;
528529
pub use resolved_scene::*;
529530
pub use scene::*;
530531
pub use scene_list::*;
531532
pub use scene_patch::*;
532533
pub use spawn::*;
534+
pub use spawn_system::*;
533535

534536
use bevy_app::{App, Plugin, SceneSpawnerSystems, SpawnScene};
535537
use bevy_asset::AssetApp;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use crate::{Scene, SceneList, WorldSceneExt};
2+
use bevy_ecs::{error::Result, world::World};
3+
4+
/// Returns a system that spawns the given [`Scene`]. This should generally only be added to
5+
/// schedules that run once, such as [`Startup`](bevy_app::Startup).
6+
pub trait SpawnSystem {
7+
/// Returns a system that spawns the given [`Scene`]. This should generally only be added to
8+
/// schedules that run once, such as [`Startup`](bevy_app::Startup).
9+
fn spawn(self) -> impl FnMut(&mut World) -> Result;
10+
}
11+
12+
impl<F: FnMut() -> S + Send + Sync + 'static, S: Scene> SpawnSystem for F {
13+
fn spawn(mut self) -> impl FnMut(&mut World) -> Result {
14+
move |world: &mut World| -> Result {
15+
world.spawn_scene(self())?;
16+
Ok(())
17+
}
18+
}
19+
}
20+
21+
/// Returns a system that spawns the given [`SceneList`]. This should generally only be added to
22+
/// schedules that run once, such as [`Startup`](bevy_app::Startup).
23+
pub trait SpawnListSystem {
24+
/// Returns a system that spawns the given [`SceneList`]. This should generally only be added to
25+
/// schedules that run once, such as [`Startup`](bevy_app::Startup).
26+
fn spawn(self) -> impl FnMut(&mut World) -> Result;
27+
}
28+
impl<F: FnMut() -> S + Send + Sync + 'static, S: SceneList> SpawnListSystem for F {
29+
fn spawn(mut self) -> impl FnMut(&mut World) -> Result {
30+
move |world: &mut World| -> Result {
31+
world.spawn_scene_list(self())?;
32+
Ok(())
33+
}
34+
}
35+
}

examples/scene/bsn.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ use bevy::{prelude::*, text::FontSourceTemplate};
44
fn main() {
55
App::new()
66
.add_plugins(DefaultPlugins)
7-
.add_systems(Startup, setup)
7+
.add_systems(Startup, scene.spawn())
88
.run();
99
}
1010

11-
fn setup(world: &mut World) -> Result {
12-
world.spawn_scene_list(bsn_list![Camera2d, ui()])?;
13-
Ok(())
11+
fn scene() -> impl SceneList {
12+
bsn_list![Camera2d, ui()]
1413
}
1514

1615
fn ui() -> impl Scene {

examples/ui/widgets/feathers_counter.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use bevy::{
1010
tokens, FeathersPlugins,
1111
},
1212
prelude::*,
13-
scene::prelude::Scene,
1413
ui_widgets::Activate,
1514
};
1615

@@ -31,17 +30,16 @@ fn main() {
3130
// Configure feathers to use the dark theme
3231
.insert_resource(UiTheme(create_dark_theme()))
3332
.insert_resource(Counter(0))
34-
.add_systems(Startup, setup)
33+
.add_systems(Startup, scene.spawn())
3534
.add_systems(
3635
Update,
3736
update_counter_text.run_if(resource_changed::<Counter>),
3837
)
3938
.run();
4039
}
4140

42-
fn setup(world: &mut World) -> Result {
43-
world.spawn_scene_list(bsn_list![Camera2d, demo_root()])?;
44-
Ok(())
41+
fn scene() -> impl SceneList {
42+
bsn_list![Camera2d, demo_root()]
4543
}
4644

4745
fn demo_root() -> impl Scene {

examples/ui/widgets/feathers_gallery.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use bevy::{
2626
},
2727
input_focus::{tab_navigation::TabGroup, AutoFocus, InputFocus},
2828
prelude::*,
29-
scene::prelude::Scene,
3029
text::{EditableText, TextEdit, TextEditChange},
3130
ui::{Checked, InteractionDisabled},
3231
ui_widgets::{
@@ -64,14 +63,13 @@ fn main() {
6463
rgb_color: palettes::tailwind::EMERALD_800.with_alpha(0.7),
6564
hsl_color: palettes::tailwind::AMBER_800.into(),
6665
})
67-
.add_systems(Startup, setup)
66+
.add_systems(Startup, scene.spawn())
6867
.add_systems(Update, update_colors)
6968
.run();
7069
}
7170

72-
fn setup(world: &mut World) -> Result {
73-
world.spawn_scene_list(bsn_list![Camera2d, demo_root()])?;
74-
Ok(())
71+
fn scene() -> impl SceneList {
72+
bsn_list![Camera2d, demo_root()]
7573
}
7674

7775
fn demo_root() -> impl Scene {

examples/ui/widgets/virtual_keyboard.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,16 @@ fn main() {
1515
App::new()
1616
.add_plugins((DefaultPlugins, FeathersPlugins))
1717
.insert_resource(UiTheme(create_dark_theme()))
18-
.add_systems(Startup, setup)
18+
.add_systems(Startup, scene.spawn())
1919
.run();
2020
}
2121

2222
fn on_virtual_key_pressed(virtual_key_pressed: On<VirtualKeyPressed<&'static str>>) {
2323
println!("key pressed: {}", virtual_key_pressed.key);
2424
}
2525

26-
fn setup(world: &mut World) -> Result {
27-
world.spawn_scene_list(bsn_list![Camera2d, keyboard()])?;
28-
Ok(())
26+
fn scene() -> impl SceneList {
27+
bsn_list![Camera2d, keyboard()]
2928
}
3029

3130
fn keyboard() -> impl Scene {

0 commit comments

Comments
 (0)