Compare commits

..

3 commits

Author SHA1 Message Date
nquidox
895193778b linear state integration 2026-04-15 23:04:03 +03:00
nquidox
c5a4d77684 log text 2026-04-15 23:03:43 +03:00
nquidox
68c895ec14 linear track plugin 2026-04-15 23:02:14 +03:00
16 changed files with 203 additions and 12 deletions

View file

@ -13,6 +13,7 @@ mod ui;
use crate::states::game::state::MainGameState;
use crate::states::main_menu::state::MainMenuState;
use crate::states::settings_menu::state::SettingsMenuState;
use crate::states::linear::plugin::LinearPlugin;
const FACTOR: u32 = 80;
const WIDTH: u32 = 16;
@ -36,6 +37,7 @@ fn main() {
MainMenuState,
SettingsMenuState,
MainGameState,
LinearPlugin,
))
.add_systems(Startup, setup)
.add_systems(Update, exit_system)

View file

@ -8,4 +8,5 @@ pub enum AppState {
GameState,
GameRestart,
LevelState,
LinearState,
}

View file

@ -9,7 +9,7 @@ pub fn restart_game_button_system(
) {
for interaction in &interaction_query {
if matches!(interaction, Interaction::Pressed) {
println!("🔄 Кнопка Restart нажата! Переход в Restarting...");
println!("Сброс состояния");
next_state.set(AppState::GameRestart);
}
}

View file

@ -0,0 +1,4 @@
use bevy::prelude::*;
#[derive(Component)]
pub struct LinearStateMarker;

View file

@ -0,0 +1,17 @@
use bevy::prelude::*;
#[derive(Component)]
pub struct Track{
pub start_point: Vec2,
pub start_direction: Vec2, // будет нормализовано при построении
pub segments: Vec<PathSegment>,
}
// линия всегда строится от точки старта по направлению
// направление изменяется сегментами поворота
// left при истине - поворот против часовой стрелки, ложь - по часовой
#[derive(Clone, Copy)]
pub enum PathSegment{
Line { length: f32 },
Turn { radius: f32, left: bool },
}

13
src/states/linear/mod.rs Normal file
View file

@ -0,0 +1,13 @@
pub mod plugin;
mod components;
pub use components::*;
mod systems;
pub use systems::*;
mod components_track;
pub use components_track::*;
mod systems_track;
pub use systems_track::*;

View file

@ -0,0 +1,22 @@
use bevy::prelude::*;
use crate::states::AppState::LinearState;
use crate::states::linear::*;
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(Update, (draw_track_gizmos).run_if(in_state(LinearState)))
.add_systems(OnExit (LinearState), cleanup);
}
}
fn setup(mut commands: Commands) {}
fn cleanup(mut commands: Commands, query: Query<Entity, With<LinearStateMarker>>) {
for entity in query.iter() {
commands.entity(entity).despawn();
}
}

View file

@ -0,0 +1,60 @@
use crate::states;
use bevy::prelude::*;
use states::linear::*;
use std::f32::consts::{FRAC_PI_2, PI};
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();
// точка старта
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
}
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;
}
}
}
// рисуем финиш
gizmos.circle_2d(current_pos, 5.0, Color::WHITE);
}
}

View file

@ -0,0 +1,44 @@
use crate::states::linear::*;
use crate::{FACTOR, HEIGHT, WIDTH};
use bevy::prelude::*;
pub const CENTER_X: f32 = 0.0;
pub const CENTER_Y: f32 = 0.0;
// pub const FACTOR_RADIUS: f32 = FACTOR as f32 / 2.0;
pub const STEP: f32 = FACTOR as f32 / SCALE;
pub const SCALE: f32 = 2.0;
pub fn setup_linear_track(mut commands: Commands) {
println!("Построение трека начато");
commands.spawn((
Track {
start_point: Vec2 {
x: CENTER_X - FACTOR as f32 * 7.0,
y: CENTER_Y - FACTOR as f32 * 4.0,
},
start_direction: Vec2::X,
segments: vec![
PathSegment::Line { length: STEP * 2.0 },
PathSegment::Turn { radius: STEP, left: true },
PathSegment::Line { length: STEP },
PathSegment::Turn { radius: STEP, left: false },
PathSegment::Line { length: STEP * 10.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 * 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,
));
}

View file

@ -8,3 +8,6 @@ pub struct MainMenuNewGameButton;
#[derive(Component, Copy, Clone)]
pub struct MainMenuSettingsButton;
#[derive(Component, Copy, Clone)]
pub struct MainMenuLinearButton;

View file

@ -1,5 +1,7 @@
pub mod state;
pub mod components;
pub(crate) mod state;
pub use state::*;
mod components;
pub use components::*;
pub mod systems;
mod systems;
pub use systems::*;
pub mod plugin;

View file

@ -1,5 +1,5 @@
use bevy::prelude::*;
use crate::states::main_menu::{MainMenuNewGameButton, MainMenuSettingsButton};
use crate::states::main_menu::*;
use crate::ui::button_click::ButtonClickMessage;
use crate::ui::click::handle_click_system;
@ -12,11 +12,13 @@ impl Plugin for MainMenuUiPlugin {
fn build(&self, app: &mut App) {
app.add_message::<ButtonClickMessage<MainMenuNewGameButton>>()
.add_message::<ButtonClickMessage<MainMenuSettingsButton>>()
.add_message::<ButtonClickMessage<MainMenuLinearButton>>()
.add_systems(
Update,
(
handle_click_system::<MainMenuNewGameButton>,
handle_click_system::<MainMenuSettingsButton>,
handle_click_system::<MainMenuLinearButton>,
).in_set(MainMenuButtonSet)
);
}

View file

@ -1,6 +1,5 @@
use crate::states::AppState;
use crate::states::main_menu::{MainMenuNewGameButton, MainMenuRootMarker, MainMenuSettingsButton};
use crate::states::main_menu::systems::*;
use crate::states::*;
use crate::states::main_menu::*;
use crate::ui::button_hover::button_hover;
use crate::ui::{ButtonStyle, spawn_background, spawn_button};
use bevy::prelude::*;
@ -20,6 +19,7 @@ impl Plugin for MainMenuState {
button_hover,
new_game_button_system,
settings_button_system,
linear_button_system,
)
.run_if(in_state(AppState::MainMenu)),
)
@ -92,6 +92,14 @@ fn setup_main_menu(mut commands: Commands, asset_server: Res<AssetServer>) {
MainMenuNewGameButton,
);
spawn_button(
&mut commands,
menu_root,
"Линейное",
&mm_button_style,
MainMenuLinearButton,
);
spawn_button(
&mut commands,
menu_root,

View file

@ -1,5 +1,5 @@
use crate::states::AppState;
use crate::states::main_menu::{MainMenuNewGameButton, MainMenuSettingsButton};
use crate::states::main_menu::*;
use bevy::prelude::*;
pub fn new_game_button_system(
@ -23,3 +23,15 @@ pub fn settings_button_system(
}
}
}
pub fn linear_button_system(
interaction_query: Query<&Interaction, (Changed<Interaction>, With<MainMenuLinearButton>)>,
mut next_state: ResMut<NextState<AppState>>,
) {
for interaction in &interaction_query {
if matches!(interaction, Interaction::Pressed) {
println!("Линейное нажата");
next_state.set(AppState::LinearState);
}
}
}

View file

@ -6,3 +6,4 @@ pub mod main_menu;
pub mod settings_menu;
pub mod game;
mod level;
pub mod linear;