From 9d80626cc6a1cf30309e060149b7562302e5c81b Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 6 May 2026 15:05:32 +0200 Subject: [PATCH] feat: add scrolling starfield background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 150 stars with randomized positions, sizes, speeds, and brightness. Stars scroll downward with parallax depth effect and wrap around at screen edges. Also fixes Bevy 0.13 API issues in game_state.rs (KeyCode::R → KeyR, Input → ButtonInput). --- TODO.md | 2 +- src/components.rs | 5 +++++ src/constants.rs | 8 ++++++++ src/game_state.rs | 5 ++--- src/main.rs | 4 ++++ src/starfield.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++ src/systems.rs | 6 ++++-- 7 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 src/starfield.rs diff --git a/TODO.md b/TODO.md index f029d92..f21d7d7 100644 --- a/TODO.md +++ b/TODO.md @@ -55,7 +55,7 @@ * [ ] **Visuals** * [ ] Replace placeholder geometric shapes with actual sprites. * [ ] Add explosion animations/effects. - * [ ] Implement a scrolling starfield background. + * [x] Implement a scrolling starfield background. * [ ] **Audio** * [ ] Integrate `bevy_audio`. * [ ] Add sound effects (shooting, explosions, player death, tractor beam, etc.). diff --git a/src/components.rs b/src/components.rs index baf1ea2..fe5ff07 100644 --- a/src/components.rs +++ b/src/components.rs @@ -90,3 +90,8 @@ pub struct StartButton; #[derive(Component)] pub struct RestartMessage; + +#[derive(Component)] +pub struct Star { + pub speed: f32, +} diff --git a/src/constants.rs b/src/constants.rs index 0b2c1c0..c8e8a15 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -39,3 +39,11 @@ pub const TRACTOR_BEAM_WIDTH: f32 = 20.0; pub const TRACTOR_BEAM_DURATION: f32 = 3.0; pub const TRACTOR_BEAM_COLOR: Color = Color::rgba(0.5, 0.0, 0.8, 0.6); pub const CAPTURE_DURATION: f32 = 10.0; // How long the player stays captured + +// Starfield constants +pub const STAR_COUNT: usize = 150; +pub const STAR_MIN_SIZE: f32 = 1.0; +pub const STAR_MAX_SIZE: f32 = 3.0; +pub const STAR_MIN_SPEED: f32 = 20.0; +pub const STAR_MAX_SPEED: f32 = 100.0; +pub const STAR_Z_DEPTH: f32 = -10.0; // Behind all game entities diff --git a/src/game_state.rs b/src/game_state.rs index 8cf8b1c..fd220ed 100644 --- a/src/game_state.rs +++ b/src/game_state.rs @@ -197,11 +197,10 @@ pub fn start_menu_button_system( } pub fn handle_restart_input( - mut keyboard_input: ResMut>, + keyboard_input: Res>, mut restart_resource: ResMut, - mut app_state: ResMut>, ) { - if keyboard_input.just_pressed(KeyCode::R) { + if keyboard_input.just_pressed(KeyCode::KeyR) { restart_resource.pressed = true; } } diff --git a/src/main.rs b/src/main.rs index ea63050..af9585b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ pub mod enemy; pub mod bullet; pub mod stage; pub mod systems; +pub mod starfield; use constants::{PLAYER_RESPAWN_DELAY, STARTING_LIVES, WINDOW_HEIGHT, WINDOW_WIDTH}; use resources::{ // Added StageConfigurations @@ -36,6 +37,7 @@ use bullet::{ }; use stage::check_stage_clear; use systems::{player_exists, player_vulnerable, setup, should_respawn_player, update_window_title}; +use starfield::scroll_starfield; fn main() { App::new() @@ -115,6 +117,8 @@ fn main() { .chain() // Ensure these run in order if needed, check_formation first .run_if(in_state(AppState::Playing)), ) + // Starfield runs in all states + .add_systems(Update, scroll_starfield) // UI and state management systems .add_systems(OnEnter(AppState::StartMenu), setup_start_menu_ui) .add_systems(OnExit(AppState::StartMenu), cleanup_start_menu_ui) diff --git a/src/starfield.rs b/src/starfield.rs new file mode 100644 index 0000000..711b276 --- /dev/null +++ b/src/starfield.rs @@ -0,0 +1,48 @@ +use bevy::prelude::*; + +use crate::components::Star; +use crate::constants::{ + STAR_COUNT, STAR_MAX_SIZE, STAR_MAX_SPEED, STAR_MIN_SIZE, STAR_MIN_SPEED, + STAR_Z_DEPTH, WINDOW_HEIGHT, WINDOW_WIDTH, +}; + +pub fn spawn_starfield(commands: &mut Commands) { + for _ in 0..STAR_COUNT { + let size = fastrand::f32() * (STAR_MAX_SIZE - STAR_MIN_SIZE) + STAR_MIN_SIZE; + let speed = fastrand::f32() * (STAR_MAX_SPEED - STAR_MIN_SPEED) + STAR_MIN_SPEED; + let brightness = fastrand::f32() * 0.5 + 0.5; // 0.5 to 1.0 + + commands.spawn(( + SpriteBundle { + sprite: Sprite { + color: Color::rgb(brightness, brightness, brightness), + custom_size: Some(Vec2::new(size, size)), + ..default() + }, + transform: Transform::from_translation(Vec3::new( + fastrand::f32() * WINDOW_WIDTH - WINDOW_WIDTH / 2.0, + fastrand::f32() * WINDOW_HEIGHT - WINDOW_HEIGHT / 2.0, + STAR_Z_DEPTH, + )), + ..default() + }, + Star { speed }, + )); + } +} + +pub fn scroll_starfield(mut star_query: Query<(&mut Transform, &Star)>, time: Res