diff --git a/src/states/level/components.rs b/src/states/level/components.rs index 47b5660..55ac05a 100644 --- a/src/states/level/components.rs +++ b/src/states/level/components.rs @@ -72,3 +72,13 @@ pub struct Score(pub u32); #[derive(Component)] pub struct ScoreTextMarker; + +#[derive(Resource, Default)] +pub struct GameOver(pub bool); +#[derive(Component)] +pub struct GameOverMarker; + +#[derive(Resource, Default)] +pub struct GameWin(pub bool); +#[derive(Component)] +pub struct GameWinMarker; \ No newline at end of file diff --git a/src/states/level/components_cannon.rs b/src/states/level/components_cannon.rs index 6113c20..94556d1 100644 --- a/src/states/level/components_cannon.rs +++ b/src/states/level/components_cannon.rs @@ -45,4 +45,10 @@ pub struct BallProjectile { pub velocity: Vec2, pub previous_position: Vec2, pub ball_type: BallType, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HitboxType { + Circle, // проверка по радиусу от центра слота + Square, // проверка по квадрату спрайта } \ No newline at end of file diff --git a/src/states/level/constants.rs b/src/states/level/constants.rs index 262c37c..2aafba0 100644 --- a/src/states/level/constants.rs +++ b/src/states/level/constants.rs @@ -3,9 +3,10 @@ use crate::FACTOR; pub const SHIFT: f32 = 1.0; // коэффициент пересчета для слотов pub const SLOT_SIZE: f32 = FACTOR as f32 / SHIFT; -pub const BALL_MOVEMENT_SPEED: f32 = 10.0; // пикселей в секунду, базово 60 -pub const INITIAL_BALLS_COUNT: usize = 10; -pub const WARMUP_BALL_MOVEMENT_MULTIPLIER: f32 = 30.0; // во сколько раз больше BALL_MOVEMENT_SPEED, базово 5 +pub const BALL_MOVEMENT_SPEED: f32 = 30.0; // пикселей в секунду, базово 60 +pub const BUFFER_SLOTS: usize = 20; +pub const INITIAL_BALLS_COUNT: usize = 15; +pub const WARMUP_BALL_MOVEMENT_MULTIPLIER: f32 = 10.0; // во сколько раз больше BALL_MOVEMENT_SPEED, базово 5 pub const HIT_THRESHOLD: f32 = SLOT_SIZE; // для detect_projectile_hit @@ -17,4 +18,6 @@ pub const NEXT_SHOT_Z_INDEX: f32 = 12.0; pub const SCORE_Z_INDEX: f32 = 100.0; -pub const TRACK_Z_INDEX: f32 = 9.0; \ No newline at end of file +pub const TRACK_Z_INDEX: f32 = 9.0; + +pub const POINTS_TO_WIN: u32 = 1000; \ No newline at end of file diff --git a/src/states/level/plugin.rs b/src/states/level/plugin.rs index 149c0e6..e80018a 100644 --- a/src/states/level/plugin.rs +++ b/src/states/level/plugin.rs @@ -8,7 +8,17 @@ impl Plugin for LevelPlugin { fn build(&self, app: &mut App) { app.add_systems( OnEnter(GameState), - (setup_level, initialize_queue, setup_cannon, setup_score_ui).chain(), + ( + ( + setup_level, + initialize_queue, + setup_cannon, + setup_score_ui, + setup_ui, + reset_in_game_text, + ) + .chain(), + ), ) .add_systems( Update, @@ -36,6 +46,8 @@ impl Plugin for LevelPlugin { sync_ball_visuals, ) .chain(), + update_game_over_ui, + update_game_win_ui, ) .run_if(in_state(GameState)), ) diff --git a/src/states/level/system.rs b/src/states/level/system.rs index fc9eb65..45d7e07 100644 --- a/src/states/level/system.rs +++ b/src/states/level/system.rs @@ -16,8 +16,15 @@ pub fn setup_level(mut commands: Commands){ commands.insert_resource(build_cannon_state()); println!("CannonState is set up"); - + commands.insert_resource(Score::default()); println!("Score is set up"); + + commands.insert_resource(GameOver::default()); + println!("Game over ui is set up"); + + commands.insert_resource(GameWin::default()); + println!("Game win ui is set up"); + //TODO подумать как объединить элементы интерфейса, для единой точки оформления } diff --git a/src/states/level/system_ball.rs b/src/states/level/system_ball.rs index 28a42f9..4359d5d 100644 --- a/src/states/level/system_ball.rs +++ b/src/states/level/system_ball.rs @@ -1,5 +1,6 @@ -use bevy::prelude::*; use crate::states::level::*; +use bevy::prelude::*; +use std::collections::HashSet; pub fn spawn_new_ball( mut commands: Commands, @@ -15,9 +16,9 @@ pub fn spawn_new_ball( return; } - let entry_blocked = balls.iter().any(|ball| { - ball.slot_index == track.spawn_index && ball.slot_progress < 1.0 - }); + let entry_blocked = balls + .iter() + .any(|ball| ball.slot_index == track.spawn_index && ball.slot_progress < 1.0); if timer.is_finished() && !entry_blocked { if let Some(&spawn_pos) = track.points.first() { @@ -45,8 +46,6 @@ pub fn spawn_new_ball( } } - - pub fn move_queue_along_track( track: Res, mut balls: Query<(Entity, &mut Ball, &mut Transform)>, @@ -54,15 +53,19 @@ pub fn move_queue_along_track( wave: Res, ) { // проверяем, не идет ли прогрев трека - if wave.is_warming_up { return; } - - //собираем все шары в вектор и сортируем по индексам слотов + if wave.is_warming_up { + return; + } + + // собираем все шары в вектор и сортируем по индексам слотов let mut balls_sorted: Vec<(Entity, Ball)> = balls.iter().map(|(e, b, _)| (e, *b)).collect(); balls_sorted.sort_by_key(|(_, b)| b.slot_index); - - if balls_sorted.is_empty() { return; } - - //ищем первый пустой слот для определения разрыва + + if balls_sorted.is_empty() { + return; + } + + // ищем первый пустой слот для определения разрыва let active_boundary = { let mut current_idx = balls_sorted[0].1.slot_index; @@ -77,7 +80,7 @@ pub fn move_queue_along_track( }; for (entity, mut ball, mut transform) in balls.iter_mut() { - //пропускаем все шары в слотах после слота начала разрыва + // пропускаем все шары в слотах после слота начала разрыва if ball.slot_index > active_boundary { // но при этом обнуляем прогресс у оторванных шариков if ball.slot_progress > 0.01 { @@ -85,23 +88,25 @@ pub fn move_queue_along_track( } continue; } - - // Увеличиваем прогресс + + // увеличиваем прогресс ball.slot_progress += BALL_MOVEMENT_SPEED * time.delta_secs() / SLOT_SIZE; - // Если шарик достиг конца текущего сегмента + // если шарик достиг конца текущего сегмента if ball.slot_progress >= 1.0 { - // Если это въезд в трек (был в слоте 0) — переходим в слот 1 + // если это въезд в трек (был в слоте 0) - переходим в слот 1 + // TODO проверить совсместно с переносом спавн-слота в буферную зону if ball.slot_index == 0 { ball.slot_index = 1; } else { - // Обычное движение: переходим к следующему слоту + // обычное движение, переходим к следующему слоту ball.slot_index += 1; } + // начинаем прогресс с нуля в новом слоте ball.slot_progress = 0.0; } - // Вычисляем визуальную позицию + // вычисляем визуальную позицию let current = track.points.get(ball.slot_index); let next = track.points.get(ball.slot_index + 1); @@ -109,31 +114,33 @@ pub fn move_queue_along_track( let pos = curr_pos.lerp(next_pos, ball.slot_progress); transform.translation = pos.extend(transform.translation.z); } else if let Some(&pos) = current { - // Последний слот — просто ставим в него + // последний слот - просто ставим в него transform.translation = pos.extend(transform.translation.z); } } } - - pub fn initialize_queue( mut commands: Commands, track: Res, asset_server: Res, mut wave: ResMut, ) { - // заполняем слоты от -START_COUNT до -1 + // заполняем слоты от -INITIAL_BALLS_COUNT до -1 let base_idx = track.spawn_index.saturating_sub(INITIAL_BALLS_COUNT); for i in 0..INITIAL_BALLS_COUNT { - let slot_idx = base_idx + i; // пропускаем индекс 0 (спавн за экраном) + // пропускаем индекс 0 (спавн за экраном), чек тудушку в спавнере шарика (~100 строка) + let slot_idx = base_idx + i; let Some(&pos) = track.points.get(slot_idx) else { continue; }; - println!("Slot {}: {:?}", slot_idx, pos); //debug - let target_idx = slot_idx + INITIAL_BALLS_COUNT; + //инфа о занимаемых буферных слотах + println!("Слот {}: {:?}", slot_idx, pos); + + // слот спавна шариков + let target_idx = slot_idx + INITIAL_BALLS_COUNT; let ball_type = BallType::random(); let image: Handle = asset_server.load(ball_type.asset_path()); @@ -151,31 +158,34 @@ pub fn initialize_queue( slot_progress: 0.0, }, LevelMarker, - WarmupTarget { target_index: target_idx }, + WarmupTarget { + target_index: target_idx, + }, )); } wave.is_warming_up = true; wave.spawning_allowed = false; } - pub fn warmup_queue_movement( track: Res, mut wave: ResMut, mut balls: ParamSet<( Query<&Ball>, - Query<(&mut Ball, &mut Transform, &WarmupTarget)> + Query<(&mut Ball, &mut Transform, &WarmupTarget)>, )>, time: Res