Compare commits
7 commits
0f7882d3fb
...
b95dde79a1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b95dde79a1 | ||
|
|
393e199a00 | ||
|
|
6506955115 | ||
|
|
0c7ceb5d8d | ||
|
|
a1180ea769 | ||
|
|
5ba555b062 | ||
|
|
7b334b33c0 |
13 changed files with 385 additions and 86 deletions
|
|
@ -13,7 +13,7 @@ mod ui;
|
||||||
use crate::states::game::state::MainGameState;
|
use crate::states::game::state::MainGameState;
|
||||||
use crate::states::main_menu::state::MainMenuState;
|
use crate::states::main_menu::state::MainMenuState;
|
||||||
use crate::states::settings_menu::state::SettingsMenuState;
|
use crate::states::settings_menu::state::SettingsMenuState;
|
||||||
use crate::states::linear::plugin::LinearPlugin;
|
use crate::states::linear::plugin::LinearPlayPlugin;
|
||||||
|
|
||||||
const FACTOR: u32 = 80;
|
const FACTOR: u32 = 80;
|
||||||
const WIDTH: u32 = 16;
|
const WIDTH: u32 = 16;
|
||||||
|
|
@ -25,7 +25,7 @@ fn main() {
|
||||||
.add_message::<ExitRequestMessage>()
|
.add_message::<ExitRequestMessage>()
|
||||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
primary_window: Some(Window {
|
primary_window: Some(Window {
|
||||||
title: "alpha".into(),
|
title: "alpha v0.2".into(),
|
||||||
resolution: (WIDTH * FACTOR, HEIGHT * FACTOR).into(),
|
resolution: (WIDTH * FACTOR, HEIGHT * FACTOR).into(),
|
||||||
resizable: true,
|
resizable: true,
|
||||||
..default()
|
..default()
|
||||||
|
|
@ -37,7 +37,7 @@ fn main() {
|
||||||
MainMenuState,
|
MainMenuState,
|
||||||
SettingsMenuState,
|
SettingsMenuState,
|
||||||
MainGameState,
|
MainGameState,
|
||||||
LinearPlugin,
|
LinearPlayPlugin,
|
||||||
))
|
))
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, exit_system)
|
.add_systems(Update, exit_system)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,6 @@ pub enum AppState {
|
||||||
SettingsMenu,
|
SettingsMenu,
|
||||||
GameState,
|
GameState,
|
||||||
GameRestart,
|
GameRestart,
|
||||||
LevelState,
|
LinearPlayState,
|
||||||
LinearState,
|
LinearGameRestart,
|
||||||
}
|
}
|
||||||
|
|
@ -2,3 +2,6 @@ use bevy::prelude::*;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct LinearStateMarker;
|
pub struct LinearStateMarker;
|
||||||
|
|
||||||
|
#[derive(Component, Copy, Clone)]
|
||||||
|
pub struct LinearRestartMarker;
|
||||||
35
src/states/linear/components_ball.rs
Normal file
35
src/states/linear/components_ball.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
use bevy::prelude::Component;
|
||||||
|
use rand::prelude::IndexedRandom;
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Clone, Copy)]
|
||||||
|
pub struct RoundBall {
|
||||||
|
pub ball_type: RoundBallType,
|
||||||
|
pub track_progress: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
|
pub enum RoundBallType {
|
||||||
|
#[default]
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue,
|
||||||
|
Purple,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RoundBallType {
|
||||||
|
pub const fn asset_path(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
RoundBallType::Red => "sprites/round/red.png",
|
||||||
|
RoundBallType::Green => "sprites/round/green.png",
|
||||||
|
RoundBallType::Blue => "sprites/round/blue.png",
|
||||||
|
RoundBallType::Purple => "sprites/round/purple.png",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random_from_4() -> Self {
|
||||||
|
[Self::Red, Self::Green, Self::Blue, Self::Purple]
|
||||||
|
.choose(&mut rand::rng())
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,10 @@ pub enum PathSegment {
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct PrecalculatedTrack{
|
pub struct PrecalculatedTrack{
|
||||||
pub segments: Vec<PTSegment>
|
pub segments: Vec<PTSegment>,
|
||||||
|
pub min_spawn_gap: f32, //нормализован
|
||||||
|
pub total_length: f32,
|
||||||
|
pub speed_norm: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -41,6 +44,7 @@ pub struct PTSegment {
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
pub start_angle: f32,
|
pub start_angle: f32,
|
||||||
pub sweep_sign: f32,
|
pub sweep_sign: f32,
|
||||||
|
pub length: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
|
||||||
18
src/states/linear/constants.rs
Normal file
18
src/states/linear/constants.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::FACTOR;
|
||||||
|
use bevy::color::Color;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Z-INDEXES
|
||||||
|
pub const ROUND_BALL_Z: f32 = 10.0;
|
||||||
|
|
||||||
|
// COLORS
|
||||||
|
pub const PINK: Color = Color::srgb_u8(250, 0, 155);
|
||||||
|
pub const BLUE: Color = Color::srgb_u8(0, 0, 255);
|
||||||
|
pub const GREEN: Color = Color::srgb_u8(0, 255, 0);
|
||||||
|
|
@ -11,3 +11,15 @@ pub use components_track::*;
|
||||||
|
|
||||||
mod systems_track;
|
mod systems_track;
|
||||||
pub use systems_track::*;
|
pub use systems_track::*;
|
||||||
|
mod components_ball;
|
||||||
|
pub use components_ball::*;
|
||||||
|
|
||||||
|
mod system_ball;
|
||||||
|
pub use system_ball::*;
|
||||||
|
mod constants;
|
||||||
|
pub use constants::*;
|
||||||
|
|
||||||
|
mod ui;
|
||||||
|
pub use ui::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,36 @@
|
||||||
use bevy::prelude::*;
|
use crate::states::AppState;
|
||||||
use crate::states::AppState::LinearState;
|
use crate::states::AppState::{LinearGameRestart, LinearPlayState};
|
||||||
use crate::states::level::GameOver;
|
|
||||||
use crate::states::linear::*;
|
use crate::states::linear::*;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
pub struct LinearPlugin;
|
pub struct LinearPlayPlugin;
|
||||||
|
|
||||||
impl Plugin for LinearPlugin {
|
// создаем енум, чтоб не указывать run_if(in_state) для каждой системы
|
||||||
|
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub enum LinearUpdateSet {
|
||||||
|
Track,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin for LinearPlayPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app
|
app
|
||||||
.add_systems(OnEnter (LinearState), setup)
|
.add_systems(OnEnter(LinearPlayState), setup)
|
||||||
.add_systems(Update, (draw_track_gizmos).run_if(in_state(LinearState)))
|
.add_systems(OnEnter(LinearGameRestart), linear_restart)
|
||||||
.add_systems(OnExit (LinearState), cleanup);
|
.add_plugins(LinearUIPlugin)
|
||||||
|
.configure_sets(
|
||||||
|
Update,
|
||||||
|
LinearUpdateSet::Track.run_if(in_state(LinearPlayState)),
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
(
|
||||||
|
draw_track_gizmos,
|
||||||
|
spawn_round_ball,
|
||||||
|
move_round_balls,
|
||||||
|
)
|
||||||
|
.in_set(LinearUpdateSet::Track),
|
||||||
|
)
|
||||||
|
.add_systems(OnExit(LinearPlayState), cleanup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,4 +50,9 @@ fn cleanup(mut commands: Commands, query: Query<Entity, With<LinearStateMarker>>
|
||||||
|
|
||||||
// зачищаем ресурсы по типу
|
// зачищаем ресурсы по типу
|
||||||
commands.remove_resource::<Track>();
|
commands.remove_resource::<Track>();
|
||||||
|
commands.remove_resource::<PrecalculatedTrack>();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn linear_restart(mut next_state: ResMut<NextState<AppState>>) {
|
||||||
|
next_state.set(LinearPlayState);
|
||||||
}
|
}
|
||||||
74
src/states/linear/system_ball.rs
Normal file
74
src/states/linear/system_ball.rs
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
use std::f32::consts::FRAC_PI_2;
|
||||||
|
use crate::states::linear::*;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub fn spawn_round_ball(
|
||||||
|
track: Res<PrecalculatedTrack>,
|
||||||
|
mut commands: Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
balls: Query<&RoundBall>,
|
||||||
|
) {
|
||||||
|
// метод all выдаст истину, если все элементы выполняют условие
|
||||||
|
let spawn_allowed = balls
|
||||||
|
.iter()
|
||||||
|
.all(|b| b.track_progress >= track.min_spawn_gap);
|
||||||
|
|
||||||
|
let min_progress = balls
|
||||||
|
.iter()
|
||||||
|
.map(|b| b.track_progress)
|
||||||
|
.reduce(f32::min)
|
||||||
|
.unwrap_or(1.0);
|
||||||
|
|
||||||
|
if min_progress >= track.min_spawn_gap {
|
||||||
|
let ball_type = RoundBallType::random_from_4();
|
||||||
|
let image: Handle<Image> = asset_server.load(ball_type.asset_path());
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Sprite {
|
||||||
|
image,
|
||||||
|
custom_size: Some(Vec2::splat(STEP)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_translation((track.segments[0].start_pos).extend(ROUND_BALL_Z)),
|
||||||
|
RoundBall {
|
||||||
|
ball_type,
|
||||||
|
track_progress: 0.0,
|
||||||
|
},
|
||||||
|
LinearStateMarker,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_round_balls(
|
||||||
|
track: Res<PrecalculatedTrack>,
|
||||||
|
mut balls: Query<(&mut Transform, &mut RoundBall)>,
|
||||||
|
time: Res<Time>,
|
||||||
|
){
|
||||||
|
// собираем все существующие шарики на треке и их трансформы
|
||||||
|
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{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transform.translation = pos.extend(ROUND_BALL_Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,6 @@ use bevy::prelude::*;
|
||||||
use states::linear::*;
|
use states::linear::*;
|
||||||
use std::f32::consts::FRAC_PI_2;
|
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(track: Res<Track>, mut gizmos: Gizmos) {
|
pub fn draw_track_gizmos(track: Res<Track>, mut gizmos: Gizmos) {
|
||||||
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();
|
||||||
|
|
@ -27,12 +23,27 @@ pub fn draw_track_gizmos(track: Res<Track>, mut gizmos: Gizmos) {
|
||||||
// вычисляем нормаль, центр, угол поворота арки и знак
|
// вычисляем нормаль, центр, угол поворота арки и знак
|
||||||
let arc = helper_arc_calculator(current_pos, current_dir, *left, *radius);
|
let arc = helper_arc_calculator(current_pos, current_dir, *left, *radius);
|
||||||
|
|
||||||
gizmos.arc_2d(
|
// арки никак не хотели вставать на свои места
|
||||||
Isometry2d::new(arc.center, Rot2::radians(arc.start_angle)),
|
// gizmos.arc_2d(
|
||||||
FRAC_PI_2,
|
// Isometry2d::new(arc.center, Rot2::degrees(current_angle)),
|
||||||
*radius,
|
// FRAC_PI_2 * arc.sweep_sign,
|
||||||
GREEN,
|
// *radius,
|
||||||
);
|
// GREEN,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// поэтому отрисовка поворота через линии
|
||||||
|
let steps = 16;
|
||||||
|
let mut prev_point = current_pos;
|
||||||
|
|
||||||
|
for i in 1..=steps {
|
||||||
|
let t = i as f32 / steps as f32;
|
||||||
|
let angle = arc.start_angle + arc.sweep_sign * FRAC_PI_2 * t;
|
||||||
|
let point = arc.center + Vec2::from_angle(angle) * *radius;
|
||||||
|
|
||||||
|
gizmos.line_2d(prev_point, point, GREEN);
|
||||||
|
prev_point = point;
|
||||||
|
}
|
||||||
|
|
||||||
current_pos = arc.end_pos;
|
current_pos = arc.end_pos;
|
||||||
current_dir = arc.normal;
|
current_dir = arc.normal;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,7 @@
|
||||||
use std::f32::consts::FRAC_PI_2;
|
use crate::FACTOR;
|
||||||
use crate::states::linear::*;
|
use crate::states::linear::*;
|
||||||
use crate::{FACTOR};
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::reflect::List;
|
use std::f32::consts::FRAC_PI_2;
|
||||||
|
|
||||||
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() -> Track {
|
pub fn setup_linear_track() -> Track {
|
||||||
println!("Построение трека начато");
|
println!("Построение трека начато");
|
||||||
|
|
@ -23,29 +13,67 @@ pub fn setup_linear_track() -> Track {
|
||||||
start_direction: Vec2::X,
|
start_direction: Vec2::X,
|
||||||
segments: vec![
|
segments: vec![
|
||||||
PathSegment::Line { length: STEP * 6.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 {
|
||||||
PathSegment::Line { length: STEP * 18.0 },
|
radius: STEP,
|
||||||
PathSegment::Turn { radius: STEP, left: true },
|
left: false,
|
||||||
|
},
|
||||||
|
PathSegment::Line {
|
||||||
|
length: STEP * 18.0,
|
||||||
|
},
|
||||||
|
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 {
|
||||||
PathSegment::Turn { radius: STEP, left: false },
|
radius: STEP,
|
||||||
|
left: true,
|
||||||
|
},
|
||||||
|
PathSegment::Turn {
|
||||||
|
radius: STEP,
|
||||||
|
left: false,
|
||||||
|
},
|
||||||
PathSegment::Line { length: STEP * 4.0 },
|
PathSegment::Line { length: STEP * 4.0 },
|
||||||
PathSegment::Turn { radius: STEP, left: true },
|
PathSegment::Turn {
|
||||||
PathSegment::Line { length: STEP * 20.0 },
|
radius: STEP,
|
||||||
PathSegment::Turn { radius: STEP, left: true },
|
left: true,
|
||||||
|
},
|
||||||
|
PathSegment::Line {
|
||||||
|
length: STEP * 20.0,
|
||||||
|
},
|
||||||
|
PathSegment::Turn {
|
||||||
|
radius: STEP,
|
||||||
|
left: true,
|
||||||
|
},
|
||||||
PathSegment::Line { length: STEP * 5.0 },
|
PathSegment::Line { length: STEP * 5.0 },
|
||||||
PathSegment::Turn { radius: STEP, left: true },
|
PathSegment::Turn {
|
||||||
PathSegment::Line { length: STEP * 10.0 },
|
radius: STEP,
|
||||||
PathSegment::Turn { radius: STEP, left: false },
|
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 },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn precalculate_track(track: &Track) -> PrecalculatedTrack {
|
pub fn precalculate_track(track: &Track) -> PrecalculatedTrack {
|
||||||
let mut pt = PrecalculatedTrack { segments: vec![] };
|
let mut pt = PrecalculatedTrack {
|
||||||
|
segments: vec![],
|
||||||
|
min_spawn_gap: 0.0,
|
||||||
|
total_length: 0.0,
|
||||||
|
speed_norm: 0.0,
|
||||||
|
};
|
||||||
let mut cumulative_length: Vec<f32> = vec![0.0];
|
let mut cumulative_length: Vec<f32> = vec![0.0];
|
||||||
|
|
||||||
let mut current_pos = track.start_point;
|
let mut current_pos = track.start_point;
|
||||||
|
|
@ -53,7 +81,7 @@ pub fn precalculate_track(track: &Track) -> PrecalculatedTrack {
|
||||||
|
|
||||||
// на первом проходе заполняем все, кроме t_start и t_end,
|
// на первом проходе заполняем все, кроме t_start и t_end,
|
||||||
// потому что мы не можем посчитать их без общей длины
|
// потому что мы не можем посчитать их без общей длины
|
||||||
for seg in track.segments.iter(){
|
for seg in track.segments.iter() {
|
||||||
match seg {
|
match seg {
|
||||||
PathSegment::Line { length } => {
|
PathSegment::Line { length } => {
|
||||||
let calc_seg = PTSegment {
|
let calc_seg = PTSegment {
|
||||||
|
|
@ -66,6 +94,7 @@ pub fn precalculate_track(track: &Track) -> PrecalculatedTrack {
|
||||||
radius: 0.0,
|
radius: 0.0,
|
||||||
start_angle: 0.0,
|
start_angle: 0.0,
|
||||||
sweep_sign: 0.0,
|
sweep_sign: 0.0,
|
||||||
|
length: *length,
|
||||||
};
|
};
|
||||||
|
|
||||||
pt.segments.push(calc_seg);
|
pt.segments.push(calc_seg);
|
||||||
|
|
@ -73,11 +102,12 @@ pub fn precalculate_track(track: &Track) -> PrecalculatedTrack {
|
||||||
current_pos = current_pos + length * current_dir;
|
current_pos = current_pos + length * current_dir;
|
||||||
// направление не меняем, пропуск
|
// направление не меняем, пропуск
|
||||||
// накапливаем длину
|
// накапливаем длину
|
||||||
cumulative_length.push(cumulative_length[cumulative_length.len() -1] + *length);
|
cumulative_length.push(cumulative_length[cumulative_length.len() - 1] + *length);
|
||||||
}
|
}
|
||||||
|
|
||||||
PathSegment::Turn {radius, left } => {
|
PathSegment::Turn { radius, left } => {
|
||||||
let arc = helper_arc_calculator(current_pos, current_dir, *left, *radius);
|
let arc = helper_arc_calculator(current_pos, current_dir, *left, *radius);
|
||||||
|
let arc_len = FRAC_PI_2 * radius;
|
||||||
|
|
||||||
let calc_seg = PTSegment {
|
let calc_seg = PTSegment {
|
||||||
t_start: 0.0,
|
t_start: 0.0,
|
||||||
|
|
@ -89,13 +119,14 @@ pub fn precalculate_track(track: &Track) -> PrecalculatedTrack {
|
||||||
radius: *radius,
|
radius: *radius,
|
||||||
start_angle: arc.start_angle,
|
start_angle: arc.start_angle,
|
||||||
sweep_sign: arc.sweep_sign,
|
sweep_sign: arc.sweep_sign,
|
||||||
|
length: arc_len,
|
||||||
};
|
};
|
||||||
pt.segments.push(calc_seg);
|
pt.segments.push(calc_seg);
|
||||||
// сдвигаем позицию для следующего сегмента
|
// сдвигаем позицию для следующего сегмента
|
||||||
current_pos = arc.end_pos;
|
current_pos = arc.end_pos;
|
||||||
current_dir = arc.normal;
|
current_dir = arc.normal;
|
||||||
// накапливаем длину
|
// накапливаем длину
|
||||||
cumulative_length.push(cumulative_length[cumulative_length.len() -1] + (FRAC_PI_2 * radius));
|
cumulative_length.push(cumulative_length[cumulative_length.len() - 1] + arc_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,33 +140,42 @@ pub fn precalculate_track(track: &Track) -> PrecalculatedTrack {
|
||||||
for (i, seg) in pt.segments.iter_mut().enumerate() {
|
for (i, seg) in pt.segments.iter_mut().enumerate() {
|
||||||
seg.t_start = cumulative_length[i] / total_length;
|
seg.t_start = cumulative_length[i] / total_length;
|
||||||
if i < last_index {
|
if i < last_index {
|
||||||
seg.t_end = cumulative_length[i+1] / total_length;
|
seg.t_end = cumulative_length[i + 1] / total_length;
|
||||||
} else {
|
} else {
|
||||||
seg.t_end = 1.0;
|
seg.t_end = 1.0;
|
||||||
}
|
}
|
||||||
println!("{:?}", seg);
|
println!("{:?}", seg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// считаем нормализованный зазор на треке, когда будет доступен спавн шарика
|
||||||
|
// pt.min_spawn_gap = STEP / total_length;
|
||||||
|
pt.min_spawn_gap = STEP / total_length;
|
||||||
|
pt.total_length = total_length;
|
||||||
|
pt.speed_norm = STEP / total_length; // нормализованное смещение для скорости в пикселях
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Нормализованное значение размера шарика: {}, уе: {}, общая длина: {}",
|
||||||
|
pt.min_spawn_gap, STEP, total_length
|
||||||
|
);
|
||||||
pt
|
pt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn helper_arc_calculator(pos: Vec2, dir: Vec2, turn_to: bool, radius: f32) -> ArcCalculation{
|
pub fn helper_arc_calculator(pos: Vec2, dir: Vec2, turn_to: bool, radius: f32) -> ArcCalculation {
|
||||||
let (normal, sign): (Vec2, f32) = if turn_to {
|
let (normal, sign): (Vec2, f32) = if turn_to {
|
||||||
(Vec2::new(-dir.y, dir.x), -1.0)
|
(Vec2::new(-dir.y, dir.x), 1.0)
|
||||||
} else {
|
} else {
|
||||||
(Vec2::new(dir.y, -dir.x), 1.0)
|
(Vec2::new(dir.y, -dir.x), -1.0)
|
||||||
};
|
};
|
||||||
|
|
||||||
let center = pos + normal * radius;
|
let center = pos + normal * radius;
|
||||||
let start_vec = pos - center;
|
let start_vec = pos - center;
|
||||||
let initial_vec = if turn_to { -start_vec.perp() } else { -start_vec };
|
let angle = start_vec.to_angle();
|
||||||
let angle = initial_vec.to_angle();
|
|
||||||
|
|
||||||
ArcCalculation{
|
ArcCalculation {
|
||||||
normal,
|
normal,
|
||||||
center,
|
center,
|
||||||
start_angle: angle,
|
start_angle: angle,
|
||||||
sweep_sign: sign,
|
sweep_sign: sign,
|
||||||
end_pos: center + normal.perp() * radius * sign,
|
end_pos: center - normal.perp() * radius * sign,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
77
src/states/linear/ui.rs
Normal file
77
src/states/linear/ui.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
use crate::states::AppState;
|
||||||
|
use crate::states::AppState::LinearPlayState;
|
||||||
|
use crate::states::linear::plugin::LinearUpdateSet;
|
||||||
|
use crate::states::linear::{LinearRestartMarker, LinearStateMarker};
|
||||||
|
use crate::ui::button_click::ButtonClickMessage;
|
||||||
|
use crate::ui::click::handle_click_system;
|
||||||
|
use crate::ui::{ButtonStyle, spawn_button};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub struct LinearUIPlugin;
|
||||||
|
|
||||||
|
impl Plugin for LinearUIPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_message::<ButtonClickMessage<LinearRestartMarker>>()
|
||||||
|
.add_systems(OnEnter(LinearPlayState), setup_ui)
|
||||||
|
.configure_sets(
|
||||||
|
Update,
|
||||||
|
LinearUpdateSet::Track.run_if(in_state(LinearPlayState)),
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
(
|
||||||
|
restart_game_button_system,
|
||||||
|
handle_click_system::<LinearRestartMarker>,
|
||||||
|
)
|
||||||
|
.in_set(LinearUpdateSet::Track),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// хоть ui отделен, но по глобальному маркеру стейта, все сущности будут очищены в основном плагине
|
||||||
|
fn setup_ui(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
let root = commands
|
||||||
|
.spawn((
|
||||||
|
Node {
|
||||||
|
display: Display::Flex,
|
||||||
|
width: Val::Percent(100.0),
|
||||||
|
height: Val::Percent(100.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
BackgroundColor(Color::NONE),
|
||||||
|
Name::new("Game state background node"),
|
||||||
|
LinearStateMarker,
|
||||||
|
))
|
||||||
|
.id();
|
||||||
|
|
||||||
|
let restart_button_style = ButtonStyle {
|
||||||
|
font: asset_server.load("fonts/QR Ames Beta.otf"),
|
||||||
|
font_size: 24.0,
|
||||||
|
text_color: Color::WHITE,
|
||||||
|
normal_bg: Color::linear_rgba(0.15, 0.15, 0.15, 1.0),
|
||||||
|
hovered_bg: Color::linear_rgb(0.25, 0.25, 0.25),
|
||||||
|
pressed_bg: Color::linear_rgb(0.3, 0.3, 0.3),
|
||||||
|
width: Val::Px(200.0),
|
||||||
|
height: Val::Px(50.0),
|
||||||
|
margin: UiRect::all(Val::Px(10.0)),
|
||||||
|
};
|
||||||
|
|
||||||
|
spawn_button(
|
||||||
|
&mut commands,
|
||||||
|
root,
|
||||||
|
"Restart",
|
||||||
|
&restart_button_style,
|
||||||
|
LinearRestartMarker,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restart_game_button_system(
|
||||||
|
interaction_query: Query<&Interaction, (Changed<Interaction>, With<LinearRestartMarker>)>,
|
||||||
|
mut next_state: ResMut<NextState<AppState>>,
|
||||||
|
) {
|
||||||
|
for interaction in &interaction_query {
|
||||||
|
if matches!(interaction, Interaction::Pressed) {
|
||||||
|
next_state.set(AppState::LinearGameRestart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,7 +31,7 @@ pub fn linear_button_system(
|
||||||
for interaction in &interaction_query {
|
for interaction in &interaction_query {
|
||||||
if matches!(interaction, Interaction::Pressed) {
|
if matches!(interaction, Interaction::Pressed) {
|
||||||
println!("Линейное нажата");
|
println!("Линейное нажата");
|
||||||
next_state.set(AppState::LinearState);
|
next_state.set(AppState::LinearPlayState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue