From 13b9b16644c40bbe855c43c8d98a7c3d95c957e2 Mon Sep 17 00:00:00 2001 From: nquidox Date: Wed, 15 Apr 2026 23:11:39 +0300 Subject: [PATCH 1/2] track extended --- src/states/linear/systems_track.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/states/linear/systems_track.rs b/src/states/linear/systems_track.rs index 0a223f0..25a0849 100644 --- a/src/states/linear/systems_track.rs +++ b/src/states/linear/systems_track.rs @@ -22,21 +22,24 @@ pub fn setup_linear_track(mut commands: Commands) { }, start_direction: Vec2::X, segments: vec![ - PathSegment::Line { length: STEP * 2.0 }, + PathSegment::Line { length: STEP * 6.0 }, PathSegment::Turn { radius: STEP, left: true }, PathSegment::Line { length: STEP }, 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::Line { length: STEP * 4.0 }, PathSegment::Turn { radius: STEP, left: true }, 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::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, From 0f7882d3fb0ce0403887e0e3815300005ed9e3f1 Mon Sep 17 00:00:00 2001 From: nquidox Date: Thu, 16 Apr 2026 22:25:07 +0300 Subject: [PATCH 2/2] precalculate track + arc helper fn + structs --- src/states/linear/components_track.rs | 44 ++++++++++- src/states/linear/plugin.rs | 15 +++- src/states/linear/systems.rs | 73 +++++++---------- src/states/linear/systems_track.rs | 108 ++++++++++++++++++++++++-- 4 files changed, 182 insertions(+), 58 deletions(-) diff --git a/src/states/linear/components_track.rs b/src/states/linear/components_track.rs index 36ab136..a9d286d 100644 --- a/src/states/linear/components_track.rs +++ b/src/states/linear/components_track.rs @@ -1,17 +1,53 @@ use bevy::prelude::*; -#[derive(Component)] -pub struct Track{ +#[derive(Resource)] +pub struct Track { pub start_point: Vec2, pub start_direction: Vec2, // будет нормализовано при построении pub segments: Vec, } +#[derive(Debug)] +pub enum SegementType{ + Line, + Turn, +} + // линия всегда строится от точки старта по направлению // направление изменяется сегментами поворота // left при истине - поворот против часовой стрелки, ложь - по часовой #[derive(Clone, Copy)] -pub enum PathSegment{ +pub enum PathSegment { Line { length: f32 }, - Turn { radius: f32, left: bool }, + Turn { radius: f32, left: bool }, + // TODO заменить перечисление на структуру с полем типа SegementType, чтоб был единый источник типа +} + + + +#[derive(Resource)] +pub struct PrecalculatedTrack{ + pub segments: Vec +} + +#[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, } \ No newline at end of file diff --git a/src/states/linear/plugin.rs b/src/states/linear/plugin.rs index d1b4e7f..09d1a1e 100644 --- a/src/states/linear/plugin.rs +++ b/src/states/linear/plugin.rs @@ -1,5 +1,6 @@ use bevy::prelude::*; use crate::states::AppState::LinearState; +use crate::states::level::GameOver; use crate::states::linear::*; pub struct LinearPlugin; @@ -7,16 +8,26 @@ pub struct LinearPlugin; impl Plugin for LinearPlugin { fn build(&self, app: &mut 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(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>) { + // зачищаем сущности for entity in query.iter() { commands.entity(entity).despawn(); } + + // зачищаем ресурсы по типу + commands.remove_resource::(); } \ No newline at end of file diff --git a/src/states/linear/systems.rs b/src/states/linear/systems.rs index ab89b70..af68fe8 100644 --- a/src/states/linear/systems.rs +++ b/src/states/linear/systems.rs @@ -1,60 +1,43 @@ use crate::states; use bevy::prelude::*; 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 BLUE: Color = Color::srgb_u8(0, 0, 255); const GREEN: Color = Color::srgb_u8(0, 255, 0); -pub fn draw_track_gizmos(query: Query<&Track>, mut gizmos: Gizmos) { - println!("Рисуем трек из примитивов"); - for track in &query { - let mut current_pos = track.start_point; - let mut current_dir = track.start_direction.normalize(); +pub fn draw_track_gizmos(track: Res, mut gizmos: Gizmos) { + let mut current_pos = track.start_point; + let mut current_dir = track.start_direction.normalize(); - // точка старта - gizmos.circle_2d(current_pos, 5.0, BLUE); + // точка старта + gizmos.circle_2d(current_pos, 5.0, BLUE); - // рисуем сегменты - for segment in &track.segments { - match segment { - PathSegment::Line { length } => { - let end_pos = current_pos + current_dir * length; - gizmos.line_2d(current_pos, end_pos, PINK); - current_pos = end_pos - } + // рисуем сегменты + for segment in &track.segments { + match segment { + PathSegment::Line { length } => { + let end_pos = current_pos + current_dir * length; + gizmos.line_2d(current_pos, end_pos, PINK); + current_pos = end_pos + } - PathSegment::Turn { radius, left } => { - // вычисляем направление поворота и знак для угла - let (normal, sign): (Vec2, f32) = if *left { - (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( - Isometry2d::new(center, Rot2::radians(initial_vec.to_angle())), - FRAC_PI_2, - *radius, - GREEN, - ); - // current_pos = center + current_dir * radius; - // current_pos = center + normal.perp() * radius; - current_pos = center + normal.perp() * radius * sign; - current_dir = normal; - } + PathSegment::Turn { radius, left } => { + // вычисляем нормаль, центр, угол поворота арки и знак + let arc = helper_arc_calculator(current_pos, current_dir, *left, *radius); + + gizmos.arc_2d( + Isometry2d::new(arc.center, Rot2::radians(arc.start_angle)), + FRAC_PI_2, + *radius, + GREEN, + ); + current_pos = arc.end_pos; + current_dir = arc.normal; } } - // рисуем финиш - gizmos.circle_2d(current_pos, 5.0, Color::WHITE); } + // рисуем финиш + gizmos.circle_2d(current_pos, 5.0, Color::WHITE); } diff --git a/src/states/linear/systems_track.rs b/src/states/linear/systems_track.rs index 25a0849..60b0172 100644 --- a/src/states/linear/systems_track.rs +++ b/src/states/linear/systems_track.rs @@ -1,6 +1,8 @@ +use std::f32::consts::FRAC_PI_2; use crate::states::linear::*; -use crate::{FACTOR, HEIGHT, WIDTH}; +use crate::{FACTOR}; use bevy::prelude::*; +use bevy::reflect::List; pub const CENTER_X: 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 fn setup_linear_track(mut commands: Commands) { +pub fn setup_linear_track() -> Track { println!("Построение трека начато"); - - commands.spawn(( Track { start_point: Vec2 { x: CENTER_X - FACTOR as f32 * 7.0, @@ -41,7 +41,101 @@ pub fn setup_linear_track(mut commands: Commands) { PathSegment::Turn { radius: STEP, left: false }, PathSegment::Line { length: STEP * 2.0 }, ], - }, - LinearStateMarker, - )); + } } + +pub fn precalculate_track(track: &Track) -> PrecalculatedTrack { + let mut pt = PrecalculatedTrack { segments: vec![] }; + let mut cumulative_length: Vec = 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, + } +} \ No newline at end of file