From bf6966fe385411ce93a81d5b9b81f0ea8cf8ff86 Mon Sep 17 00:00:00 2001 From: Thom Bruce Date: Mon, 6 Nov 2023 00:26:15 +0000 Subject: [PATCH 1/3] add new menu translations and buttons --- assets/locales/de/de-DE/start_menu.ftl | 6 +- assets/locales/en/en-US/start_menu.ftl | 6 +- assets/locales/ru/ru-RU/start_menu.ftl | 6 +- src/ui/menus/start_menu.rs | 96 ++++++++------------------ 4 files changed, 42 insertions(+), 72 deletions(-) diff --git a/assets/locales/de/de-DE/start_menu.ftl b/assets/locales/de/de-DE/start_menu.ftl index 05ef160..13b4345 100644 --- a/assets/locales/de/de-DE/start_menu.ftl +++ b/assets/locales/de/de-DE/start_menu.ftl @@ -1,2 +1,4 @@ -press-start = Drücke Start -credits-prompt = Drücken Sie „C“ für Credits +new-game = Neues Spiel +settings = Einstellungen +credits = Credits +quit = Aufhören diff --git a/assets/locales/en/en-US/start_menu.ftl b/assets/locales/en/en-US/start_menu.ftl index 8c307b0..9a26ffb 100644 --- a/assets/locales/en/en-US/start_menu.ftl +++ b/assets/locales/en/en-US/start_menu.ftl @@ -1,2 +1,4 @@ -press-start = Press Start -credits-prompt = Press 'C' for Credits +new-game = New Game +settings = Settings +credits = Credits +quit = Quit diff --git a/assets/locales/ru/ru-RU/start_menu.ftl b/assets/locales/ru/ru-RU/start_menu.ftl index 424aa1c..a1e1197 100644 --- a/assets/locales/ru/ru-RU/start_menu.ftl +++ b/assets/locales/ru/ru-RU/start_menu.ftl @@ -1,2 +1,4 @@ -press-start = нажмите старт -credits-prompt = Нажмите «C», чтобы получить кредиты \ No newline at end of file +new-game = Новая игра +settings = Настройки +credits = Кредиты +quit = Покидать diff --git a/src/ui/menus/start_menu.rs b/src/ui/menus/start_menu.rs index 3ce2fd4..4fdb7f8 100644 --- a/src/ui/menus/start_menu.rs +++ b/src/ui/menus/start_menu.rs @@ -6,10 +6,7 @@ use fluent_content::Content; use leafwing_input_manager::prelude::{ActionState, InputManagerPlugin}; use crate::{ - core::{ - effects::blink::DrawBlinkTimer, - resources::assets::{AudioAssets, UiAssets}, - }, + core::resources::assets::{AudioAssets, UiAssets}, i18n::I18n, inputs::menu::{menu_input_map, MenuAction}, systems::states::{ForState, GameState}, @@ -71,77 +68,44 @@ pub(crate) fn spawn_start_menu(mut commands: Commands, ui: Res, i18n: style: Style { width: Val::Px(350.0), height: Val::Px(169.4), + margin: UiRect::bottom(Val::Px(25.)), ..default() }, ..default() }, Name::new("Title"), )); - parent.spawn(( - TextBundle { - style: Style { - margin: UiRect::top(Val::Px(50.)), - ..default() - }, - text: Text::from_section( - i18n.content("press-start").unwrap().to_ascii_uppercase(), - TextStyle { - font: ui.font.clone(), - font_size: 35.0, - color: Color::rgb_u8(0x00, 0x88, 0x88), - }, - ), - ..default() - }, - DrawBlinkTimer(Timer::from_seconds(0.65, TimerMode::Repeating)), - )); - }); - commands - .spawn(( - NodeBundle { - style: Style { - position_type: PositionType::Absolute, - width: Val::Percent(100.0), - height: Val::Percent(100.0), - align_items: AlignItems::FlexEnd, - justify_content: JustifyContent::FlexEnd, - flex_direction: FlexDirection::Row, - ..default() - }, - ..default() - }, - ForState { - states: vec![GameState::StartMenu], - }, - Name::new("Hint"), - )) - .with_children(|parent| { - parent.spawn(( - TextBundle { - style: Style { - justify_content: JustifyContent::FlexEnd, - margin: UiRect { - left: Val::Px(10.0), - right: Val::Px(10.0), - top: Val::Px(10.0), - bottom: Val::Px(10.0), - }, - ..default() - }, - text: Text::from_section( - i18n.content("credits-prompt").unwrap(), - TextStyle { - font: ui.font.clone(), - font_size: 25.0, - color: Color::rgb_u8(0xAA, 0xAA, 0x33), + for string in ["new-game", "settings", "credits", "quit"] { + parent + .spawn(ButtonBundle { + style: Style { + margin: UiRect::top(Val::Px(25.)), + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, ..default() }, - ), - ..default() - }, - Name::new("Hint Text"), - )); + background_color: BackgroundColor(Color::Rgba { + red: 0., + green: 0., + blue: 0., + alpha: 0., + }), + ..default() + }) + .with_children(|parent| { + parent.spawn( + TextBundle::from_section( + i18n.content(string).unwrap().to_ascii_uppercase(), + TextStyle { + font: ui.font.clone(), + font_size: 25.0, + color: Color::rgb_u8(0x00, 0x88, 0x88), + }, + ), // DrawBlinkTimer(Timer::from_seconds(0.65, TimerMode::Repeating)), + ); + }); + } }); } From fec8e52285a268ac60395f8285bb327e1a3dfc9d Mon Sep 17 00:00:00 2001 From: Thom Bruce Date: Mon, 6 Nov 2023 02:09:05 +0000 Subject: [PATCH 2/3] enable menu navigation via Bevy UI Navigation crate --- CHANGELOG.md | 10 ++++ Cargo.lock | 92 ++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/inputs/menu.rs | 13 ++++- src/systems/mod.rs | 7 +++ src/ui/menus/start_menu.rs | 105 ++++++++++++++++++++++++++++--------- 6 files changed, 203 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef823e9..a8c0506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- New start menu with "New Game", "Settings", "Credits" and "Quit" buttons + +### Changed + +- Changed basic English (US) translations for start menu +- Changed basic Russian (RU) translations for start menu +- Changed basic German (DE) translations for start menu + ## [0.0.22] - 2023-11-03 ### Added diff --git a/Cargo.lock b/Cargo.lock index 1ef4433..51d1f82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -350,6 +350,17 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "bevy-ui-navigation" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea489c1999072befdd555259cd85038b1ed46d09577ad6d2b759f81a2ef4e281" +dependencies = [ + "bevy", + "bevy_mod_picking", + "non-empty-vec", +] + [[package]] name = "bevy_a11y" version = "0.11.3" @@ -600,6 +611,37 @@ dependencies = [ "encase_derive_impl", ] +[[package]] +name = "bevy_eventlistener" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233f729a5a2c7182f89b17c5d603cd604d095ee68d22c81b29be3cd30324f77d" +dependencies = [ + "bevy_eventlistener_core", + "bevy_eventlistener_derive", +] + +[[package]] +name = "bevy_eventlistener_core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd344e21cb61878a24169bff3f2e0bc5584df77b06732b5223a13a96a92dfe9" +dependencies = [ + "bevy", +] + +[[package]] +name = "bevy_eventlistener_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "407110082f2a861eb198c254226aa4a24c239fcff235f2aec41b0b0db5e8937f" +dependencies = [ + "bevy_eventlistener_core", + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "bevy_fluent" version = "0.7.0" @@ -806,6 +848,19 @@ dependencies = [ "glam", ] +[[package]] +name = "bevy_mod_picking" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce0837727f451ec62dad98a7bd35a8ff929e5a148bb0044122aba02683c09df" +dependencies = [ + "bevy", + "bevy_eventlistener", + "bevy_picking_core", + "bevy_picking_input", + "bevy_picking_ui", +] + [[package]] name = "bevy_pbr" version = "0.11.3" @@ -829,6 +884,36 @@ dependencies = [ "radsort", ] +[[package]] +name = "bevy_picking_core" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b10f159071accb3f17f86b70a2cafed27f96a828f155721d8f82706d5a65af" +dependencies = [ + "bevy", + "bevy_eventlistener", +] + +[[package]] +name = "bevy_picking_input" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc165dccadf85856cd713f1dabf5c845c7b6fd56e2c901a53f69f3693c5149" +dependencies = [ + "bevy", + "bevy_picking_core", +] + +[[package]] +name = "bevy_picking_ui" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1533a7723939a1dfad933da76a4cfb0d37eb15abd64519c0db2477f04de1ee8b" +dependencies = [ + "bevy", + "bevy_picking_core", +] + [[package]] name = "bevy_ptr" version = "0.11.3" @@ -2940,6 +3025,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "non-empty-vec" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceeba69aa8d4c53cdceeac8f17eb2656bb88b468bbe6c0889d34edfdea26ec8b" + [[package]] name = "notify" version = "6.1.1" @@ -4294,6 +4385,7 @@ version = "0.0.22" dependencies = [ "bevy", "bevy-inspector-egui", + "bevy-ui-navigation", "bevy_asset_loader", "bevy_common_assets", "bevy_fluent", diff --git a/Cargo.toml b/Cargo.toml index 27320bd..fcb7f00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] bevy = "0.11.3" bevy-inspector-egui = "0.20.0" +bevy-ui-navigation = "0.32.0" bevy_asset_loader = { version = "0.17.0", features = ["2d"] } bevy_common_assets = { version = "0.7.0", features = ["ron"] } bevy_fluent = "0.7.0" diff --git a/src/inputs/menu.rs b/src/inputs/menu.rs index 876736e..0768a71 100644 --- a/src/inputs/menu.rs +++ b/src/inputs/menu.rs @@ -3,15 +3,26 @@ use leafwing_input_manager::prelude::*; #[derive(Actionlike, PartialEq, Eq, Clone, Copy, Hash, Debug, Reflect)] pub enum MenuAction { + Select, Start, + Settings, Credits, + Quit, } pub fn menu_input_map() -> InputMap { let mut input_map = InputMap::::new([ - (KeyCode::Return, MenuAction::Start), + // Action Keys + (KeyCode::Return, MenuAction::Select), + // Hotkeys + (KeyCode::N, MenuAction::Start), + (KeyCode::S, MenuAction::Settings), (KeyCode::C, MenuAction::Credits), + (KeyCode::Q, MenuAction::Quit), ]); + // Action Buttons + input_map.insert(GamepadButtonType::South, MenuAction::Select); + // Hotkey Buttons input_map.insert(GamepadButtonType::Start, MenuAction::Start); input_map.insert(GamepadButtonType::North, MenuAction::Credits); diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 2067a58..8e6efd0 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -1,4 +1,5 @@ use bevy::prelude::*; +use bevy_ui_navigation::prelude::NavRequestSystem; pub mod events; pub mod states; @@ -133,6 +134,12 @@ impl Plugin for SystemsPlugin { Update, start_menu::menu_input_system.run_if(is_in_menu_state), ); + app.add_systems( + Update, + start_menu::menu_focus_system + .after(NavRequestSystem) + .run_if(is_in_menu_state), + ); app.add_systems( Update, diff --git a/src/ui/menus/start_menu.rs b/src/ui/menus/start_menu.rs index 4fdb7f8..7418d79 100644 --- a/src/ui/menus/start_menu.rs +++ b/src/ui/menus/start_menu.rs @@ -1,7 +1,9 @@ use bevy::{ + app::AppExit, audio::{PlaybackMode, Volume}, prelude::*, }; +use bevy_ui_navigation::{prelude::*, systems::InputMapping}; use fluent_content::Content; use leafwing_input_manager::prelude::{ActionState, InputManagerPlugin}; @@ -15,11 +17,30 @@ use crate::{ pub struct MenuPlugin; impl Plugin for MenuPlugin { fn build(&self, app: &mut App) { - app.add_plugins(InputManagerPlugin::::default()); + app.add_plugins(( + InputManagerPlugin::::default(), + DefaultNavigationPlugins, + )); } } -pub(crate) fn init_start_menu(mut commands: Commands, audios: Res) { +#[derive(Component)] +pub enum MenuButton { + NewGame, + Settings, + Credits, + Quit, +} + +pub(crate) fn init_start_menu( + mut commands: Commands, + audios: Res, + mut input_mapping: ResMut, +) { + // TODO: We might prefer to do this part ourselves... maybe. + input_mapping.keyboard_navigation = true; + // input_mapping.focus_follows_mouse = true; + commands.insert_resource(menu_input_map()); commands.insert_resource(ActionState::::default()); @@ -76,35 +97,33 @@ pub(crate) fn spawn_start_menu(mut commands: Commands, ui: Res, i18n: Name::new("Title"), )); - for string in ["new-game", "settings", "credits", "quit"] { - parent - .spawn(ButtonBundle { + for (string, marker) in [ + ("new-game", MenuButton::NewGame), + ("settings", MenuButton::Settings), + ("credits", MenuButton::Credits), + ("quit", MenuButton::Quit), + ] { + parent.spawn(( + TextBundle { + text: Text::from_section( + i18n.content(string).unwrap().to_ascii_uppercase(), + TextStyle { + font: ui.font.clone(), + font_size: 25.0, + color: Color::rgb_u8(0x00, 0x88, 0x88), + }, + ), style: Style { margin: UiRect::top(Val::Px(25.)), justify_content: JustifyContent::Center, align_items: AlignItems::Center, ..default() }, - background_color: BackgroundColor(Color::Rgba { - red: 0., - green: 0., - blue: 0., - alpha: 0., - }), ..default() - }) - .with_children(|parent| { - parent.spawn( - TextBundle::from_section( - i18n.content(string).unwrap().to_ascii_uppercase(), - TextStyle { - font: ui.font.clone(), - font_size: 25.0, - color: Color::rgb_u8(0x00, 0x88, 0x88), - }, - ), // DrawBlinkTimer(Timer::from_seconds(0.65, TimerMode::Repeating)), - ); - }); + }, // DrawBlinkTimer(Timer::from_seconds(0.65, TimerMode::Repeating)), + Focusable::default(), + marker, + )); } }); } @@ -113,7 +132,32 @@ pub(crate) fn menu_input_system( state: Res>, mut next_state: ResMut>, inputs: Res>, + mut requests: EventWriter, + mut buttons: Query<&mut MenuButton>, + mut events: EventReader, + mut exit: EventWriter, ) { + events.nav_iter().activated_in_query_foreach_mut( + &mut buttons, + |mut button| match &mut *button { + MenuButton::NewGame => { + next_state.set(GameState::GameCreate); + } + MenuButton::Settings => { + // start the game + } + MenuButton::Credits => { + next_state.set(GameState::Credits); + } + MenuButton::Quit => { + exit.send(AppExit); + } + }, + ); + + if inputs.just_pressed(MenuAction::Select) { + requests.send(NavRequest::Action); + } if inputs.just_pressed(MenuAction::Start) { next_state.set(GameState::GameCreate); } @@ -129,3 +173,16 @@ pub(crate) fn menu_input_system( } } } + +// TODO: This is very general. It can be moved to a less specific location. +pub(crate) fn menu_focus_system( + mut interaction_query: Query<(&Focusable, &mut Text), Changed>, +) { + for (focusable, mut text) in interaction_query.iter_mut() { + if let FocusState::Focused = focusable.state() { + text.sections[0].style.color = Color::rgb_u8(0x00, 0x88, 0x88); + } else { + text.sections[0].style.color = Color::rgb_u8(0x00, 0x44, 0x44); + } + } +} From 2246b34b9cb39361b49080aef9ca8ae746e85848 Mon Sep 17 00:00:00 2001 From: Thom Bruce Date: Mon, 6 Nov 2023 04:37:14 +0000 Subject: [PATCH 3/3] remove unfinished settings menu --- CHANGELOG.md | 2 +- src/inputs/menu.rs | 2 -- src/ui/menus/start_menu.rs | 5 ----- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8c0506..efbcb46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- New start menu with "New Game", "Settings", "Credits" and "Quit" buttons +- New start menu with "New Game", "Credits" and "Quit" buttons ### Changed diff --git a/src/inputs/menu.rs b/src/inputs/menu.rs index 0768a71..0cdd1d0 100644 --- a/src/inputs/menu.rs +++ b/src/inputs/menu.rs @@ -5,7 +5,6 @@ use leafwing_input_manager::prelude::*; pub enum MenuAction { Select, Start, - Settings, Credits, Quit, } @@ -16,7 +15,6 @@ pub fn menu_input_map() -> InputMap { (KeyCode::Return, MenuAction::Select), // Hotkeys (KeyCode::N, MenuAction::Start), - (KeyCode::S, MenuAction::Settings), (KeyCode::C, MenuAction::Credits), (KeyCode::Q, MenuAction::Quit), ]); diff --git a/src/ui/menus/start_menu.rs b/src/ui/menus/start_menu.rs index 7418d79..8cb3033 100644 --- a/src/ui/menus/start_menu.rs +++ b/src/ui/menus/start_menu.rs @@ -27,7 +27,6 @@ impl Plugin for MenuPlugin { #[derive(Component)] pub enum MenuButton { NewGame, - Settings, Credits, Quit, } @@ -99,7 +98,6 @@ pub(crate) fn spawn_start_menu(mut commands: Commands, ui: Res, i18n: for (string, marker) in [ ("new-game", MenuButton::NewGame), - ("settings", MenuButton::Settings), ("credits", MenuButton::Credits), ("quit", MenuButton::Quit), ] { @@ -143,9 +141,6 @@ pub(crate) fn menu_input_system( MenuButton::NewGame => { next_state.set(GameState::GameCreate); } - MenuButton::Settings => { - // start the game - } MenuButton::Credits => { next_state.set(GameState::Credits); }