Compare commits
No commits in common. "8d673fb3aad83c7973352d30e85e7918379062d0" and "4da000f1c83c3eb7be45ef89d30cd1d14137c0b7" have entirely different histories.
8d673fb3aa
...
4da000f1c8
10 changed files with 59 additions and 153 deletions
|
|
@ -39,7 +39,6 @@ pub struct WaveState {
|
|||
pub ball_reached_end: bool,
|
||||
pub is_warming_up: bool,
|
||||
pub is_queue_locked: bool, //отдельно для большей явности
|
||||
pub last_insert_index: Option<usize>, //для системы удаления серий шариков
|
||||
}
|
||||
|
||||
impl Default for WaveState {
|
||||
|
|
@ -49,7 +48,6 @@ impl Default for WaveState {
|
|||
ball_reached_end: false,
|
||||
is_warming_up: false,
|
||||
is_queue_locked: false,
|
||||
last_insert_index: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -67,8 +65,4 @@ pub enum Direction {
|
|||
Bottom,
|
||||
}
|
||||
|
||||
#[derive(Resource, Default, Debug)]
|
||||
pub struct Score(pub u32);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct ScoreTextMarker;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
use bevy::prelude::*;
|
||||
use rand::seq::IndexedRandom;
|
||||
|
||||
#[derive(Component, Debug, Clone, Copy)]
|
||||
pub struct Ball {
|
||||
pub ball_type: BallType,
|
||||
pub slot_index: usize,
|
||||
pub slot_progress: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum BallType {
|
||||
#[default]
|
||||
|
|
@ -35,3 +28,9 @@ impl BallType {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
pub struct Ball {
|
||||
pub ball_type: BallType,
|
||||
pub slot_index: usize,
|
||||
pub slot_progress: f32,
|
||||
}
|
||||
|
|
@ -27,6 +27,12 @@ impl CannonState {
|
|||
}
|
||||
|
||||
pub fn cycle_next(&mut self) {
|
||||
// self.next_type = match self.next_type {
|
||||
// BallType::First => BallType::Second,
|
||||
// BallType::Second => BallType::Third,
|
||||
// BallType::Third => BallType::Forth,
|
||||
// BallType::Forth => BallType::First,
|
||||
// };
|
||||
let cur = self.current_type;
|
||||
self.current_type = self.next_type;
|
||||
self.next_type = cur;
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
use crate::FACTOR;
|
||||
|
||||
pub const SHIFT: f32 = 1.0; // коэффициент пересчета для слотов
|
||||
pub const SLOT_SIZE: f32 = FACTOR as f32 / SHIFT;
|
||||
|
||||
pub const BALL_MOVEMENT_SPEED: f32 = 60.0; // пикселей в секунду
|
||||
pub const INITIAL_BALLS_COUNT: usize = 10;
|
||||
pub const WARMUP_BALL_MOVEMENT_MULTIPLIER: f32 = 5.0; // во сколько раз больше BALL_MOVEMENT_SPEED
|
||||
|
||||
// Z-индексы элементов
|
||||
pub const CANNON_Z_INDEX: f32 = 10.0;
|
||||
pub const PROJECTILE_Z_INDEX: f32 = 11.0;
|
||||
pub const CURRENT_SHOT_Z_INDEX: f32 = 12.0;
|
||||
pub const NEXT_SHOT_Z_INDEX: f32 = 12.0;
|
||||
|
|
@ -18,9 +18,3 @@ mod components_cannon;
|
|||
pub use components_cannon::*;
|
||||
mod components_ball;
|
||||
pub use components_ball::*;
|
||||
mod ui;
|
||||
pub use ui::*;
|
||||
mod constants;
|
||||
pub use constants::*;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,40 +2,36 @@ use crate::states::AppState::GameState;
|
|||
use crate::states::level::*;
|
||||
use bevy::prelude::*;
|
||||
|
||||
|
||||
pub struct LevelPlugin;
|
||||
|
||||
impl Plugin for LevelPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
OnEnter(GameState),
|
||||
(setup_level, initialize_queue, setup_cannon).chain(),
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
// debug_draw_grid,
|
||||
debug_draw_track,
|
||||
warmup_queue_movement, //до спавна остальных шариков
|
||||
spawn_new_ball,
|
||||
check_wave_completion,
|
||||
//cannon systems
|
||||
cycle_cannon_type,
|
||||
update_cannon_preview,
|
||||
spawn_projectile_from_cannon,
|
||||
//сохранять порядок верхних трех
|
||||
rotate_cannon,
|
||||
move_projectiles,
|
||||
//сохраняем порядок трех
|
||||
app.add_systems(OnEnter(GameState), (
|
||||
setup_level,
|
||||
initialize_queue,
|
||||
setup_cannon).chain())
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
detect_projectile_hit,
|
||||
check_and_remove_matches,
|
||||
// debug_draw_grid,
|
||||
debug_draw_track,
|
||||
warmup_queue_movement, //до спавна остальных шариков
|
||||
spawn_new_ball,
|
||||
move_queue_along_track,
|
||||
check_wave_completion,
|
||||
//cannon systems
|
||||
cycle_cannon_type,
|
||||
update_cannon_preview,
|
||||
spawn_projectile_from_cannon,
|
||||
//сохранять порядок верхних трех
|
||||
rotate_cannon,
|
||||
move_projectiles,
|
||||
detect_projectile_hit,
|
||||
)
|
||||
.chain(),
|
||||
.run_if(in_state(GameState)),
|
||||
)
|
||||
.run_if(in_state(GameState)),
|
||||
)
|
||||
.add_systems(OnExit(GameState), cleanup_main_menu);
|
||||
.add_systems(OnExit(GameState), cleanup_main_menu);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use crate::states::level::*;
|
|||
use crate::{FACTOR, HEIGHT, WIDTH};
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub const SHIFT: f32 = 1.0;
|
||||
pub const SLOT_SIZE: f32 = FACTOR as f32 / SHIFT;
|
||||
|
||||
pub fn setup_timer(mut commands: Commands){
|
||||
commands.insert_resource(SpawnTimer::default());
|
||||
|
|
@ -16,8 +18,5 @@ 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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@ pub fn spawn_new_ball(
|
|||
return;
|
||||
}
|
||||
|
||||
//до добавления буферных слотов
|
||||
// let entry_blocked = balls
|
||||
// .iter()
|
||||
// .any(|ball| ball.slot_index == 0 && ball.slot_progress < 1.0);
|
||||
|
||||
let entry_blocked = balls.iter().any(|ball| {
|
||||
ball.slot_index == track.spawn_index && ball.slot_progress < 1.0
|
||||
});
|
||||
|
|
@ -45,7 +50,8 @@ pub fn spawn_new_ball(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// FIXME перенести позже в более удобное место/объект левела
|
||||
const SPEED: f32 = 60.0; // пикселей в секунду
|
||||
|
||||
pub fn move_queue_along_track(
|
||||
track: Res<TrackPath>,
|
||||
|
|
@ -58,7 +64,7 @@ pub fn move_queue_along_track(
|
|||
|
||||
for (mut ball, mut transform) in balls.iter_mut() {
|
||||
// Увеличиваем прогресс
|
||||
ball.slot_progress += BALL_MOVEMENT_SPEED * time.delta_secs() / SLOT_SIZE;
|
||||
ball.slot_progress += SPEED * time.delta_secs() / SLOT_SIZE;
|
||||
|
||||
// Если шарик достиг конца текущего сегмента
|
||||
if ball.slot_progress >= 1.0 {
|
||||
|
|
@ -86,7 +92,8 @@ pub fn move_queue_along_track(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// FIXME перенести позже в более удобное место/объект левела
|
||||
const START_COUNT: usize = 10;
|
||||
|
||||
pub fn initialize_queue(
|
||||
mut commands: Commands,
|
||||
|
|
@ -95,16 +102,16 @@ pub fn initialize_queue(
|
|||
mut wave: ResMut<WaveState>,
|
||||
) {
|
||||
// заполняем слоты от -START_COUNT до -1
|
||||
let base_idx = track.spawn_index.saturating_sub(INITIAL_BALLS_COUNT);
|
||||
let base_idx = track.spawn_index.saturating_sub(START_COUNT);
|
||||
|
||||
for i in 0..INITIAL_BALLS_COUNT {
|
||||
for i in 0..START_COUNT {
|
||||
let slot_idx = base_idx + i; // пропускаем индекс 0 (спавн за экраном)
|
||||
let Some(&pos) = track.points.get(slot_idx) else {
|
||||
continue;
|
||||
};
|
||||
println!("Slot {}: {:?}", slot_idx, pos); //debug
|
||||
|
||||
let target_idx = slot_idx + INITIAL_BALLS_COUNT;
|
||||
let target_idx = slot_idx + START_COUNT;
|
||||
|
||||
let ball_type = BallType::random();
|
||||
let image: Handle<Image> = asset_server.load(ball_type.asset_path());
|
||||
|
|
@ -129,7 +136,8 @@ pub fn initialize_queue(
|
|||
wave.spawning_allowed = false;
|
||||
}
|
||||
|
||||
|
||||
// FIXME перенести позже в более удобное место/объект левела
|
||||
const WARMUP_MULTIPLIER: f32 = 5.0;
|
||||
pub fn warmup_queue_movement(
|
||||
track: Res<TrackPath>,
|
||||
mut wave: ResMut<WaveState>,
|
||||
|
|
@ -141,7 +149,7 @@ pub fn warmup_queue_movement(
|
|||
) {
|
||||
if !wave.is_warming_up { return; }
|
||||
|
||||
let speed = BALL_MOVEMENT_SPEED * WARMUP_BALL_MOVEMENT_MULTIPLIER;
|
||||
let speed = SPEED * WARMUP_MULTIPLIER;
|
||||
|
||||
|
||||
let spawn_slot = track.spawn_index.saturating_sub(1);
|
||||
|
|
@ -201,82 +209,3 @@ pub fn check_wave_completion(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_and_remove_matches(
|
||||
mut commands: Commands,
|
||||
mut wave: ResMut<WaveState>,
|
||||
mut balls: ParamSet<(
|
||||
Query<(Entity, &Ball)>,
|
||||
Query<(Entity, &mut Ball)>
|
||||
)>,
|
||||
mut score: ResMut<Score>,
|
||||
) {
|
||||
let Some(insert_idx) = wave.last_insert_index.take() else { return; };
|
||||
|
||||
let mut balls_sorted: Vec<(Entity, Ball)> = balls.p0()
|
||||
.iter()
|
||||
.map(|(e, b)| (e, *b))
|
||||
.collect();
|
||||
balls_sorted.sort_by_key(|(_, b)| b.slot_index);
|
||||
|
||||
if balls_sorted.is_empty() { return; }
|
||||
|
||||
let Some(pos) = balls_sorted.iter().position(|(_, b)| b.slot_index == insert_idx) else { return; };
|
||||
let inserted_ball = balls_sorted[pos].1;
|
||||
|
||||
// Ищем группу того же типа
|
||||
let mut group_start = pos;
|
||||
while group_start > 0 && balls_sorted[group_start - 1].1.ball_type == inserted_ball.ball_type {
|
||||
group_start -= 1;
|
||||
}
|
||||
|
||||
let mut group_end = pos + 1;
|
||||
while group_end < balls_sorted.len() && balls_sorted[group_end].1.ball_type == inserted_ball.ball_type {
|
||||
group_end += 1;
|
||||
}
|
||||
|
||||
let group_size = group_end - group_start;
|
||||
if group_size < 3 { return; }
|
||||
|
||||
// Запоминаем диапазон до удаления
|
||||
let removed_min_idx = balls_sorted[group_start].1.slot_index;
|
||||
let removed_max_idx = balls_sorted[group_end - 1].1.slot_index;
|
||||
let removed_count = group_size;
|
||||
|
||||
// Проверяем соседей
|
||||
let has_left_neighbors = balls_sorted.iter().any(|(_, b)| b.slot_index < removed_min_idx);
|
||||
let has_right_neighbors = balls_sorted.iter().any(|(_, b)| b.slot_index > removed_max_idx);
|
||||
|
||||
// --- УДАЛЕНИЕ ---
|
||||
let to_remove: Vec<Entity> = balls_sorted[group_start..group_end]
|
||||
.iter()
|
||||
.map(|(e, _)| *e)
|
||||
.collect();
|
||||
|
||||
for entity in to_remove.iter() {
|
||||
commands.entity(*entity).despawn();
|
||||
}
|
||||
|
||||
let gained = removed_count * 100;
|
||||
score.0 += gained as u32;
|
||||
println!("Match at idx {}! +{} points | total: {}", insert_idx, gained, score.0);
|
||||
|
||||
// --- СХЛОПЫВАНИЕ ОЧЕРЕДИ ---
|
||||
|
||||
if has_left_neighbors && has_right_neighbors {
|
||||
// 🔸 Удаление ВНУТРИ: сдвигаем все шарики СПРАВА влево
|
||||
println!("Closing gap ({}..{}), shifting right balls left by {}",
|
||||
removed_min_idx, removed_max_idx, removed_count);
|
||||
|
||||
// Сдвигаем индексы всех шариков с index > removed_max_idx
|
||||
for (entity, mut ball) in balls.p1().iter_mut() {
|
||||
if ball.slot_index > removed_max_idx {
|
||||
ball.slot_index -= removed_count;
|
||||
ball.slot_progress = 0.0; // "Примагничиваем" к новому слоту
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔸 Удаление в НАЧАЛЕ или КОНЦЕ: индексы не меняем
|
||||
// (опционально: можно сбросить progress у правых соседей для чёткости)
|
||||
}
|
||||
|
|
@ -2,6 +2,11 @@ use bevy::prelude::*;
|
|||
use crate::states::level::*;
|
||||
use bevy::window::PrimaryWindow;
|
||||
|
||||
const CANNON_Z_INDEX: f32 = 10.0;
|
||||
const PROJECTILE_Z_INDEX: f32 = 11.0;
|
||||
const CURRENT_SHOT_Z_INDEX: f32 = 12.0;
|
||||
const NEXT_SHOT_Z_INDEX: f32 = 12.0;
|
||||
|
||||
pub fn setup_cannon(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let texture: Handle<Image> = asset_server.load("cannon/cannon.png");
|
||||
|
||||
|
|
@ -186,8 +191,6 @@ pub fn detect_projectile_hit(
|
|||
slot_progress: target_progress,
|
||||
}
|
||||
}).remove::<BallProjectile>();
|
||||
// запоминаем индекс слота, куда попали
|
||||
wave.last_insert_index = Some(insert_idx);
|
||||
}
|
||||
|
||||
// Разблокировка (структурные изменения завершены)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ pub fn setup_track_with_buffer() -> TrackPath {
|
|||
|
||||
TrackPath {
|
||||
points: full_points,
|
||||
spawn_index: BUFFER_SLOTS-1,
|
||||
spawn_index: BUFFER_SLOTS,
|
||||
buffer_size: BUFFER_SLOTS,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue