diff --git a/src/states/linear/components.rs b/src/states/linear/components.rs index 2d78beb..06548c8 100644 --- a/src/states/linear/components.rs +++ b/src/states/linear/components.rs @@ -18,10 +18,4 @@ pub enum DebugToggle{ Track, Grid, Laser, -} - -#[derive(Component, Copy, Clone)] -pub struct HitMarker; - -#[derive(Component, Copy, Clone)] -pub struct ToRemoveMarker; \ No newline at end of file +} \ No newline at end of file diff --git a/src/states/linear/plugin.rs b/src/states/linear/plugin.rs index 5d39da1..5c0ceac 100644 --- a/src/states/linear/plugin.rs +++ b/src/states/linear/plugin.rs @@ -47,7 +47,6 @@ impl Plugin for LinearPlayPlugin { calculate_projectile_hits, linear_move_projectiles, insert_projectile_into_track, - count_matching_balls, ).in_set(LinearUpdateSet::Track), ) .add_systems(OnExit(LinearPlayState), cleanup); diff --git a/src/states/linear/system_ball.rs b/src/states/linear/system_ball.rs index 24866bf..be907ed 100644 --- a/src/states/linear/system_ball.rs +++ b/src/states/linear/system_ball.rs @@ -48,20 +48,20 @@ pub fn move_round_balls( for (mut transform, mut ball) in &mut balls { // сразу обновляем прогресс ball.track_progress += track.speed_norm * time.delta_secs(); - + // потом позиционируем визуал for seg in &track.segments { if ball.track_progress >= seg.t_start && ball.track_progress < seg.t_end { // вычисляем локальное нормализованое положение на треке let t_local = (ball.track_progress - seg.t_start) / (seg.t_end - seg.t_start); - + let mut pos = Vec2::ZERO; - match seg.segment_type { + match seg.segment_type{ SegementType::Line {} => { pos = seg.start_pos + seg.direction * seg.length * t_local; // transform.translation = pos.extend(ROUND_BALL_Z); } - + SegementType::Turn {} => { let angle = seg.start_angle + FRAC_PI_2 * seg.sweep_sign * t_local; pos = seg.center + Vec2::from_angle(angle) * seg.radius; @@ -72,86 +72,3 @@ pub fn move_round_balls( } } } - -pub fn count_matching_balls( - mut commands: Commands, - start_point: Query<(Entity, &RoundBall), With>, - balls: Query<(Entity, &RoundBall)>, -) { - // если хит маркеров нет, сразу выходим - if start_point.is_empty() { - return; - } - - println!("Проверка на группу начата"); - - //собираем все шарики на треке - let mut balls_sorted: Vec<(Entity, RoundBall)> = balls.iter().map(|(e, b)| (e, *b)).collect(); - - if balls_sorted.is_empty() { - return; - } - - // сортируем по прогрессу - balls_sorted.sort_by(|a, b| a.1.track_progress.total_cmp(&b.1.track_progress)); - - // принимаем шарик снаряд как точку отсчета - for (start_entity, start_round) in start_point.iter() { - // сразу снимаем маркер попадания с бывшего шарика-снаряда, который был встроен в трек - commands.entity(start_entity).remove::(); - - // берем прогресс и тип шарика в точке попадания - let start_progress = start_round.track_progress; - let hit_ball_type = start_round.ball_type; - - // будем итерировать по индексам - let mut hit_index = 0; - for (index, ball) in balls_sorted.iter().enumerate() { - if ball.1.track_progress == start_progress { - hit_index = index; - } - } - - let mut left_threshold = hit_index; - let mut right_threshold = hit_index; - - // ищем крайнего соседа слева - for li in (0..=hit_index).rev() { - // - if balls_sorted[li].1.ball_type == hit_ball_type { - // записываем индекс как порог - left_threshold = li; - continue; - } else { - // иначе прерываем - break; - } - } - - // то же самое для правого - for ri in hit_index..=balls_sorted.len() { - if balls_sorted[ri].1.ball_type == hit_ball_type { - right_threshold = ri; - continue; - } else { - break; - } - } - - // считаем размер группы - let group_size = right_threshold - left_threshold + 1; - if group_size < 3 { - continue; // не return, так как может быть несколько хит маркеров - } - - println!("Группа из 3+ шариков! Размер: {}", group_size); - - // маркируем группу по порогам на удаление - for entity in balls_sorted[left_threshold..=right_threshold] - .iter() - .map(|(e, _)| *e) - { - commands.entity(entity).insert(ToRemoveMarker); - } - } -} diff --git a/src/states/linear/systems_projectile.rs b/src/states/linear/systems_projectile.rs index 60a22a0..dc51d2e 100644 --- a/src/states/linear/systems_projectile.rs +++ b/src/states/linear/systems_projectile.rs @@ -1,6 +1,6 @@ use crate::states::linear::*; use crate::{FACTOR, HEIGHT, WIDTH}; -use bevy::asset::{AssetServer, ErasedAssetLoader}; +use bevy::asset::AssetServer; use bevy::math::Vec2; use bevy::prelude::*; use std::f32::consts::{FRAC_PI_2, PI}; @@ -46,7 +46,32 @@ pub fn calculate_projectile_hits( // сортируем от ближайшего t к дальнему intersections.sort_by(|a, b| a.t.partial_cmp(&b.t).unwrap()); - + + // // если есть точки пересечения + // if let Some(closest_hit) = intersections.first() { + // // собираем шарики + // let mut occupied: Vec = balls_on_track.iter().map(|b| b.track_progress).collect(); + // // сортируем прогресс для дальнейших вычислений + // occupied.sort_by(|a, b| a.partial_cmp(b).unwrap()); + // + // //считаем прогресс для точки попадания через мировые координаты и индекс сегмента на треке + // let hit_progress = calculate_progress(closest_hit, &track); + // + // let radius_norm = BALL_RADIUS / track.total_length; + // if is_occupied(hit_progress, &occupied, radius_norm) { + // proj.hit_result = Some(ProjectileHit { + // hit_progress, + // hit_position: closest_hit.world_pos, + // segment_index: closest_hit.segment_index, + // }); + // println!("Попадание в трек: {}", hit_progress); + // transform.translation = closest_hit.world_pos.extend(transform.translation.z); + // // убираем маркер поиска, добавляем маркер готовности ко вставке для другой системы + // commands + // .entity(entity) + // .remove::() + // .insert(InsertMarker); + // } let mut occupied: Vec = balls_on_track.iter().map(|b| b.track_progress).collect(); occupied.sort_by(|a, b| a.partial_cmp(b).unwrap()); let radius_norm = BALL_RADIUS / track.total_length; @@ -70,6 +95,9 @@ pub fn calculate_projectile_hits( }); // записываем координаты точки попадания, до которой летит шарик + // вероятен момент, когда шарик летит, а в точке пересечения появится шарики на треке + // если вылезет такое, пофиксить через проверку всех точек пересечения, а не только + // конкретной, которая тут записана proj.target_position = Some(target_inter.world_pos); // Переключаем маркер: больше не ищем, готовы к вставке @@ -282,55 +310,75 @@ fn is_occupied(target: f32, occupied: &[f32], radius_norm: f32) -> bool { pub fn insert_projectile_into_track( mut commands: Commands, mut projectiles: Query<(Entity, &RoundBallProjectile), With>, - mut balls_on_track: Query<(Entity, &mut RoundBall, &Transform)>, + mut balls_on_track: Query<(Entity, &mut RoundBall)>, track: Res, asset_server: Res, ) { for (proj_entity, proj) in projectiles.iter_mut() { - // let Some(hit) = proj.hit_result else { continue }; // артефактный код, пока просто закоментил - let proj_pos = proj.previous_position; - - // ищем любой шарик, до которого снаряд долетел визуально - let mut target_found = None; - for (ball_entity, ball, ball_tf) in balls_on_track.iter() { - let ball_pos = ball_tf.translation.truncate(); - let distance = proj_pos.distance(ball_pos); - - if distance < BALL_COLLISION_THRESHOLD { - target_found = Some((ball_entity, ball.track_progress, ball_pos)); - break; // нашли первую коллизию - дальше не ищем - } - } - - // target_entity - артефактный код, пока просто закоментил - let Some((target_entity, original_progress, target_ball_pos)) = target_found else { - continue; // ни с одним шариком не столкнулся - летит дальше + let Some(hit) = proj.hit_result else { + continue; }; + + let Some(target_pos) = proj.target_position else { continue }; + let distance = proj.previous_position.distance(target_pos); + + // расстояние, при котором встраивается шарик + const ARRIVAL_DIST: f32 = 5.0; + + if distance > ARRIVAL_DIST { + continue; + } + + // ищем шарик, в который попали (цель) + // собираем данные, чтобы не конфликтовать с мутабельным доступом ниже + let mut balls_data: Vec<_> = balls_on_track + .iter() + .map(|(entity, ball)| (entity, ball.track_progress)) + .collect(); + + // сортируем по близости к точке удара + balls_data.sort_by(|a, b| { + let diff_a = (a.1 - hit.hit_progress).abs(); + let diff_b = (b.1 - hit.hit_progress).abs(); + diff_a.partial_cmp(&diff_b).unwrap() + }); + + let Some((target_entity, original_target_progress)) = balls_data.first() else { + continue; + }; + // копируем значение + let original_progress = *original_target_progress; + + // размер сдвига посчитан в треке + let shift = track.min_spawn_gap; - // entity - артефактный код, пока просто закоментил - // сдвигаем только те шарики, которые находятся после точки вставки - for (entity, mut ball, _) in balls_on_track.iter_mut() { - if ball.track_progress >= original_progress { - ball.track_progress = (ball.track_progress + track.min_spawn_gap).clamp(0.0, 1.0); + // ставим на место шарика-цели, а сам шарик-цель сдвигаем вперед по треку + for (entity, mut ball) in balls_on_track.iter_mut() { + if entity == *target_entity { + ball.track_progress = (original_progress + shift).clamp(0.0, 1.0); } } - // спавним новый шарик ровно в позицию целевого + for (entity, mut ball) in balls_on_track.iter_mut() { + ball.track_progress = (ball.track_progress + shift).clamp(0.0, 1.0); + } + + // спавним новый шарик на месте цели commands.spawn(( Sprite { image: asset_server.load(proj.ball_type.asset_path()), custom_size: Some(Vec2::splat(ROUND_BALL_SIZE)), ..default() }, - Transform::from_translation(target_ball_pos.extend(ROUND_BALL_Z)), + Transform::from_translation(hit.hit_position.extend(ROUND_BALL_Z)), RoundBall { ball_type: proj.ball_type, - track_progress: original_progress, // занимает место шарика, в который попали + track_progress: original_progress, }, LinearStateMarker, - HitMarker, // маркер для системы уничтожения шариков )); + // деспавн шарика-снаряда commands.entity(proj_entity).despawn(); } -} \ No newline at end of file +}