From b706d752eff0a061ccaddd1d6ba443d5e44da3c2 Mon Sep 17 00:00:00 2001 From: Wybe Westra Date: Sat, 15 Jan 2022 13:32:00 +0100 Subject: [PATCH 1/2] During startup, auto adds default ui camera if there is none and ui nodes exist. --- crates/bevy_ui/src/lib.rs | 5 ++ crates/bevy_ui/src/startup.rs | 96 +++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 crates/bevy_ui/src/startup.rs diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 2f89f6df9ca85..119a162e28e82 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -6,6 +6,7 @@ mod flex; mod focus; mod margins; mod render; +mod startup; mod ui_node; pub mod entity; @@ -72,6 +73,10 @@ impl Plugin for UiPlugin { .register_type::() .register_type::() .register_type::() + .add_system_to_stage( + StartupStage::PostStartup, + startup::add_default_ui_cam_if_needed, + ) .add_system_to_stage( CoreStage::PreUpdate, ui_focus_system.label(UiSystem::Focus).after(InputSystem), diff --git a/crates/bevy_ui/src/startup.rs b/crates/bevy_ui/src/startup.rs new file mode 100644 index 0000000000000..ce0d02c788369 --- /dev/null +++ b/crates/bevy_ui/src/startup.rs @@ -0,0 +1,96 @@ +use crate::prelude::UiCameraBundle; +use crate::ui_node::Node; +use crate::CAMERA_UI; +use bevy_ecs::prelude::{Commands, Entity, Query, With}; +use bevy_render::prelude::{Camera, OrthographicProjection}; + +pub fn add_default_ui_cam_if_needed( + mut commands: Commands, + node_query: Query>, + ui_cam_query: Query<&Camera, With>, +) { + let world_contains_nodes = node_query.iter().next().is_some(); + let world_contains_ui_cam = ui_cam_query + .iter() + .any(|cam| cam.name.as_deref() == Some(CAMERA_UI)); + + if world_contains_nodes && !world_contains_ui_cam { + commands.spawn_bundle(UiCameraBundle::default()); + } +} + +#[cfg(test)] +mod tests { + use crate::entity::{NodeBundle, UiCameraBundle}; + use crate::startup::add_default_ui_cam_if_needed; + use bevy_ecs::prelude::{Stage, SystemStage, World}; + use bevy_render::prelude::{Camera, OrthographicProjection}; + + #[test] + fn no_ui_camera_added_when_no_ui_nodes_exist() { + let mut world = World::default(); + + let mut startup_stage = SystemStage::parallel(); + startup_stage.add_system(add_default_ui_cam_if_needed); + + startup_stage.run(&mut world); + + assert_eq!( + world + .query::<(&Camera, &OrthographicProjection)>() + .iter(&world) + .len(), + 0 + ); + } + + #[test] + fn ui_camera_added_when_ui_nodes_exist() { + let mut world = World::default(); + + world.spawn().insert_bundle(NodeBundle::default()); + + let mut startup_stage = SystemStage::parallel(); + startup_stage.add_system(add_default_ui_cam_if_needed); + + startup_stage.run(&mut world); + + assert_eq!( + world + .query::<(&Camera, &OrthographicProjection)>() + .iter(&world) + .len(), + 1 + ); + } + + #[test] + fn no_duplicate_ui_camera_added_when_one_is_already_present() { + let mut world = World::default(); + + let cam_id = world.spawn().insert_bundle(UiCameraBundle::default()).id(); + + assert_eq!( + world + .query::<(&Camera, &OrthographicProjection)>() + .iter(&world) + .len(), + 1 + ); + + let mut startup_stage = SystemStage::parallel(); + startup_stage.add_system(add_default_ui_cam_if_needed); + + startup_stage.run(&mut world); + + assert_eq!( + world + .query::<(&Camera, &OrthographicProjection)>() + .iter(&world) + .len(), + 1 + ); + + assert!(world.get::(cam_id).is_some()); + } +} From 12539c7cf8621fd5df988f9e7791500e034dca88 Mon Sep 17 00:00:00 2001 From: Wybe Westra Date: Sat, 15 Jan 2022 13:49:44 +0100 Subject: [PATCH 2/2] Removed ui camera from examples that spawn ui before startup --- crates/bevy_ui/src/lib.rs | 3 ++- examples/2d/contributors.rs | 1 - examples/game/breakout.rs | 1 - examples/scene/scene.rs | 1 - examples/tools/bevymark.rs | 1 - examples/ui/button.rs | 2 -- examples/ui/font_atlas_debug.rs | 1 - examples/ui/text.rs | 2 -- examples/ui/text_debug.rs | 1 - examples/ui/ui.rs | 3 --- examples/window/scale_factor_override.rs | 2 -- 11 files changed, 2 insertions(+), 16 deletions(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 119a162e28e82..3d19b7fb5a568 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -1,6 +1,7 @@ //! This crate contains Bevy's UI system, which can be used to create UI for both 2D and 3D games //! # Basic usage //! Spawn [`entity::UiCameraBundle`] and spawn UI elements with [`entity::ButtonBundle`], [`entity::ImageBundle`], [`entity::TextBundle`] and [`entity::NodeBundle`] +//! When UI elements are present during startup, the [`entity::UiCameraBundle`] will be added automatically. //! This UI is laid out with the Flexbox paradigm (see ) except the vertical axis is inverted mod flex; mod focus; @@ -73,7 +74,7 @@ impl Plugin for UiPlugin { .register_type::() .register_type::() .register_type::() - .add_system_to_stage( + .add_startup_system_to_stage( StartupStage::PostStartup, startup::add_default_ui_cam_if_needed, ) diff --git a/examples/2d/contributors.rs b/examples/2d/contributors.rs index cefce614588a7..17089baa7b297 100644 --- a/examples/2d/contributors.rs +++ b/examples/2d/contributors.rs @@ -118,7 +118,6 @@ fn setup_contributor_selection(mut commands: Commands, asset_server: Res) { commands.spawn_bundle(OrthographicCameraBundle::new_2d()); - commands.spawn_bundle(UiCameraBundle::default()); commands.spawn_bundle((SelectTimer, Timer::from_seconds(SHOWCASE_TIMER_SECS, true))); diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 1bee7cb8df37c..38d690b0f1299 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -50,7 +50,6 @@ fn setup(mut commands: Commands, asset_server: Res) { // cameras commands.spawn_bundle(OrthographicCameraBundle::new_2d()); - commands.spawn_bundle(UiCameraBundle::default()); // paddle commands .spawn_bundle(SpriteBundle { diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index b8f821489451a..078e8d2c309e8 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -103,7 +103,6 @@ fn save_scene_system(world: &mut World) { // This is only necessary for the info message in the UI. See examples/ui/text.rs for a standalone // text example. fn infotext_system(mut commands: Commands, asset_server: Res) { - commands.spawn_bundle(UiCameraBundle::default()); commands.spawn_bundle(TextBundle { style: Style { align_self: AlignSelf::FlexEnd, diff --git a/examples/tools/bevymark.rs b/examples/tools/bevymark.rs index 290fdf42594aa..c67b65928eb54 100644 --- a/examples/tools/bevymark.rs +++ b/examples/tools/bevymark.rs @@ -83,7 +83,6 @@ fn setup(mut commands: Commands, asset_server: Res) { let texture = asset_server.load("branding/icon.png"); commands.spawn_bundle(OrthographicCameraBundle::new_2d()); - commands.spawn_bundle(UiCameraBundle::default()); commands.spawn_bundle(TextBundle { text: Text { sections: vec![ diff --git a/examples/ui/button.rs b/examples/ui/button.rs index 48e67b555ebf9..bdea65e6f9323 100644 --- a/examples/ui/button.rs +++ b/examples/ui/button.rs @@ -41,8 +41,6 @@ fn button_system( } fn setup(mut commands: Commands, asset_server: Res) { - // ui camera - commands.spawn_bundle(UiCameraBundle::default()); commands .spawn_bundle(ButtonBundle { style: Style { diff --git a/examples/ui/font_atlas_debug.rs b/examples/ui/font_atlas_debug.rs index aef0075bca909..083c8c68795cc 100644 --- a/examples/ui/font_atlas_debug.rs +++ b/examples/ui/font_atlas_debug.rs @@ -79,7 +79,6 @@ fn text_update_system(mut state: ResMut, time: Res