2025-01-01 18:52:19 +02:00
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
|
|
use bevy::{
|
|
|
|
|
asset::{io::AssetSourceId, AssetPath},
|
2025-01-01 20:03:58 +02:00
|
|
|
ecs::query::QueryIter,
|
2025-01-01 18:52:19 +02:00
|
|
|
prelude::*,
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-01 20:03:58 +02:00
|
|
|
use crate::{
|
|
|
|
|
gamepad::GamepadOne, player::Player, score::Score, GameStartEvent, GameState, START_POSITION,
|
|
|
|
|
};
|
2025-01-01 18:52:19 +02:00
|
|
|
|
|
|
|
|
pub struct HUDPlugin;
|
|
|
|
|
|
|
|
|
|
impl Plugin for HUDPlugin {
|
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
|
app.add_systems(Startup, setup);
|
|
|
|
|
app.add_systems(Update, start_button_logic);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|
|
|
|
// let asset_path: AssetPath = "embedded://avoid_rs/assets/Xolonium-Regular.ttf".into();
|
|
|
|
|
let asset_path: AssetPath = "Xolonium-Regular.ttf".into();
|
|
|
|
|
let font: Handle<_> = asset_server.load(asset_path);
|
|
|
|
|
|
|
|
|
|
// let path = Path::new("avoid-rs").join("assets/Xolonium-Regular.ttf");
|
|
|
|
|
// let source = AssetSourceId::from("embedded");
|
|
|
|
|
// let asset_path = AssetPath::from_path(&path).with_source(source);
|
|
|
|
|
// let font: Handle<_> = asset_server.load(asset_path);
|
|
|
|
|
|
|
|
|
|
// score UI, top middle
|
|
|
|
|
commands
|
|
|
|
|
.spawn(NodeBundle {
|
|
|
|
|
style: Style {
|
|
|
|
|
width: Val::Percent(100.0),
|
|
|
|
|
align_items: AlignItems::Center,
|
|
|
|
|
justify_content: JustifyContent::Center,
|
|
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
..Default::default()
|
|
|
|
|
})
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
parent.spawn((
|
|
|
|
|
TextBundle::from_section(
|
|
|
|
|
"0",
|
|
|
|
|
TextStyle {
|
|
|
|
|
font: font.clone(),
|
|
|
|
|
font_size: 64.0,
|
|
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.with_text_justify(JustifyText::Center)
|
|
|
|
|
.with_style(Style {
|
|
|
|
|
justify_content: JustifyContent::Center,
|
|
|
|
|
align_items: AlignItems::Center,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}),
|
|
|
|
|
ScoreText,
|
|
|
|
|
));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
commands
|
|
|
|
|
.spawn(NodeBundle {
|
|
|
|
|
style: Style {
|
|
|
|
|
width: Val::Percent(100.0),
|
|
|
|
|
height: Val::Percent(100.0),
|
|
|
|
|
align_items: AlignItems::Center,
|
|
|
|
|
justify_content: JustifyContent::Center,
|
|
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
..Default::default()
|
|
|
|
|
})
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
// game info, middle of the screen
|
|
|
|
|
parent.spawn((
|
|
|
|
|
TextBundle::from_section(
|
|
|
|
|
"Dodge the creeps!",
|
|
|
|
|
TextStyle {
|
|
|
|
|
font: font.clone(),
|
|
|
|
|
font_size: 64.0,
|
|
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.with_text_justify(JustifyText::Center)
|
|
|
|
|
.with_style(Style {
|
|
|
|
|
justify_content: JustifyContent::Center,
|
|
|
|
|
align_items: AlignItems::Center,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}),
|
|
|
|
|
IntroMessageText,
|
|
|
|
|
StartMenuUI,
|
|
|
|
|
));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
commands
|
|
|
|
|
.spawn(NodeBundle {
|
|
|
|
|
style: Style {
|
|
|
|
|
width: Val::Percent(100.0),
|
|
|
|
|
height: Val::Percent(90.0),
|
|
|
|
|
align_items: AlignItems::End,
|
|
|
|
|
justify_content: JustifyContent::Center,
|
|
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
..Default::default()
|
|
|
|
|
})
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
parent
|
|
|
|
|
.spawn((
|
|
|
|
|
ButtonBundle {
|
|
|
|
|
style: Style {
|
|
|
|
|
width: Val::Px(200.0),
|
|
|
|
|
height: Val::Px(100.0),
|
|
|
|
|
border: UiRect::all(Val::Px(5.0)),
|
|
|
|
|
// horizontally center child text
|
|
|
|
|
justify_content: JustifyContent::Center,
|
|
|
|
|
// vertically center child text
|
|
|
|
|
align_items: AlignItems::Center,
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
border_color: BorderColor(Color::BLACK),
|
|
|
|
|
border_radius: BorderRadius::all(Val::Px(10.0)),
|
|
|
|
|
background_color: Color::srgb(0.15, 0.15, 0.15).into(),
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
StartButton,
|
|
|
|
|
StartMenuUI,
|
|
|
|
|
))
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
parent.spawn(TextBundle::from_section(
|
|
|
|
|
"Start",
|
|
|
|
|
TextStyle {
|
|
|
|
|
font: font.clone(),
|
|
|
|
|
font_size: 64.0,
|
|
|
|
|
color: Color::srgb(0.9, 0.9, 0.9),
|
|
|
|
|
},
|
|
|
|
|
));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
pub struct ScoreText;
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
pub struct IntroMessageText;
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
pub struct StartButton;
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
pub struct StartMenuUI;
|
|
|
|
|
|
|
|
|
|
fn start_button_logic(
|
2025-01-01 20:03:58 +02:00
|
|
|
current_state: Res<State<GameState>>,
|
2025-01-01 18:52:19 +02:00
|
|
|
mut state: ResMut<NextState<GameState>>,
|
|
|
|
|
mut score: ResMut<Score>,
|
2025-01-01 20:03:58 +02:00
|
|
|
gamepads: Res<ButtonInput<GamepadButton>>,
|
|
|
|
|
gamepad1: Option<Res<GamepadOne>>,
|
2025-01-01 18:52:19 +02:00
|
|
|
mut button: Query<(&Interaction, &mut BorderColor), With<StartButton>>,
|
|
|
|
|
mut player_query: Query<(&mut Transform, &mut Visibility), With<Player>>,
|
|
|
|
|
mut ui_elems_query: Query<&mut Visibility, (With<StartMenuUI>, Without<Player>)>,
|
|
|
|
|
mut score_text_query: Query<&mut Text, With<ScoreText>>,
|
|
|
|
|
) {
|
2025-01-01 20:03:58 +02:00
|
|
|
if current_state.get() == &GameState::Playing {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut score_text = score_text_query.single_mut();
|
|
|
|
|
let (mut player_transform, mut player_visibility) = player_query.single_mut();
|
|
|
|
|
|
|
|
|
|
if let Some(gamepad) = gamepad1.as_deref() {
|
|
|
|
|
let start_btn = GamepadButton {
|
|
|
|
|
gamepad: gamepad.0,
|
|
|
|
|
button_type: GamepadButtonType::Start,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if gamepads.pressed(start_btn) {
|
|
|
|
|
start_game(
|
|
|
|
|
&mut score_text,
|
|
|
|
|
&mut score,
|
|
|
|
|
&mut state,
|
|
|
|
|
&mut ui_elems_query,
|
|
|
|
|
&mut player_visibility,
|
|
|
|
|
&mut player_transform,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:52:19 +02:00
|
|
|
let (interaction, mut border_color) = button.single_mut();
|
|
|
|
|
match *interaction {
|
|
|
|
|
Interaction::Pressed => {
|
2025-01-01 20:03:58 +02:00
|
|
|
start_game(
|
|
|
|
|
&mut score_text,
|
|
|
|
|
&mut score,
|
|
|
|
|
&mut state,
|
|
|
|
|
&mut ui_elems_query,
|
|
|
|
|
&mut player_visibility,
|
|
|
|
|
&mut player_transform,
|
|
|
|
|
);
|
2025-01-01 18:52:19 +02:00
|
|
|
}
|
|
|
|
|
Interaction::Hovered => border_color.0 = Color::WHITE,
|
|
|
|
|
Interaction::None => border_color.0 = Color::BLACK,
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-01 20:03:58 +02:00
|
|
|
|
|
|
|
|
fn start_game(
|
|
|
|
|
score_text: &mut Text,
|
|
|
|
|
score: &mut Score,
|
|
|
|
|
state: &mut NextState<GameState>,
|
|
|
|
|
ui_elems: &mut Query<&mut Visibility, (With<StartMenuUI>, Without<Player>)>,
|
|
|
|
|
player_visibility: &mut Visibility,
|
|
|
|
|
player_transform: &mut Transform,
|
|
|
|
|
) {
|
|
|
|
|
score.0 = 0;
|
|
|
|
|
score_text.sections[0].value = format!("{}", score.0);
|
|
|
|
|
|
|
|
|
|
state.set(GameState::Playing);
|
|
|
|
|
|
|
|
|
|
for mut elem in ui_elems {
|
|
|
|
|
*elem = Visibility::Hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*player_visibility = Visibility::Visible;
|
|
|
|
|
player_transform.translation = START_POSITION;
|
|
|
|
|
}
|