diff --git a/src/main.rs b/src/main.rs index aa15696..7851732 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +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; +use crate::states::linear::plugin::LinearPlayPlugin; const FACTOR: u32 = 80; const WIDTH: u32 = 16; @@ -25,7 +25,7 @@ fn main() { .add_message::() .add_plugins(DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { - title: "alpha".into(), + title: "alpha v0.2".into(), resolution: (WIDTH * FACTOR, HEIGHT * FACTOR).into(), resizable: true, ..default() @@ -37,7 +37,7 @@ fn main() { MainMenuState, SettingsMenuState, MainGameState, - LinearPlugin, + LinearPlayPlugin, )) .add_systems(Startup, setup) .add_systems(Update, exit_system) diff --git a/src/states/app_states.rs b/src/states/app_states.rs index 77f2b45..7274112 100644 --- a/src/states/app_states.rs +++ b/src/states/app_states.rs @@ -7,6 +7,6 @@ pub enum AppState { SettingsMenu, GameState, GameRestart, - LevelState, - LinearState, + LinearPlayState, + LinearGameRestart, } \ No newline at end of file diff --git a/src/states/linear/components.rs b/src/states/linear/components.rs index 8fb44ec..6c0a133 100644 --- a/src/states/linear/components.rs +++ b/src/states/linear/components.rs @@ -1,4 +1,7 @@ use bevy::prelude::*; #[derive(Component)] -pub struct LinearStateMarker; \ No newline at end of file +pub struct LinearStateMarker; + +#[derive(Component, Copy, Clone)] +pub struct LinearRestartMarker; \ No newline at end of file diff --git a/src/states/linear/components_ball.rs b/src/states/linear/components_ball.rs new file mode 100644 index 0000000..634a81e --- /dev/null +++ b/src/states/linear/components_ball.rs @@ -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() + } +} \ No newline at end of file diff --git a/src/states/linear/components_track.rs b/src/states/linear/components_track.rs index a9d286d..58c0a75 100644 --- a/src/states/linear/components_track.rs +++ b/src/states/linear/components_track.rs @@ -27,7 +27,10 @@ pub enum PathSegment { #[derive(Resource)] pub struct PrecalculatedTrack{ - pub segments: Vec + pub segments: Vec, + pub min_spawn_gap: f32, //нормализован + pub total_length: f32, + pub speed_norm: f32, } #[derive(Debug)] @@ -41,6 +44,7 @@ pub struct PTSegment { pub radius: f32, pub start_angle: f32, pub sweep_sign: f32, + pub length: f32, } #[derive(Debug, Clone, Copy)] diff --git a/src/states/linear/constants.rs b/src/states/linear/constants.rs new file mode 100644 index 0000000..248dac0 --- /dev/null +++ b/src/states/linear/constants.rs @@ -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); diff --git a/src/states/linear/mod.rs b/src/states/linear/mod.rs index c9c5a5f..c2c08ac 100644 --- a/src/states/linear/mod.rs +++ b/src/states/linear/mod.rs @@ -11,3 +11,15 @@ pub use components_track::*; mod 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::*; + + diff --git a/src/states/linear/plugin.rs b/src/states/linear/plugin.rs index 09d1a1e..b95b59e 100644 --- a/src/states/linear/plugin.rs +++ b/src/states/linear/plugin.rs @@ -1,23 +1,43 @@ -use bevy::prelude::*; -use crate::states::AppState::LinearState; -use crate::states::level::GameOver; +use crate::states::AppState; +use crate::states::AppState::{LinearGameRestart, LinearPlayState}; 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) { app - .add_systems(OnEnter (LinearState), setup) - .add_systems(Update, (draw_track_gizmos).run_if(in_state(LinearState))) - .add_systems(OnExit (LinearState), cleanup); + .add_systems(OnEnter(LinearPlayState), setup) + .add_systems(OnEnter(LinearGameRestart), linear_restart) + .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); } } 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); } @@ -30,4 +50,9 @@ fn cleanup(mut commands: Commands, query: Query> // зачищаем ресурсы по типу commands.remove_resource::(); -} \ No newline at end of file + commands.remove_resource::(); +} + +fn linear_restart(mut next_state: ResMut>) { + next_state.set(LinearPlayState); +} diff --git a/src/states/linear/system_ball.rs b/src/states/linear/system_ball.rs new file mode 100644 index 0000000..b74b174 --- /dev/null +++ b/src/states/linear/system_ball.rs @@ -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, + mut commands: Commands, + asset_server: Res, + 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 = 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, + mut balls: Query<(&mut Transform, &mut RoundBall)>, + time: Res