Compare commits

...

2 commits

Author SHA1 Message Date
nquidox
0f7882d3fb precalculate track + arc helper fn + structs 2026-04-16 22:25:07 +03:00
nquidox
13b9b16644 track extended 2026-04-15 23:11:39 +03:00
4 changed files with 192 additions and 65 deletions

View file

@ -1,17 +1,53 @@
use bevy::prelude::*; use bevy::prelude::*;
#[derive(Component)] #[derive(Resource)]
pub struct Track{ pub struct Track {
pub start_point: Vec2, pub start_point: Vec2,
pub start_direction: Vec2, // будет нормализовано при построении pub start_direction: Vec2, // будет нормализовано при построении
pub segments: Vec<PathSegment>, pub segments: Vec<PathSegment>,
} }
#[derive(Debug)]
pub enum SegementType{
Line,
Turn,
}
// линия всегда строится от точки старта по направлению // линия всегда строится от точки старта по направлению
// направление изменяется сегментами поворота // направление изменяется сегментами поворота
// left при истине - поворот против часовой стрелки, ложь - по часовой // left при истине - поворот против часовой стрелки, ложь - по часовой
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum PathSegment{ pub enum PathSegment {
Line { length: f32 }, Line { length: f32 },
Turn { radius: f32, left: bool }, Turn { radius: f32, left: bool },
// TODO заменить перечисление на структуру с полем типа SegementType, чтоб был единый источник типа
}
#[derive(Resource)]
pub struct PrecalculatedTrack{
pub segments: Vec<PTSegment>
}
#[derive(Debug)]
pub struct PTSegment {
pub t_start: f32,
pub t_end: f32,
pub segment_type: SegementType,
pub start_pos: Vec2,
pub direction: Vec2,
pub center: Vec2,
pub radius: f32,
pub start_angle: f32,
pub sweep_sign: f32,
}
#[derive(Debug, Clone, Copy)]
pub struct ArcCalculation {
pub normal: Vec2,
pub center: Vec2,
pub start_angle: f32,
pub sweep_sign: f32,
pub end_pos: Vec2,
} }

View file

@ -1,5 +1,6 @@
use bevy::prelude::*; use bevy::prelude::*;
use crate::states::AppState::LinearState; use crate::states::AppState::LinearState;
use crate::states::level::GameOver;
use crate::states::linear::*; use crate::states::linear::*;
pub struct LinearPlugin; pub struct LinearPlugin;
@ -7,16 +8,26 @@ pub struct LinearPlugin;
impl Plugin for LinearPlugin { impl Plugin for LinearPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app app
.add_systems(OnEnter (LinearState), (setup, setup_linear_track).chain()) .add_systems(OnEnter (LinearState), setup)
.add_systems(Update, (draw_track_gizmos).run_if(in_state(LinearState))) .add_systems(Update, (draw_track_gizmos).run_if(in_state(LinearState)))
.add_systems(OnExit (LinearState), cleanup); .add_systems(OnExit (LinearState), cleanup);
} }
} }
fn setup(mut commands: Commands) {} fn setup(mut commands: Commands) {
let build_track = setup_linear_track();
let precalculated_track = precalculate_track(&build_track);
commands.insert_resource(build_track);
commands.insert_resource(precalculated_track);
}
fn cleanup(mut commands: Commands, query: Query<Entity, With<LinearStateMarker>>) { fn cleanup(mut commands: Commands, query: Query<Entity, With<LinearStateMarker>>) {
// зачищаем сущности
for entity in query.iter() { for entity in query.iter() {
commands.entity(entity).despawn(); commands.entity(entity).despawn();
} }
// зачищаем ресурсы по типу
commands.remove_resource::<Track>();
} }

View file

@ -1,15 +1,13 @@
use crate::states; use crate::states;
use bevy::prelude::*; use bevy::prelude::*;
use states::linear::*; use states::linear::*;
use std::f32::consts::{FRAC_PI_2, PI}; use std::f32::consts::FRAC_PI_2;
const PINK: Color = Color::srgb_u8(250, 0, 155); const PINK: Color = Color::srgb_u8(250, 0, 155);
const BLUE: Color = Color::srgb_u8(0, 0, 255); const BLUE: Color = Color::srgb_u8(0, 0, 255);
const GREEN: Color = Color::srgb_u8(0, 255, 0); const GREEN: Color = Color::srgb_u8(0, 255, 0);
pub fn draw_track_gizmos(query: Query<&Track>, mut gizmos: Gizmos) { pub fn draw_track_gizmos(track: Res<Track>, mut gizmos: Gizmos) {
println!("Рисуем трек из примитивов");
for track in &query {
let mut current_pos = track.start_point; let mut current_pos = track.start_point;
let mut current_dir = track.start_direction.normalize(); let mut current_dir = track.start_direction.normalize();
@ -26,35 +24,20 @@ pub fn draw_track_gizmos(query: Query<&Track>, mut gizmos: Gizmos) {
} }
PathSegment::Turn { radius, left } => { PathSegment::Turn { radius, left } => {
// вычисляем направление поворота и знак для угла // вычисляем нормаль, центр, угол поворота арки и знак
let (normal, sign): (Vec2, f32) = if *left { let arc = helper_arc_calculator(current_pos, current_dir, *left, *radius);
(Vec2::new(-current_dir.y, current_dir.x), -1.0)
} else {
(Vec2::new(current_dir.y, -current_dir.x), 1.0)
};
let center = current_pos + normal * radius;
let start_vec = current_pos - center;
let initial_vec = if *left {
-start_vec.perp()
} else {
-start_vec
};
gizmos.arc_2d( gizmos.arc_2d(
Isometry2d::new(center, Rot2::radians(initial_vec.to_angle())), Isometry2d::new(arc.center, Rot2::radians(arc.start_angle)),
FRAC_PI_2, FRAC_PI_2,
*radius, *radius,
GREEN, GREEN,
); );
// current_pos = center + current_dir * radius; current_pos = arc.end_pos;
// current_pos = center + normal.perp() * radius; current_dir = arc.normal;
current_pos = center + normal.perp() * radius * sign;
current_dir = normal;
} }
} }
} }
// рисуем финиш // рисуем финиш
gizmos.circle_2d(current_pos, 5.0, Color::WHITE); gizmos.circle_2d(current_pos, 5.0, Color::WHITE);
}
} }

View file

@ -1,6 +1,8 @@
use std::f32::consts::FRAC_PI_2;
use crate::states::linear::*; use crate::states::linear::*;
use crate::{FACTOR, HEIGHT, WIDTH}; use crate::{FACTOR};
use bevy::prelude::*; use bevy::prelude::*;
use bevy::reflect::List;
pub const CENTER_X: f32 = 0.0; pub const CENTER_X: f32 = 0.0;
pub const CENTER_Y: f32 = 0.0; pub const CENTER_Y: f32 = 0.0;
@ -11,10 +13,8 @@ pub const STEP: f32 = FACTOR as f32 / SCALE;
pub const SCALE: f32 = 2.0; pub const SCALE: f32 = 2.0;
pub fn setup_linear_track(mut commands: Commands) { pub fn setup_linear_track() -> Track {
println!("Построение трека начато"); println!("Построение трека начато");
commands.spawn((
Track { Track {
start_point: Vec2 { start_point: Vec2 {
x: CENTER_X - FACTOR as f32 * 7.0, x: CENTER_X - FACTOR as f32 * 7.0,
@ -22,23 +22,120 @@ pub fn setup_linear_track(mut commands: Commands) {
}, },
start_direction: Vec2::X, start_direction: Vec2::X,
segments: vec![ segments: vec![
PathSegment::Line { length: STEP * 2.0 }, PathSegment::Line { length: STEP * 6.0 },
PathSegment::Turn { radius: STEP, left: true }, PathSegment::Turn { radius: STEP, left: true },
PathSegment::Line { length: STEP }, PathSegment::Line { length: STEP },
PathSegment::Turn { radius: STEP, left: false }, PathSegment::Turn { radius: STEP, left: false },
PathSegment::Line { length: STEP * 10.0 }, PathSegment::Line { length: STEP * 18.0 },
PathSegment::Turn { radius: STEP, left: true }, PathSegment::Turn { radius: STEP, left: true },
PathSegment::Line { length: STEP * 4.0 }, PathSegment::Line { length: STEP * 4.0 },
PathSegment::Turn { radius: STEP, left: true }, PathSegment::Turn { radius: STEP, left: true },
PathSegment::Turn { radius: STEP, left: false }, PathSegment::Turn { radius: STEP, left: false },
PathSegment::Line { length: STEP * 4.0 },
PathSegment::Turn { radius: STEP, left: true },
PathSegment::Line { length: STEP * 20.0 },
PathSegment::Turn { radius: STEP, left: true },
PathSegment::Line { length: STEP * 5.0 },
PathSegment::Turn { radius: STEP, left: true },
PathSegment::Line { length: STEP * 10.0 },
PathSegment::Turn { radius: STEP, left: false },
PathSegment::Line { length: STEP * 2.0 }, PathSegment::Line { length: STEP * 2.0 },
PathSegment::Turn { radius: STEP, left: true },
PathSegment::Line { length: STEP * 6.0 },
PathSegment::Turn { radius: STEP, left: true },
PathSegment::Line { length: STEP * 1.0 },
PathSegment::Turn { radius: STEP, left: true },
], ],
}, }
LinearStateMarker, }
));
pub fn precalculate_track(track: &Track) -> PrecalculatedTrack {
let mut pt = PrecalculatedTrack { segments: vec![] };
let mut cumulative_length: Vec<f32> = vec![0.0];
let mut current_pos = track.start_point;
let mut current_dir = track.start_direction;
// на первом проходе заполняем все, кроме t_start и t_end,
// потому что мы не можем посчитать их без общей длины
for seg in track.segments.iter(){
match seg {
PathSegment::Line { length } => {
let calc_seg = PTSegment {
t_start: 0.0,
t_end: 0.0,
segment_type: SegementType::Line,
start_pos: current_pos,
direction: current_dir,
center: Vec2::ZERO,
radius: 0.0,
start_angle: 0.0,
sweep_sign: 0.0,
};
pt.segments.push(calc_seg);
// сдвигаем позицию для следующего сегмента
current_pos = current_pos + length * current_dir;
// направление не меняем, пропуск
// накапливаем длину
cumulative_length.push(cumulative_length[cumulative_length.len() -1] + *length);
}
PathSegment::Turn {radius, left } => {
let arc = helper_arc_calculator(current_pos, current_dir, *left, *radius);
let calc_seg = PTSegment {
t_start: 0.0,
t_end: 0.0,
segment_type: SegementType::Turn,
start_pos: current_pos,
direction: current_dir,
center: arc.center,
radius: *radius,
start_angle: arc.start_angle,
sweep_sign: arc.sweep_sign,
};
pt.segments.push(calc_seg);
// сдвигаем позицию для следующего сегмента
current_pos = arc.end_pos;
current_dir = arc.normal;
// накапливаем длину
cumulative_length.push(cumulative_length[cumulative_length.len() -1] + (FRAC_PI_2 * radius));
}
}
}
let last_index = cumulative_length.len() - 1;
let total_length = cumulative_length[last_index];
// итерируемся уже по прекалькулированному вектору
// заполняем точки на треке в пересчете на общую длину
println!("Precalculated track:");
for (i, seg) in pt.segments.iter_mut().enumerate() {
seg.t_start = cumulative_length[i] / total_length;
if i < last_index {
seg.t_end = cumulative_length[i+1] / total_length;
} else {
seg.t_end = 1.0;
}
println!("{:?}", seg);
}
pt
}
pub fn helper_arc_calculator(pos: Vec2, dir: Vec2, turn_to: bool, radius: f32) -> ArcCalculation{
let (normal, sign): (Vec2, f32) = if turn_to {
(Vec2::new(-dir.y, dir.x), -1.0)
} else {
(Vec2::new(dir.y, -dir.x), 1.0)
};
let center = pos + normal * radius;
let start_vec = pos - center;
let initial_vec = if turn_to { -start_vec.perp() } else { -start_vec };
let angle = initial_vec.to_angle();
ArcCalculation{
normal,
center,
start_angle: angle,
sweep_sign: sign,
end_pos: center + normal.perp() * radius * sign,
}
} }