Skip to content

Commit

Permalink
Merge pull request #179 from janhohenheim/menu-egui
Browse files Browse the repository at this point in the history
Former-commit-id: 921c16d
  • Loading branch information
janhohenheim authored Feb 21, 2023
2 parents 9d4e341 + 2506f41 commit 9f9fcd2
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 138 deletions.
Binary file removed assets/fonts/FiraSans-Bold.ttf
Binary file not shown.
7 changes: 0 additions & 7 deletions src/file_system_interaction/asset_loading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ impl Plugin for LoadingPlugin {
.add_plugin(ProgressPlugin::new(GameState::Loading).continue_to(GameState::Menu))
.add_loading_state(
LoadingState::new(GameState::Loading)
.with_collection::<FontAssets>()
.with_collection::<AudioAssets>()
.with_collection::<SceneAssets>()
.with_collection::<AnimationAssets>()
Expand All @@ -39,12 +38,6 @@ impl Plugin for LoadingPlugin {
// the following asset collections will be loaded during the State `GameState::InitialLoading`
// when done loading, they will be inserted as resources (see <https://github.com/NiklasEi/bevy_asset_loader>)

#[derive(AssetCollection, Resource)]
pub struct FontAssets {
#[asset(path = "fonts/FiraSans-Bold.ttf")]
pub fira_sans: Handle<Font>,
}

#[derive(AssetCollection, Resource)]
pub struct AudioAssets {
#[asset(path = "audio/walking.ogg")]
Expand Down
125 changes: 41 additions & 84 deletions src/menu.rs
Original file line number Diff line number Diff line change
@@ -1,103 +1,60 @@
use crate::file_system_interaction::asset_loading::FontAssets;
use crate::util::log_error::log_errors;
use crate::GameState;
use anyhow::Ok;
use anyhow::Result;
use bevy::prelude::*;
use bevy_egui::egui::FontFamily::Proportional;
use bevy_egui::egui::FontId;
use bevy_egui::egui::TextStyle::{Button, Heading};
use bevy_egui::{egui, EguiContext};

/// This plugin is responsible for the game menu
/// The menu is only drawn during the State `GameState::Menu` and is removed when that state is exited.
/// Because the Bevy UI situation is not quite mature yet, this work would ideally be done in egui instead,
/// so don't try to replicate this.
/// See [issue #13](https://github.com/janhohenheim/foxtrot/issues/13)
pub struct MenuPlugin;

impl Plugin for MenuPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ButtonColors>()
.add_system_set(SystemSet::on_enter(GameState::Menu).with_system(setup_menu))
.add_system_set(
SystemSet::on_update(GameState::Menu)
.with_system(click_play_button.pipe(log_errors)),
);
}
}

#[derive(Resource)]
struct ButtonColors {
normal: Color,
hovered: Color,
}

impl Default for ButtonColors {
fn default() -> Self {
ButtonColors {
normal: Color::rgb(0.15, 0.15, 0.15),
hovered: Color::rgb(0.25, 0.25, 0.25),
}
app.add_system_set(
SystemSet::on_update(GameState::Menu).with_system(setup_menu.pipe(log_errors)),
);
}
}

fn setup_menu(
mut commands: Commands,
font_assets: Res<FontAssets>,
button_colors: Res<ButtonColors>,
) {
commands
.spawn(ButtonBundle {
style: Style {
size: Size::new(Val::Px(120.0), Val::Px(50.0)),
margin: UiRect::all(Val::Auto),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
},
background_color: button_colors.normal.into(),
..default()
})
.insert(Name::new("Play Button"))
.with_children(|parent| {
parent
.spawn(TextBundle {
text: Text {
sections: vec![TextSection {
value: "Play".to_string(),
style: TextStyle {
font: font_assets.fira_sans.clone(),
font_size: 40.0,
color: Color::rgb(0.9, 0.9, 0.9),
},
}],
alignment: default(),
},
..default()
})
.insert(Name::new("Play Text"));
});
}

#[allow(clippy::type_complexity)]
fn click_play_button(
mut commands: Commands,
button_colors: Res<ButtonColors>,
mut egui_context: ResMut<EguiContext>,
mut state: ResMut<State<GameState>>,
mut interaction_query: Query<
(Entity, &Interaction, &mut BackgroundColor),
(Changed<Interaction>, With<Button>),
>,
) -> Result<()> {
for (button, interaction, mut color) in interaction_query.iter_mut() {
match *interaction {
Interaction::Clicked => {
commands.entity(button).despawn_recursive();
state.set(GameState::Playing)?;
}
Interaction::Hovered => {
*color = button_colors.hovered.into();
}
Interaction::None => {
*color = button_colors.normal.into();
}
}
}
get_menu_panel()
.show(egui_context.ctx_mut(), |ui| {
set_menu_style(ui.style_mut());
ui.vertical_centered_justified(|ui| {
ui.add_space(50.);
ui.heading("Foxtrot");
ui.separator();
ui.add_space(50.);
if ui.button("Play").clicked() {
state.set(GameState::Playing)?;
}
Ok(())
})
.inner
})
.inner?;
Ok(())
}

fn get_menu_panel() -> egui::CentralPanel {
egui::CentralPanel::default().frame(egui::Frame {
inner_margin: egui::style::Margin::same(60.),
..default()
})
}

fn set_menu_style(style: &mut egui::Style) {
style.text_styles = [
(Heading, FontId::new(30.0, Proportional)),
(Button, FontId::new(20.0, Proportional)),
]
.into();
style.visuals.widgets.noninteractive.fg_stroke.color = egui::Color32::from_gray(250);
}
106 changes: 59 additions & 47 deletions src/world_interaction/dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ pub use crate::world_interaction::dialog::resources::{
use crate::GameState;
use anyhow::{Context, Result};
use bevy::prelude::*;
use bevy_egui::egui::FontFamily::Proportional;
use bevy_egui::egui::FontId;
use bevy_egui::egui::TextStyle::{Body, Button};
use bevy_egui::{egui, EguiContext, EguiPlugin};
use serde::{Deserialize, Serialize};
use unicode_segmentation::UnicodeSegmentation;
Expand Down Expand Up @@ -106,48 +109,37 @@ fn show_dialog(
}
};
let current_page = current_dialog.fetch_current_page()?;
let dialog_text = create_dialog_rich_text(&current_page, *elapsed_time);

let dialog_size = egui::Vec2::new(500., 150.);
egui::Window::new("Dialog")
.anchor(egui::Align2::CENTER_BOTTOM, egui::Vec2::new(0., -30.))
.collapsible(false)
.resizable(false)
.title_bar(false)
.frame(egui::Frame {
fill: egui::Color32::from_black_alpha(230),
inner_margin: egui::style::Margin::same(25.),
rounding: egui::Rounding::same(30.0),
..default()
})
.show(egui_context.ctx_mut(), |ui| {
ui.set_width(dialog_size.x);
ui.set_height(dialog_size.y);
get_dialog_window().show(egui_context.ctx_mut(), |ui| {
// Get current context style
set_dialog_style(ui.style_mut());
let dialog_size = egui::Vec2::new(500., 150.);
ui.set_width(dialog_size.x);
ui.set_height(dialog_size.y);

let style = ui.style_mut();
style.visuals.button_frame = false;
ui.vertical(|ui| {
ui.add_space(5.);
ui.label(dialog_text.clone());
if dialog_text.text() == current_page.text {
ui.add_space(3.);
ui.separator();
ui.add_space(8.);
present_choices(
ui,
&mut commands,
&mut current_dialog,
&active_conditions,
&mut condition_writer,
&mut actions_frozen,
&actions.ui,
current_page.next_page,
&mut elapsed_time,
)
.expect("Failed to present dialog choices");
}
});
let dialog_text = create_dialog_rich_text(&current_page, *elapsed_time);
ui.vertical(|ui| {
ui.add_space(5.);
ui.label(&dialog_text);
if dialog_text == current_page.text {
ui.add_space(3.);
ui.separator();
ui.add_space(8.);
present_choices(
ui,
&mut commands,
&mut current_dialog,
&active_conditions,
&mut condition_writer,
&mut actions_frozen,
&actions.ui,
current_page.next_page,
&mut elapsed_time,
)
.expect("Failed to present dialog choices");
}
});
});
let dt_speed_multiplier = if actions.ui.speed_up_dialog { 4. } else { 1. };
*elapsed_time += time.delta_seconds() * dt_speed_multiplier;
Ok(())
Expand Down Expand Up @@ -220,18 +212,38 @@ fn present_choices(
Ok(())
}

fn create_dialog_rich_text(page: &Page, elapsed_time: f32) -> egui::RichText {
fn get_dialog_window() -> egui::Window<'static> {
egui::Window::new("Dialog")
.anchor(egui::Align2::CENTER_BOTTOM, egui::Vec2::new(0., -30.))
.collapsible(false)
.resizable(false)
.title_bar(false)
.frame(egui::Frame {
fill: egui::Color32::from_black_alpha(230),
inner_margin: egui::style::Margin::same(25.),
rounding: egui::Rounding::same(30.0),
..default()
})
}

fn set_dialog_style(style: &mut egui::Style) {
style.text_styles = [
(Body, FontId::new(16.0, Proportional)),
(Button, FontId::new(14.0, Proportional)),
]
.into();
style.visuals.button_frame = false;
style.visuals.widgets.noninteractive.fg_stroke.color = egui::Color32::from_gray(250);
}

fn create_dialog_rich_text(page: &Page, elapsed_time: f32) -> String {
const BASE_LETTERS_PER_SECOND: f32 = 60.;
let letters_to_display = (BASE_LETTERS_PER_SECOND * page.talking_speed * elapsed_time) as usize;
let text: String = page.text.graphemes(true).take(letters_to_display).collect();
egui::RichText::new(text)
.color(egui::Color32::from_gray(250))
.size(16.)
page.text.graphemes(true).take(letters_to_display).collect()
}

fn create_choice_rich_text(index: usize, text: &str) -> egui::RichText {
let text = format!("{}. {}", index + 1, text);
egui::RichText::new(text).size(14.)
fn create_choice_rich_text(index: usize, text: &str) -> String {
format!("{}. {}", index + 1, text)
}

fn was_just_picked(current_dialog: &CurrentDialog, choice_id: &ConditionId) -> bool {
Expand Down

0 comments on commit 9f9fcd2

Please sign in to comment.