|
| 1 | +use bevy::{ |
| 2 | + prelude::*, |
| 3 | + render::render_resource::{AsBindGroup, ShaderRef, ShaderType}, |
| 4 | + sprite::{Material2d, Material2dPlugin}, |
| 5 | + window::WindowResized, |
| 6 | +}; |
| 7 | +use rand::Rng; |
| 8 | + |
| 9 | +use crate::GameState; |
| 10 | + |
| 11 | +pub fn starfield_plugin(app: &mut bevy::prelude::App) { |
| 12 | + app.add_plugins(Material2dPlugin::<StarfieldMaterial>::default()) |
| 13 | + .add_systems(OnEnter(GameState::Game), setup) |
| 14 | + .add_systems( |
| 15 | + PostUpdate, |
| 16 | + update_starfield.run_if(in_state(GameState::Game)), |
| 17 | + ); |
| 18 | +} |
| 19 | + |
| 20 | +#[derive(Asset, TypePath, AsBindGroup, ShaderType, Debug, Clone)] |
| 21 | +#[uniform(0, StarfieldMaterial)] |
| 22 | +pub struct StarfieldMaterial { |
| 23 | + position: Vec2, |
| 24 | + seeds: Vec2, |
| 25 | +} |
| 26 | + |
| 27 | +impl<'a> From<&'a StarfieldMaterial> for StarfieldMaterial { |
| 28 | + fn from(material: &'a StarfieldMaterial) -> Self { |
| 29 | + material.clone() |
| 30 | + } |
| 31 | +} |
| 32 | + |
| 33 | +impl Material2d for StarfieldMaterial { |
| 34 | + fn fragment_shader() -> bevy::render::render_resource::ShaderRef { |
| 35 | + ShaderRef::Path("starfield.wgsl".into()) |
| 36 | + } |
| 37 | +} |
| 38 | + |
| 39 | +fn setup( |
| 40 | + mut commands: Commands, |
| 41 | + mut meshes: ResMut<Assets<Mesh>>, |
| 42 | + mut materials: ResMut<Assets<StarfieldMaterial>>, |
| 43 | + windows: Query<&Window>, |
| 44 | +) { |
| 45 | + let window = windows.single().unwrap(); |
| 46 | + let size = window.width().max(window.height()); |
| 47 | + |
| 48 | + commands.spawn(( |
| 49 | + Mesh2d(meshes.add(Rectangle::default())), |
| 50 | + MeshMaterial2d(materials.add(StarfieldMaterial { |
| 51 | + position: Vec2::ZERO, |
| 52 | + seeds: Vec2::new( |
| 53 | + rand::thread_rng().gen_range(0.0..1000.0), |
| 54 | + rand::thread_rng().gen_range(0.0..1000.0), |
| 55 | + ), |
| 56 | + })), |
| 57 | + Transform::from_scale(Vec3::new(size, size, 1.0)), |
| 58 | + StateScoped(GameState::Game), |
| 59 | + )); |
| 60 | +} |
| 61 | + |
| 62 | +fn update_starfield( |
| 63 | + mut starfield: Query<(&mut Transform, &MeshMaterial2d<StarfieldMaterial>), Without<Camera2d>>, |
| 64 | + camera: Query<Ref<Transform>, With<Camera2d>>, |
| 65 | + mut materials: ResMut<Assets<StarfieldMaterial>>, |
| 66 | + mut resized: EventReader<WindowResized>, |
| 67 | +) { |
| 68 | + let camera_transform = camera.single().unwrap(); |
| 69 | + if camera_transform.is_changed() { |
| 70 | + let (mut starfield_transform, material) = starfield.single_mut().unwrap(); |
| 71 | + |
| 72 | + starfield_transform.translation = camera_transform.translation.with_z(-2.0); |
| 73 | + |
| 74 | + let material = materials.get_mut(&material.0).unwrap(); |
| 75 | + material.position = camera_transform.translation.xy(); |
| 76 | + } |
| 77 | + |
| 78 | + if let Some(resized) = resized.read().last() { |
| 79 | + let (mut starfield_transform, _) = starfield.single_mut().unwrap(); |
| 80 | + |
| 81 | + starfield_transform.scale.x = resized.width.max(resized.height); |
| 82 | + starfield_transform.scale.y = resized.width.max(resized.height); |
| 83 | + } |
| 84 | +} |
0 commit comments