feat: add scrolling starfield background
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).
This commit is contained in:
parent
c9188f58f6
commit
9d80626cc6
7 changed files with 72 additions and 6 deletions
2
TODO.md
2
TODO.md
|
|
@ -55,7 +55,7 @@
|
||||||
* [ ] **Visuals**
|
* [ ] **Visuals**
|
||||||
* [ ] Replace placeholder geometric shapes with actual sprites.
|
* [ ] Replace placeholder geometric shapes with actual sprites.
|
||||||
* [ ] Add explosion animations/effects.
|
* [ ] Add explosion animations/effects.
|
||||||
* [ ] Implement a scrolling starfield background.
|
* [x] Implement a scrolling starfield background.
|
||||||
* [ ] **Audio**
|
* [ ] **Audio**
|
||||||
* [ ] Integrate `bevy_audio`.
|
* [ ] Integrate `bevy_audio`.
|
||||||
* [ ] Add sound effects (shooting, explosions, player death, tractor beam, etc.).
|
* [ ] Add sound effects (shooting, explosions, player death, tractor beam, etc.).
|
||||||
|
|
|
||||||
|
|
@ -90,3 +90,8 @@ pub struct StartButton;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct RestartMessage;
|
pub struct RestartMessage;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Star {
|
||||||
|
pub speed: f32,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,3 +39,11 @@ pub const TRACTOR_BEAM_WIDTH: f32 = 20.0;
|
||||||
pub const TRACTOR_BEAM_DURATION: f32 = 3.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 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
|
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
|
||||||
|
|
|
||||||
|
|
@ -197,11 +197,10 @@ pub fn start_menu_button_system(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_restart_input(
|
pub fn handle_restart_input(
|
||||||
mut keyboard_input: ResMut<Input<KeyCode>>,
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
mut restart_resource: ResMut<RestartPressed>,
|
mut restart_resource: ResMut<RestartPressed>,
|
||||||
mut app_state: ResMut<NextState<AppState>>,
|
|
||||||
) {
|
) {
|
||||||
if keyboard_input.just_pressed(KeyCode::R) {
|
if keyboard_input.just_pressed(KeyCode::KeyR) {
|
||||||
restart_resource.pressed = true;
|
restart_resource.pressed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ pub mod enemy;
|
||||||
pub mod bullet;
|
pub mod bullet;
|
||||||
pub mod stage;
|
pub mod stage;
|
||||||
pub mod systems;
|
pub mod systems;
|
||||||
|
pub mod starfield;
|
||||||
|
|
||||||
use constants::{PLAYER_RESPAWN_DELAY, STARTING_LIVES, WINDOW_HEIGHT, WINDOW_WIDTH};
|
use constants::{PLAYER_RESPAWN_DELAY, STARTING_LIVES, WINDOW_HEIGHT, WINDOW_WIDTH};
|
||||||
use resources::{ // Added StageConfigurations
|
use resources::{ // Added StageConfigurations
|
||||||
|
|
@ -36,6 +37,7 @@ use bullet::{
|
||||||
};
|
};
|
||||||
use stage::check_stage_clear;
|
use stage::check_stage_clear;
|
||||||
use systems::{player_exists, player_vulnerable, setup, should_respawn_player, update_window_title};
|
use systems::{player_exists, player_vulnerable, setup, should_respawn_player, update_window_title};
|
||||||
|
use starfield::scroll_starfield;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
|
|
@ -115,6 +117,8 @@ fn main() {
|
||||||
.chain() // Ensure these run in order if needed, check_formation first
|
.chain() // Ensure these run in order if needed, check_formation first
|
||||||
.run_if(in_state(AppState::Playing)),
|
.run_if(in_state(AppState::Playing)),
|
||||||
)
|
)
|
||||||
|
// Starfield runs in all states
|
||||||
|
.add_systems(Update, scroll_starfield)
|
||||||
// UI and state management systems
|
// UI and state management systems
|
||||||
.add_systems(OnEnter(AppState::StartMenu), setup_start_menu_ui)
|
.add_systems(OnEnter(AppState::StartMenu), setup_start_menu_ui)
|
||||||
.add_systems(OnExit(AppState::StartMenu), cleanup_start_menu_ui)
|
.add_systems(OnExit(AppState::StartMenu), cleanup_start_menu_ui)
|
||||||
|
|
|
||||||
48
src/starfield.rs
Normal file
48
src/starfield.rs
Normal file
|
|
@ -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<Time>) {
|
||||||
|
let dt = time.delta_seconds();
|
||||||
|
let half_height = WINDOW_HEIGHT / 2.0;
|
||||||
|
let half_width = WINDOW_WIDTH / 2.0;
|
||||||
|
|
||||||
|
for (mut transform, star) in star_query.iter_mut() {
|
||||||
|
let parallax = -transform.translation.y * 0.01;
|
||||||
|
transform.translation.y -= (star.speed + parallax) * dt;
|
||||||
|
|
||||||
|
if transform.translation.y < -half_height {
|
||||||
|
transform.translation.y = half_height;
|
||||||
|
transform.translation.x = fastrand::f32() * WINDOW_WIDTH - half_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,12 +2,14 @@ use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::components::{Invincible, Player};
|
use crate::components::{Invincible, Player};
|
||||||
use crate::resources::{CurrentStage, PlayerLives, Score};
|
use crate::resources::{CurrentStage, PlayerLives, Score};
|
||||||
use crate::player::spawn_player_ship; // Import the helper function
|
use crate::player::spawn_player_ship;
|
||||||
|
use crate::starfield::spawn_starfield;
|
||||||
|
|
||||||
// --- Setup ---
|
// --- Setup ---
|
||||||
pub fn setup(mut commands: Commands) {
|
pub fn setup(mut commands: Commands) {
|
||||||
commands.spawn(Camera2dBundle::default());
|
commands.spawn(Camera2dBundle::default());
|
||||||
spawn_player_ship(&mut commands); // Use helper function from player module
|
spawn_starfield(&mut commands);
|
||||||
|
spawn_player_ship(&mut commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Run Conditions ---
|
// --- Run Conditions ---
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue