Update menu
This commit is contained in:
parent
b3c0bf53a4
commit
614e1fc865
5 changed files with 487 additions and 10 deletions
|
@ -1,12 +1,13 @@
|
|||
mod plugins;
|
||||
pub mod themes;
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)]
|
||||
enum AppState {
|
||||
#[default]
|
||||
MainView,
|
||||
Setting,
|
||||
#[default]
|
||||
Menu,
|
||||
List,
|
||||
Graph
|
||||
}
|
||||
|
@ -39,3 +40,12 @@ fn main() {
|
|||
.run();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Generic system that takes a component as a parameter, and will despawn all entities with that component
|
||||
fn despawn_screen<T: Component>(to_despawn: Query<Entity, With<T>>, mut commands: Commands) {
|
||||
for entity in &to_despawn {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
|
@ -5,15 +5,20 @@ use bevy::{
|
|||
prelude::*,
|
||||
};
|
||||
|
||||
use super::{
|
||||
use crate::{
|
||||
despawn_screen,
|
||||
themes::dark::{self, NORMAL_BUTTON},
|
||||
DisplayQuality,
|
||||
GameState,
|
||||
AppState,
|
||||
Volume,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)]
|
||||
enum SettingsState {
|
||||
enum MenuState {
|
||||
Main,
|
||||
Settings,
|
||||
SettingsDisplay,
|
||||
SettingsVolume,
|
||||
#[default]
|
||||
Disabled,
|
||||
}
|
||||
|
@ -21,11 +26,450 @@ enum SettingsState {
|
|||
pub fn menu_plugin(app: &mut App) {
|
||||
app
|
||||
.init_state::<MenuState>()
|
||||
.add_systems(OnEnter(GamsState::Settin), menu_setup)
|
||||
.add_systems(OnEnter(AppState::Menu), menu_setup)
|
||||
.add_systems(OnEnter(MenuState::Main), main_menu_setup)
|
||||
.add_systems(OnExit(MenuState::Main), despawn_screen::<OnMainMenuScreen>)
|
||||
.add_ssytems(OnEnter(MenuState::Settings), settings_menu_setup)
|
||||
.add_systems(OnExit(MenuState::Main), crate::despawn_screen::<OnMainMenuScreen>)
|
||||
.add_systems(OnEnter(MenuState::Settings), settings_menu_setup)
|
||||
.add_systems(
|
||||
OnExit(MenuState::Setting)
|
||||
OnExit(MenuState::Settings),
|
||||
crate::despawn_screen::<OnSettingsMenuScreen>
|
||||
)
|
||||
}
|
||||
.add_systems(OnEnter(MenuState::SettingsDisplay), display_settings_menu_setup)
|
||||
.add_systems(
|
||||
Update,
|
||||
(setting_button::<DisplayQuality>.run_if(in_state(MenuState::SettingsDisplay)))
|
||||
)
|
||||
.add_systems(
|
||||
OnExit(MenuState::SettingsDisplay),
|
||||
despawn_screen::<OnDisplaySettingsMenuScreen>
|
||||
)
|
||||
.add_systems(OnEnter(MenuState::SettingsVolume), sound_settings_menu_setup)
|
||||
.add_systems(
|
||||
Update,
|
||||
(setting_button::<Volume>.run_if(in_state(MenuState::SettingsVolume)))
|
||||
)
|
||||
.add_systems(
|
||||
OnExit(MenuState::SettingsVolume),
|
||||
despawn_screen::<OnSoundSettingsMenuScreen>
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
(menu_action, button_system).run_if(in_state(AppState::Menu))
|
||||
)
|
||||
|
||||
;
|
||||
}
|
||||
#[derive(Component)]
|
||||
struct OnMainMenuScreen;
|
||||
|
||||
#[derive(Component)]
|
||||
struct OnSettingsMenuScreen;
|
||||
|
||||
#[derive(Component)]
|
||||
struct OnDisplaySettingsMenuScreen;
|
||||
|
||||
#[derive(Component)]
|
||||
struct OnSoundSettingsMenuScreen;
|
||||
|
||||
#[derive(Component)]
|
||||
struct SelectedOption;
|
||||
|
||||
|
||||
#[derive(Component)]
|
||||
enum MenuButtonAction {
|
||||
BackToMainView,
|
||||
BackToMainMenu,
|
||||
BackToSettings,
|
||||
Settings,
|
||||
SettingsDisplay,
|
||||
SettingsSound,
|
||||
Quit,
|
||||
}
|
||||
|
||||
fn button_system(
|
||||
mut interaction_query: Query<(&Interaction, &mut BackgroundColor, Option<&SelectedOption>),(Changed<Interaction>, With<Button>)>
|
||||
) {
|
||||
for (interaction, mut background_color, selected) in &mut interaction_query {
|
||||
*background_color = match (*interaction, selected) {
|
||||
(Interaction::Pressed, _) | (_, Some(_)) => dark::PRESSED_BUTTON.into(),
|
||||
(Interaction::Hovered, None) => dark::HOVERED_BUTTON.into(),
|
||||
(Interaction::None, None) => dark::NORMAL_BUTTON.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn setting_button<T: Resource + Component + PartialEq + Copy> (
|
||||
interaction_query: Query<(&Interaction, &T, Entity), (Changed<Interaction>, With<Button>)>,
|
||||
selected_query: Single<(Entity, &mut BackgroundColor), With<SelectedOption>>,
|
||||
mut commands: Commands,
|
||||
mut setting: ResMut<T>
|
||||
) {
|
||||
let (previous_button, mut previous_button_color) = selected_query.into_inner();
|
||||
for (interaction, button_setting, entity) in &interaction_query {
|
||||
if *interaction == Interaction::Pressed && *setting != *button_setting {
|
||||
*previous_button_color = NORMAL_BUTTON.into();
|
||||
commands.entity(previous_button).remove::<SelectedOption>();
|
||||
commands.entity(entity).insert(SelectedOption);
|
||||
*setting = *button_setting
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn menu_setup(mut menu_state: ResMut<NextState<MenuState>>) {
|
||||
menu_state.set(MenuState::Main);
|
||||
}
|
||||
|
||||
fn main_menu_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let button_node = Node {
|
||||
width: Val::Px(300.0),
|
||||
height: Val::Px(65.0),
|
||||
margin: UiRect::all(Val::Px(20.0)),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
};
|
||||
let button_icon_node = Node {
|
||||
width: Val::Px(30.0),
|
||||
position_type: PositionType::Absolute,
|
||||
left: Val::Px(10.0),
|
||||
..default()
|
||||
};
|
||||
let button_text_font = TextFont {
|
||||
font_size: 33.0,
|
||||
..default()
|
||||
};
|
||||
let right_icon = asset_server.load("textures/Game Icons/right.png");
|
||||
let wrench_icon = asset_server.load("textures/Game Icons/wrench.png");
|
||||
let exit_icon = asset_server.load("textures/Game Icons/exitRight.png");
|
||||
|
||||
|
||||
commands.spawn((
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
OnMainMenuScreen,
|
||||
children![(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(dark::BACKGROUND_COLOR.into()),
|
||||
children![
|
||||
(
|
||||
Text::new("Progress Pile"),
|
||||
TextFont {
|
||||
font_size: 67.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(dark::TEXT_COLOR),
|
||||
Node {
|
||||
margin: UiRect::all(Val::Px(50.0)),
|
||||
..default()
|
||||
},
|
||||
),
|
||||
(
|
||||
Button,
|
||||
button_node.clone(),
|
||||
BackgroundColor(dark::NORMAL_BUTTON),
|
||||
MenuButtonAction::BackToMainView,
|
||||
children![
|
||||
(ImageNode::new(right_icon), button_icon_node.clone()),
|
||||
(
|
||||
Text::new("Back to main window"),
|
||||
button_text_font.clone(),
|
||||
TextColor(dark::TEXT_COLOR),
|
||||
)
|
||||
]
|
||||
),
|
||||
(
|
||||
Button,
|
||||
button_node.clone(),
|
||||
BackgroundColor(dark::NORMAL_BUTTON),
|
||||
MenuButtonAction::Settings,
|
||||
children![
|
||||
(ImageNode::new(wrench_icon), button_icon_node.clone()),
|
||||
(
|
||||
Text::new("Settings"),
|
||||
button_text_font.clone(),
|
||||
TextColor(dark::TEXT_COLOR),
|
||||
)
|
||||
]
|
||||
),
|
||||
(
|
||||
Button,
|
||||
button_node.clone(),
|
||||
BackgroundColor(dark::NORMAL_BUTTON),
|
||||
MenuButtonAction::Quit,
|
||||
children![
|
||||
(ImageNode::new(exit_icon), button_icon_node.clone()),
|
||||
(
|
||||
Text::new("Quit"),
|
||||
button_text_font.clone(),
|
||||
TextColor(dark::TEXT_COLOR),
|
||||
)
|
||||
]
|
||||
),
|
||||
]
|
||||
)]
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
fn settings_menu_setup(mut commands: Commands) {
|
||||
let button_node = Node {
|
||||
width: Val::Px(200.0),
|
||||
height: Val::Px(65.0),
|
||||
margin: UiRect::all(Val::Px(20.0)),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
};
|
||||
|
||||
let button_text_style = (
|
||||
TextFont {
|
||||
font_size: 33.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(dark::TEXT_COLOR),
|
||||
);
|
||||
|
||||
commands.spawn((
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
OnSettingsMenuScreen,
|
||||
children![(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(CRIMSON.into()),
|
||||
Children::spawn(SpawnIter(
|
||||
[
|
||||
(MenuButtonAction::SettingsDisplay, "Display"),
|
||||
(MenuButtonAction::SettingsSound, "Sound"),
|
||||
(MenuButtonAction::BackToMainMenu, "Back"),
|
||||
]
|
||||
.into_iter()
|
||||
.map(move |(action, text)| {
|
||||
(
|
||||
Button,
|
||||
button_node.clone(),
|
||||
BackgroundColor(NORMAL_BUTTON),
|
||||
action,
|
||||
children![(Text::new(text), button_text_style.clone())],
|
||||
)
|
||||
})
|
||||
))
|
||||
)],
|
||||
));
|
||||
}
|
||||
|
||||
fn display_settings_menu_setup(mut commands: Commands, display_quality: Res<DisplayQuality>) {
|
||||
fn button_node() -> Node {
|
||||
Node {
|
||||
width: Val::Px(200.0),
|
||||
height: Val::Px(65.0),
|
||||
margin: UiRect::all(Val::Px(20.0)),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
}
|
||||
}
|
||||
fn button_text_style() -> impl Bundle {
|
||||
(
|
||||
TextFont {
|
||||
font_size: 33.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(dark::TEXT_COLOR),
|
||||
)
|
||||
}
|
||||
|
||||
let display_quality = *display_quality;
|
||||
commands.spawn((
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
OnDisplaySettingsMenuScreen,
|
||||
children![(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(CRIMSON.into()),
|
||||
children![
|
||||
// Create a new `Node`, this time not setting its `flex_direction`. It will
|
||||
// use the default value, `FlexDirection::Row`, from left to right.
|
||||
(
|
||||
Node {
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(CRIMSON.into()),
|
||||
Children::spawn((
|
||||
// Display a label for the current setting
|
||||
Spawn((Text::new("Display Quality"), button_text_style())),
|
||||
SpawnWith(move |parent: &mut ChildSpawner| {
|
||||
for quality_setting in [
|
||||
DisplayQuality::Low,
|
||||
DisplayQuality::Medium,
|
||||
DisplayQuality::High,
|
||||
] {
|
||||
let mut entity = parent.spawn((
|
||||
Button,
|
||||
Node {
|
||||
width: Val::Px(150.0),
|
||||
height: Val::Px(65.0),
|
||||
..button_node()
|
||||
},
|
||||
BackgroundColor(NORMAL_BUTTON),
|
||||
quality_setting,
|
||||
children![(
|
||||
Text::new(format!("{quality_setting:?}")),
|
||||
button_text_style(),
|
||||
)],
|
||||
));
|
||||
if display_quality == quality_setting {
|
||||
entity.insert(SelectedOption);
|
||||
}
|
||||
}
|
||||
})
|
||||
))
|
||||
),
|
||||
// Display the back button to return to the settings screen
|
||||
(
|
||||
Button,
|
||||
button_node(),
|
||||
BackgroundColor(NORMAL_BUTTON),
|
||||
MenuButtonAction::BackToSettings,
|
||||
children![(Text::new("Back"), button_text_style())]
|
||||
)
|
||||
]
|
||||
)],
|
||||
));
|
||||
}
|
||||
|
||||
fn sound_settings_menu_setup(mut commands: Commands, volume: Res<Volume>) {
|
||||
let button_node = Node {
|
||||
width: Val::Px(200.0),
|
||||
height: Val::Px(65.0),
|
||||
margin: UiRect::all(Val::Px(20.0)),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
};
|
||||
let button_text_style = (
|
||||
TextFont {
|
||||
font_size: 33.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(dark::TEXT_COLOR),
|
||||
);
|
||||
|
||||
let volume = *volume;
|
||||
let button_node_clone = button_node.clone();
|
||||
commands.spawn((
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
OnSoundSettingsMenuScreen,
|
||||
children![(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(CRIMSON.into()),
|
||||
children![
|
||||
(
|
||||
Node {
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(CRIMSON.into()),
|
||||
Children::spawn((
|
||||
Spawn((Text::new("Volume"), button_text_style.clone())),
|
||||
SpawnWith(move |parent: &mut ChildSpawner| {
|
||||
for volume_setting in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] {
|
||||
let mut entity = parent.spawn((
|
||||
Button,
|
||||
Node {
|
||||
width: Val::Px(30.0),
|
||||
height: Val::Px(65.0),
|
||||
..button_node_clone.clone()
|
||||
},
|
||||
BackgroundColor(NORMAL_BUTTON),
|
||||
Volume(volume_setting),
|
||||
));
|
||||
if volume == Volume(volume_setting) {
|
||||
entity.insert(SelectedOption);
|
||||
}
|
||||
}
|
||||
})
|
||||
))
|
||||
),
|
||||
(
|
||||
Button,
|
||||
button_node,
|
||||
BackgroundColor(NORMAL_BUTTON),
|
||||
MenuButtonAction::BackToSettings,
|
||||
children![(Text::new("Back"), button_text_style)]
|
||||
)
|
||||
]
|
||||
)],
|
||||
));
|
||||
}
|
||||
|
||||
fn menu_action(
|
||||
interaction_query: Query<
|
||||
(&Interaction, &MenuButtonAction),
|
||||
(Changed<Interaction>, With<Button>),
|
||||
>,
|
||||
mut app_exit_events: EventWriter<AppExit>,
|
||||
mut menu_state: ResMut<NextState<MenuState>>,
|
||||
mut game_state: ResMut<NextState<AppState>>,
|
||||
) {
|
||||
for (interaction, menu_button_action) in &interaction_query {
|
||||
if *interaction == Interaction::Pressed {
|
||||
match menu_button_action {
|
||||
MenuButtonAction::Quit => {
|
||||
app_exit_events.write(AppExit::Success);
|
||||
}
|
||||
MenuButtonAction::BackToMainView => {
|
||||
game_state.set(AppState::MainView);
|
||||
menu_state.set(MenuState::Disabled);
|
||||
}
|
||||
MenuButtonAction::Settings => menu_state.set(MenuState::Settings),
|
||||
MenuButtonAction::SettingsDisplay => {
|
||||
menu_state.set(MenuState::SettingsDisplay);
|
||||
}
|
||||
MenuButtonAction::SettingsSound => {
|
||||
menu_state.set(MenuState::SettingsVolume);
|
||||
}
|
||||
MenuButtonAction::BackToMainMenu => menu_state.set(MenuState::Main),
|
||||
MenuButtonAction::BackToSettings => {
|
||||
menu_state.set(MenuState::Settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
7
progress-pile-app/src/themes/dark.rs
Normal file
7
progress-pile-app/src/themes/dark.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
pub const TEXT_COLOR: Color = Color::srgb(0.9, 0.9, 0.9);
|
||||
pub const BACKGROUND_COLOR: Color = Color::srgb(0.1, 0.1, 0.1);
|
||||
pub const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
|
||||
pub const HOVERED_BUTTON: Color= Color::srgb(0.25, 0.25, 0.25);
|
||||
pub const PRESSED_BUTTON: Color = Color::srgb(0.5,0.5, 0.5);
|
4
progress-pile-app/src/themes/light.rs
Normal file
4
progress-pile-app/src/themes/light.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
const TEXT_COLOR: Color = Color::srgb(0.1, 0.1, 0.1);
|
||||
const BACKGROUND_COLOR: Color = Color::srgb(0.9, 0.9, 0.9);
|
12
progress-pile-app/src/themes/mod.rs
Normal file
12
progress-pile-app/src/themes/mod.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
pub mod dark;
|
||||
pub mod light;
|
||||
|
||||
#[derive(Resource, Debug, Component, Default, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum UiTheme {
|
||||
Light,
|
||||
Dark,
|
||||
#[default]
|
||||
Default,
|
||||
}
|
Loading…
Add table
Reference in a new issue