Compare commits
2 commits
67ddcb0c65
...
745ed5cc54
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
745ed5cc54 | ||
|
|
f6b5765b83 |
4 changed files with 124 additions and 82 deletions
|
|
@ -19,3 +19,9 @@ pub enum DebugToggle{
|
||||||
Grid,
|
Grid,
|
||||||
Laser,
|
Laser,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Copy, Clone)]
|
||||||
|
pub struct HitMarker;
|
||||||
|
|
||||||
|
#[derive(Component, Copy, Clone)]
|
||||||
|
pub struct ToRemoveMarker;
|
||||||
|
|
@ -47,6 +47,7 @@ impl Plugin for LinearPlayPlugin {
|
||||||
calculate_projectile_hits,
|
calculate_projectile_hits,
|
||||||
linear_move_projectiles,
|
linear_move_projectiles,
|
||||||
insert_projectile_into_track,
|
insert_projectile_into_track,
|
||||||
|
count_matching_balls,
|
||||||
).in_set(LinearUpdateSet::Track),
|
).in_set(LinearUpdateSet::Track),
|
||||||
)
|
)
|
||||||
.add_systems(OnExit(LinearPlayState), cleanup);
|
.add_systems(OnExit(LinearPlayState), cleanup);
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ pub fn move_round_balls(
|
||||||
let t_local = (ball.track_progress - seg.t_start) / (seg.t_end - seg.t_start);
|
let t_local = (ball.track_progress - seg.t_start) / (seg.t_end - seg.t_start);
|
||||||
|
|
||||||
let mut pos = Vec2::ZERO;
|
let mut pos = Vec2::ZERO;
|
||||||
match seg.segment_type{
|
match seg.segment_type {
|
||||||
SegementType::Line {} => {
|
SegementType::Line {} => {
|
||||||
pos = seg.start_pos + seg.direction * seg.length * t_local;
|
pos = seg.start_pos + seg.direction * seg.length * t_local;
|
||||||
// transform.translation = pos.extend(ROUND_BALL_Z);
|
// transform.translation = pos.extend(ROUND_BALL_Z);
|
||||||
|
|
@ -72,3 +72,86 @@ pub fn move_round_balls(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn count_matching_balls(
|
||||||
|
mut commands: Commands,
|
||||||
|
start_point: Query<(Entity, &RoundBall), With<HitMarker>>,
|
||||||
|
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::<HitMarker>();
|
||||||
|
|
||||||
|
// берем прогресс и тип шарика в точке попадания
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::states::linear::*;
|
use crate::states::linear::*;
|
||||||
use crate::{FACTOR, HEIGHT, WIDTH};
|
use crate::{FACTOR, HEIGHT, WIDTH};
|
||||||
use bevy::asset::AssetServer;
|
use bevy::asset::{AssetServer, ErasedAssetLoader};
|
||||||
use bevy::math::Vec2;
|
use bevy::math::Vec2;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use std::f32::consts::{FRAC_PI_2, PI};
|
use std::f32::consts::{FRAC_PI_2, PI};
|
||||||
|
|
@ -47,31 +47,6 @@ pub fn calculate_projectile_hits(
|
||||||
// сортируем от ближайшего t к дальнему
|
// сортируем от ближайшего t к дальнему
|
||||||
intersections.sort_by(|a, b| a.t.partial_cmp(&b.t).unwrap());
|
intersections.sort_by(|a, b| a.t.partial_cmp(&b.t).unwrap());
|
||||||
|
|
||||||
// // если есть точки пересечения
|
|
||||||
// if let Some(closest_hit) = intersections.first() {
|
|
||||||
// // собираем шарики
|
|
||||||
// let mut occupied: Vec<f32> = 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::<IntersectMarker>()
|
|
||||||
// .insert(InsertMarker);
|
|
||||||
// }
|
|
||||||
let mut occupied: Vec<f32> = balls_on_track.iter().map(|b| b.track_progress).collect();
|
let mut occupied: Vec<f32> = balls_on_track.iter().map(|b| b.track_progress).collect();
|
||||||
occupied.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
occupied.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||||
let radius_norm = BALL_RADIUS / track.total_length;
|
let radius_norm = BALL_RADIUS / track.total_length;
|
||||||
|
|
@ -95,9 +70,6 @@ pub fn calculate_projectile_hits(
|
||||||
});
|
});
|
||||||
|
|
||||||
// записываем координаты точки попадания, до которой летит шарик
|
// записываем координаты точки попадания, до которой летит шарик
|
||||||
// вероятен момент, когда шарик летит, а в точке пересечения появится шарики на треке
|
|
||||||
// если вылезет такое, пофиксить через проверку всех точек пересечения, а не только
|
|
||||||
// конкретной, которая тут записана
|
|
||||||
proj.target_position = Some(target_inter.world_pos);
|
proj.target_position = Some(target_inter.world_pos);
|
||||||
|
|
||||||
// Переключаем маркер: больше не ищем, готовы к вставке
|
// Переключаем маркер: больше не ищем, готовы к вставке
|
||||||
|
|
@ -310,75 +282,55 @@ fn is_occupied(target: f32, occupied: &[f32], radius_norm: f32) -> bool {
|
||||||
pub fn insert_projectile_into_track(
|
pub fn insert_projectile_into_track(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut projectiles: Query<(Entity, &RoundBallProjectile), With<InsertMarker>>,
|
mut projectiles: Query<(Entity, &RoundBallProjectile), With<InsertMarker>>,
|
||||||
mut balls_on_track: Query<(Entity, &mut RoundBall)>,
|
mut balls_on_track: Query<(Entity, &mut RoundBall, &Transform)>,
|
||||||
track: Res<PrecalculatedTrack>,
|
track: Res<PrecalculatedTrack>,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
) {
|
) {
|
||||||
for (proj_entity, proj) in projectiles.iter_mut() {
|
for (proj_entity, proj) in projectiles.iter_mut() {
|
||||||
let Some(hit) = proj.hit_result else {
|
// let Some(hit) = proj.hit_result else { continue }; // артефактный код, пока просто закоментил
|
||||||
continue;
|
let proj_pos = proj.previous_position;
|
||||||
};
|
|
||||||
|
|
||||||
let Some(target_pos) = proj.target_position else { continue };
|
// ищем любой шарик, до которого снаряд долетел визуально
|
||||||
let distance = proj.previous_position.distance(target_pos);
|
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 {
|
||||||
const ARRIVAL_DIST: f32 = 5.0;
|
target_found = Some((ball_entity, ball.track_progress, ball_pos));
|
||||||
|
break; // нашли первую коллизию - дальше не ищем
|
||||||
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;
|
|
||||||
|
|
||||||
// ставим на место шарика-цели, а сам шарик-цель сдвигаем вперед по треку
|
|
||||||
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() {
|
// target_entity - артефактный код, пока просто закоментил
|
||||||
ball.track_progress = (ball.track_progress + shift).clamp(0.0, 1.0);
|
let Some((target_entity, original_progress, target_ball_pos)) = target_found else {
|
||||||
|
continue; // ни с одним шариком не столкнулся - летит дальше
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// спавним новый шарик на месте цели
|
// спавним новый шарик ровно в позицию целевого
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Sprite {
|
Sprite {
|
||||||
image: asset_server.load(proj.ball_type.asset_path()),
|
image: asset_server.load(proj.ball_type.asset_path()),
|
||||||
custom_size: Some(Vec2::splat(ROUND_BALL_SIZE)),
|
custom_size: Some(Vec2::splat(ROUND_BALL_SIZE)),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
Transform::from_translation(hit.hit_position.extend(ROUND_BALL_Z)),
|
Transform::from_translation(target_ball_pos.extend(ROUND_BALL_Z)),
|
||||||
RoundBall {
|
RoundBall {
|
||||||
ball_type: proj.ball_type,
|
ball_type: proj.ball_type,
|
||||||
track_progress: original_progress,
|
track_progress: original_progress, // занимает место шарика, в который попали
|
||||||
},
|
},
|
||||||
LinearStateMarker,
|
LinearStateMarker,
|
||||||
|
HitMarker, // маркер для системы уничтожения шариков
|
||||||
));
|
));
|
||||||
|
|
||||||
// деспавн шарика-снаряда
|
|
||||||
commands.entity(proj_entity).despawn();
|
commands.entity(proj_entity).despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue