add main menu

This commit is contained in:
Schrottkatze 2024-05-10 23:32:11 +02:00
parent 0d945de135
commit e5f87736df
Signed by: schrottkatze
SSH key fingerprint: SHA256:hXb3t1vINBFCiDCmhRABHX5ocdbLiKyCdKI4HK2Rbbc
5 changed files with 230 additions and 66 deletions

View file

@ -10,7 +10,7 @@ use rand::Rng;
use crate::{ use crate::{
player::{LifeChangeEvent, Player}, player::{LifeChangeEvent, Player},
scene::SceneObj, scene::SceneObj,
METER, GameState, GameplaySet, METER,
}; };
#[derive(Component)] #[derive(Component)]
@ -28,7 +28,7 @@ pub fn spawner_plugin(app: &mut App) {
app.add_plugins(EntropyPlugin::<WyRand>::default()) app.add_plugins(EntropyPlugin::<WyRand>::default())
.add_event::<CrateDropEvent>() .add_event::<CrateDropEvent>()
.add_event::<CrateCollision>() .add_event::<CrateCollision>()
.add_systems(Startup, add_timer) .add_systems(OnEnter(GameState::InGame), add_timer.in_set(GameplaySet))
.add_systems( .add_systems(
Update, Update,
( (
@ -37,7 +37,8 @@ pub fn spawner_plugin(app: &mut App) {
crate_collisions, crate_collisions,
delete_on_env_coll, delete_on_env_coll,
player_coll, player_coll,
), )
.in_set(GameplaySet),
); );
} }

57
src/game_state.rs Normal file
View file

@ -0,0 +1,57 @@
use bevy::{prelude::*, time::Stopwatch};
use crate::GameplaySet;
#[derive(Component)]
pub struct GameState {
game_time: Stopwatch,
}
#[derive(Component)]
struct StateText;
pub fn state_and_ui_plugin(app: &mut App) {
app.add_systems(
OnEnter(crate::GameState::InGame),
(setup_gamestate, setup_ui).in_set(GameplaySet),
)
.add_systems(Update, (update_time).in_set(GameplaySet));
}
fn setup_gamestate(mut commands: Commands) {
commands.spawn(GameState {
game_time: Stopwatch::new(),
});
}
fn setup_ui(mut commands: Commands) {
commands.spawn((
TextBundle::from_sections([
TextSection::new("Score: ", TextStyle::default()),
TextSection::new("0", TextStyle::default()),
])
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(5.0),
right: Val::Px(5.0),
..default()
}),
StateText,
));
}
fn update_time(
mut state_txt: Query<&mut Text, With<StateText>>,
mut game_state: Query<&mut GameState>,
time: Res<Time>,
) {
let mut txt = state_txt.single_mut();
let time = game_state
.single_mut()
.game_time
.tick(time.delta())
.elapsed_secs()
* 100.;
txt.sections[1].value = format!("{time:.0}");
}

View file

@ -1,12 +1,24 @@
use crate::main_menu::main_menu_plugin;
use bevy::{ use bevy::{
prelude::*, prelude::*,
sprite::{MaterialMesh2dBundle, Mesh2dHandle}, sprite::{MaterialMesh2dBundle, Mesh2dHandle},
}; };
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
use drops::spawner_plugin; use drops::spawner_plugin;
use main_menu::MainMenuSet;
use player::LifeChangeEvent; use player::LifeChangeEvent;
const METER: f32 = 48.; const METER: f32 = 48.;
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
struct GameplaySet;
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
enum GameState {
MainMenu,
InGame,
}
fn main() { fn main() {
let mut rapier_config = RapierConfiguration::new(METER * 2.5); let mut rapier_config = RapierConfiguration::new(METER * 2.5);
rapier_config.timestep_mode = TimestepMode::Variable { rapier_config.timestep_mode = TimestepMode::Variable {
@ -18,14 +30,24 @@ fn main() {
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
// this does things with gravity // this does things with gravity
.insert_resource(rapier_config) .insert_resource(rapier_config)
.insert_state(GameState::MainMenu)
.add_plugins(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(METER)) .add_plugins(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(METER))
.add_plugins(RapierDebugRenderPlugin::default()) .add_plugins(RapierDebugRenderPlugin::default())
.add_plugins(( .add_plugins((
main_menu_plugin,
spawner_plugin, spawner_plugin,
game_state::state_and_ui_plugin, game_state::state_and_ui_plugin,
player::player_plugin, player::player_plugin,
)) ))
.add_systems(Startup, (scene::setup_scene, setup_cam)) .add_systems(Startup, setup_cam)
.add_systems(OnEnter(GameState::InGame), scene::setup_scene)
.configure_sets(
Update,
(
GameplaySet.run_if(in_state(GameState::InGame)),
MainMenuSet.run_if(in_state(GameState::MainMenu)),
),
)
.run() .run()
} }
@ -37,64 +59,7 @@ fn setup_cam(mut commands: Commands) {
} }
mod drops; mod drops;
mod game_state;
mod main_menu;
mod player; mod player;
mod scene; mod scene;
mod game_state {
use bevy::{prelude::*, time::Stopwatch};
#[derive(Component)]
pub struct GameState {
score: u32,
game_time: Stopwatch,
}
#[derive(Component)]
struct StateText;
pub fn state_and_ui_plugin(app: &mut App) {
app.add_systems(Startup, (setup_gamestate, setup_ui))
.add_systems(Update, (update_time));
}
fn setup_gamestate(mut commands: Commands) {
commands.spawn(GameState {
score: 0,
game_time: Stopwatch::new(),
});
}
fn setup_ui(mut commands: Commands) {
commands.spawn((
TextBundle::from_sections([
TextSection::new("Score: ", TextStyle::default()),
TextSection::new("0", TextStyle::default()),
TextSection::new("\n", TextStyle::default()),
TextSection::new("Time: ", TextStyle::default()),
TextSection::new("0", TextStyle::default()),
TextSection::new("s", TextStyle::default()),
])
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(5.0),
right: Val::Px(5.0),
..default()
}),
StateText,
));
}
fn update_time(
mut state_txt: Query<&mut Text, With<StateText>>,
mut game_state: Query<&mut GameState>,
time: Res<Time>,
) {
let mut txt = state_txt.single_mut();
let time = game_state
.single_mut()
.game_time
.tick(time.delta())
.elapsed_secs();
txt.sections[4].value = format!("{time:.2}");
}
}

135
src/main_menu.rs Normal file
View file

@ -0,0 +1,135 @@
use bevy::{app::AppExit, prelude::*};
use crate::GameState;
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
pub struct MainMenuSet;
pub fn main_menu_plugin(app: &mut App) {
app.add_systems(
OnEnter(GameState::MainMenu),
setup_main_menu_ui.in_set(MainMenuSet),
)
.add_systems(
Update,
(button_color_system, button_action).in_set(MainMenuSet),
)
.add_systems(OnExit(GameState::MainMenu), exit_menu.in_set(MainMenuSet));
}
#[derive(Component)]
struct MainMenu;
#[derive(Component)]
enum MenuButtonAction {
Play,
Quit,
}
fn setup_main_menu_ui(mut commands: Commands) {
commands
.spawn((
NodeBundle {
style: Style {
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
flex_direction: FlexDirection::Column,
width: Val::Percent(100.0),
height: Val::Percent(100.0),
..default()
},
..default()
},
MainMenu,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
"Ev4der",
TextStyle {
font_size: 144.,
..default()
},
));
let btn_style = Style {
width: Val::Px(300.0),
height: Val::Px(80.0),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let btn_txt_style = TextStyle {
font_size: 64.,
..default()
};
parent
.spawn((
ButtonBundle {
style: btn_style.clone(),
background_color: Color::rgb(0.3, 0.3, 0.3).into(),
..default()
},
MenuButtonAction::Play,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section("Play!", btn_txt_style.clone()));
});
parent
.spawn((
ButtonBundle {
style: btn_style.clone(),
background_color: Color::rgb(0.3, 0.3, 0.3).into(),
..default()
},
MenuButtonAction::Quit,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section("Quit :(", btn_txt_style.clone()));
});
});
}
fn exit_menu(to_despawn: Query<Entity, With<MainMenu>>, mut commands: Commands) {
for e in &to_despawn {
commands.entity(e).despawn_recursive();
}
}
fn button_color_system(
mut interaction_query: Query<
(&Interaction, &mut BackgroundColor),
(Changed<Interaction>, With<Button>),
>,
) {
for (interaction, mut color) in &mut interaction_query {
*color = match *interaction {
Interaction::Pressed => Color::rgb(0.4, 0.4, 0.4).into(),
Interaction::Hovered => Color::rgb(0.2, 0.2, 0.2).into(),
Interaction::None => Color::rgb(0.3, 0.3, 0.3).into(),
}
}
}
fn button_action(
interaction_query: Query<
(&Interaction, &MenuButtonAction),
(Changed<Interaction>, With<Button>),
>,
mut ev_exit: EventWriter<AppExit>,
mut game_state: ResMut<NextState<GameState>>,
) {
for (interaction, action) in &interaction_query {
if *interaction == Interaction::Pressed {
match action {
MenuButtonAction::Quit => {
ev_exit.send(AppExit);
}
MenuButtonAction::Play => {
game_state.set(GameState::InGame);
}
}
}
}
}

View file

@ -5,7 +5,7 @@ use bevy::{
}; };
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
use crate::METER; use crate::{GameState, GameplaySet, METER};
#[derive(Component)] #[derive(Component)]
pub struct Player { pub struct Player {
@ -24,8 +24,14 @@ const DEFAULT_LIVES: u8 = 5;
pub fn player_plugin(app: &mut App) { pub fn player_plugin(app: &mut App) {
app.add_event::<LifeChangeEvent>() app.add_event::<LifeChangeEvent>()
.add_systems(Startup, (add_player, add_lives_ui)) .add_systems(
.add_systems(Update, (move_player, player_ground_collision, update_lives)); OnEnter(GameState::InGame),
(add_player, add_lives_ui).in_set(GameplaySet),
)
.add_systems(
Update,
(move_player, player_ground_collision, update_lives).in_set(GameplaySet),
);
} }
pub fn add_player( pub fn add_player(