Compare commits

..

2 commits

Author SHA1 Message Date
nquidox
9bad37d011 sync balls to align them in slots 2026-04-12 17:39:32 +03:00
nquidox
6423764a17 no move for orphan balls 2026-04-12 16:39:23 +03:00
4 changed files with 83 additions and 8 deletions

View file

@ -3,9 +3,11 @@ use crate::FACTOR;
pub const SHIFT: f32 = 1.0; // коэффициент пересчета для слотов pub const SHIFT: f32 = 1.0; // коэффициент пересчета для слотов
pub const SLOT_SIZE: f32 = FACTOR as f32 / SHIFT; pub const SLOT_SIZE: f32 = FACTOR as f32 / SHIFT;
pub const BALL_MOVEMENT_SPEED: f32 = 60.0; // пикселей в секунду pub const BALL_MOVEMENT_SPEED: f32 = 10.0; // пикселей в секунду, базово 60
pub const INITIAL_BALLS_COUNT: usize = 10; pub const INITIAL_BALLS_COUNT: usize = 10;
pub const WARMUP_BALL_MOVEMENT_MULTIPLIER: f32 = 5.0; // во сколько раз больше BALL_MOVEMENT_SPEED pub const WARMUP_BALL_MOVEMENT_MULTIPLIER: f32 = 30.0; // во сколько раз больше BALL_MOVEMENT_SPEED, базово 5
pub const HIT_THRESHOLD: f32 = SLOT_SIZE; // для detect_projectile_hit
// Z-индексы элементов // Z-индексы элементов
pub const CANNON_Z_INDEX: f32 = 10.0; pub const CANNON_Z_INDEX: f32 = 10.0;
@ -13,4 +15,6 @@ pub const PROJECTILE_Z_INDEX: f32 = 11.0;
pub const CURRENT_SHOT_Z_INDEX: f32 = 12.0; pub const CURRENT_SHOT_Z_INDEX: f32 = 12.0;
pub const NEXT_SHOT_Z_INDEX: f32 = 12.0; pub const NEXT_SHOT_Z_INDEX: f32 = 12.0;
pub const SCORE_Z_INDEX: f32 = 100.0; pub const SCORE_Z_INDEX: f32 = 100.0;
pub const TRACK_Z_INDEX: f32 = 9.0;

View file

@ -33,6 +33,7 @@ impl Plugin for LevelPlugin {
check_and_remove_matches, check_and_remove_matches,
update_score_text, update_score_text,
move_queue_along_track, move_queue_along_track,
sync_ball_visuals,
) )
.chain(), .chain(),
) )

View file

@ -49,14 +49,43 @@ pub fn spawn_new_ball(
pub fn move_queue_along_track( pub fn move_queue_along_track(
track: Res<TrackPath>, track: Res<TrackPath>,
mut balls: Query<(&mut Ball, &mut Transform)>, mut balls: Query<(Entity, &mut Ball, &mut Transform)>,
time: Res<Time>, time: Res<Time>,
wave: Res<WaveState>, wave: Res<WaveState>,
) { ) {
// проверяем, не идет ли прогрев трека // проверяем, не идет ли прогрев трека
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; }
//ищем первый пустой слот для определения разрыва
let active_boundary = {
let mut current_idx = balls_sorted[0].1.slot_index;
for (mut ball, mut transform) in balls.iter_mut() { for (_, ball) in balls_sorted.iter().skip(1) {
if ball.slot_index == current_idx + 1 {
current_idx = ball.slot_index;
} else {
break;
}
}
current_idx
};
for (entity, mut ball, mut transform) in balls.iter_mut() {
//пропускаем все шары в слотах после слота начала разрыва
if ball.slot_index > active_boundary {
// но при этом обнуляем прогресс у оторванных шариков
if ball.slot_progress > 0.01 {
ball.slot_progress = 0.0;
}
continue;
}
// Увеличиваем прогресс // Увеличиваем прогресс
ball.slot_progress += BALL_MOVEMENT_SPEED * time.delta_secs() / SLOT_SIZE; ball.slot_progress += BALL_MOVEMENT_SPEED * time.delta_secs() / SLOT_SIZE;
@ -297,12 +326,19 @@ pub fn check_and_remove_matches(
// записываем ласт хит, чтоб имитировать триггер каскадного уничтожения // записываем ласт хит, чтоб имитировать триггер каскадного уничтожения
wave.last_insert_index = Some(removed_min_idx - 1); wave.last_insert_index = Some(removed_min_idx - 1);
println!("Cascade armed at idx {}", removed_min_idx - 1); println!("Cascade armed at idx {}", removed_min_idx - 1);
} else { } else {
// === РАЗНЫЕ ТИПЫ: НИЧЕГО не делаем === // === РАЗНЫЕ ТИПЫ: НИЧЕГО не делаем ===
// Дыра остаётся, индексы и прогресс не трогаем // Дыра остаётся, индексы и прогресс не трогаем
println!("Different types ({:?} vs {:?}): gap preserved", println!("Different types ({:?} vs {:?}): gap preserved",
left_neighbor_type, right_neighbor_type); left_neighbor_type, right_neighbor_type);
// Сбрасываем прогресс только у шариков ПРАВЕЕ удалённой группы (оторванный хвост)
for (entity, mut ball) in balls.p1().iter_mut() {
if ball.slot_index > removed_max_idx {
ball.slot_progress = 0.0;
}
}
} }
return; return;
} }
@ -311,4 +347,24 @@ pub fn check_and_remove_matches(
for (entity, mut ball) in balls.p1().iter_mut() { for (entity, mut ball) in balls.p1().iter_mut() {
ball.slot_progress = 0.0; ball.slot_progress = 0.0;
} }
}
pub fn sync_ball_visuals(
track: Res<TrackPath>,
mut balls: Query<(&Ball, &mut Transform)>,
) {
for (ball, mut transform) in balls.iter_mut() {
// Получаем точку текущего слота
if let Some(&curr_pos) = track.points.get(ball.slot_index) {
// Если есть следующий слот — интерполируем, иначе — просто центр текущего
let visual_pos = if let Some(&next_pos) = track.points.get(ball.slot_index + 1) {
curr_pos.lerp(next_pos, ball.slot_progress)
} else {
curr_pos
};
// 🔥 ПРИНУДИТЕЛЬНО ставим позицию и правильный Z
transform.translation = visual_pos.extend(TRACK_Z_INDEX);
}
}
} }

View file

@ -108,7 +108,6 @@ pub fn move_projectiles(
} }
} }
const HIT_THRESHOLD: f32 = SLOT_SIZE;
pub fn detect_projectile_hit( pub fn detect_projectile_hit(
mut commands: Commands, mut commands: Commands,
track: Res<TrackPath>, track: Res<TrackPath>,
@ -186,6 +185,21 @@ pub fn detect_projectile_hit(
slot_progress: target_progress, slot_progress: target_progress,
} }
}).remove::<BallProjectile>(); }).remove::<BallProjectile>();
if let Some(&curr_pos) = track.points.get(insert_idx) {
// Вычисляем позицию: интерполяция или центр слота
let visual_pos = if let Some(&next_pos) = track.points.get(insert_idx + 1) {
curr_pos.lerp(next_pos, target_progress)
} else {
curr_pos
};
// Обновляем Transform: ставим на трек и выравниваем Z (10.0),
// чтобы шарик не висел «над» очередью (PROJECTILE_Z_INDEX)
commands.entity(proj_entity).insert(
Transform::from_translation(visual_pos.extend(10.0))
);
}
// запоминаем индекс слота, куда попали // запоминаем индекс слота, куда попали
wave.last_insert_index = Some(insert_idx); wave.last_insert_index = Some(insert_idx);
} }