diff --git a/Cargo.lock b/Cargo.lock index c232af5870425231352896f5ca57058a2145c979..149aa7c3b32da8155e3327b432076bd7a5813c76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -524,6 +524,18 @@ dependencies = [ "radsort", ] +[[package]] +name = "bevy_prototype_lyon" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799f9bc78cdef67d8dbecd06d2147d30732cdc74eb5e15edacf76ff2069b3d9f" +dependencies = [ + "bevy", + "lyon_algorithms", + "lyon_tessellation", + "svgtypes", +] + [[package]] name = "bevy_ptr" version = "0.10.1" @@ -1148,6 +1160,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float_next_after" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc612c5837986b7104a87a0df74a5460931f1c5274be12f8d0f40aa2f30d632" +dependencies = [ + "num-traits", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -1511,6 +1532,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + [[package]] name = "lock_api" version = "0.4.10" @@ -1527,6 +1554,48 @@ version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +[[package]] +name = "lyon_algorithms" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00a0349cd8f0270781bb93a824b63df6178e3b4a27794e7be3ce3763f5a44d6e" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74df1ff0a0147282eb10699537a03baa7d31972b58984a1d44ce0624043fe8ad" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8358c012e5651e4619cfd0b5b75c0f77866181a01b0909aab4bae14adf660" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d2124218d5428149f9e09520b9acc024334a607e671f032d06567b61008977c" +dependencies = [ + "float_next_after", + "lyon_path", + "thiserror", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1762,6 +1831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2158,6 +2228,7 @@ name = "shoot-the-revival" version = "0.1.0" dependencies = [ "bevy", + "bevy_prototype_lyon", "log", ] @@ -2167,6 +2238,12 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "slab" version = "0.4.8" @@ -2235,6 +2312,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" +[[package]] +name = "svgtypes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22975e8a2bac6a76bb54f898a6b18764633b00e780330f0b689f65afb3975564" +dependencies = [ + "siphasher", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 246fa02efc9185261e37db2323cf911bda7ed39d..82a09c98573acfc64a0791ef90a71e9c04ef5234 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] log = "0.4.19" +bevy_prototype_lyon = "0.8.0" [dependencies.bevy] version = "0.10.1" diff --git a/src/entities/mod.rs b/src/entities/mod.rs index f604191ddf9abad6a95d780d9c365f8cdd772374..0b8174e00a131672fd1495c24c961e1cab0b3a61 100644 --- a/src/entities/mod.rs +++ b/src/entities/mod.rs @@ -18,6 +18,7 @@ mod _plugin { .add_systems(( super::player::process_player_input, super::motion::apply_velocity, + super::spawning::apply_despawn_boundaries, )); } } @@ -25,6 +26,6 @@ mod _plugin { pub use _plugin::EntityPlugin; pub use collision::{check_box_collisions, BoxSize, CollisionGroup}; -pub use motion::Velocity; +pub use motion::{Velocity, DIR_LEFT, DIR_RIGHT}; pub use player::Player; -pub use spawning::EntitySpawner; +pub use spawning::{DespawnOffScreen, EntitySpawner}; diff --git a/src/entities/motion.rs b/src/entities/motion.rs index e4a96d4b899e988775b83cb5abedb87d855dab49..9f2523bfb18ef2213c56e9b5fd1fe8c4bf4ff218 100644 --- a/src/entities/motion.rs +++ b/src/entities/motion.rs @@ -3,6 +3,9 @@ use bevy::math::Vec2; use bevy::prelude::{Component, Query, Res, Time, Transform}; use std::ops::Mul; +pub const DIR_LEFT: Vec2 = Vec2::new(-1.0, 0.0); +pub const DIR_RIGHT: Vec2 = Vec2::new(1.0, 0.0); + #[derive(Clone, Copy, Debug, Component)] pub struct Velocity(Vec2); deref_as!(mut Velocity => Vec2); diff --git a/src/entities/player.rs b/src/entities/player.rs index ab1bd70edb97fcc8fc0823a79c56bbc736722ed4..e86b3bdf7ab337a57dcb73229a4b560af2187d66 100644 --- a/src/entities/player.rs +++ b/src/entities/player.rs @@ -1,8 +1,7 @@ -use crate::entities::spawning::EntitySpawner; -use crate::entities::Velocity; +use crate::entities::{CollisionGroup, DespawnOffScreen, EntitySpawner, Velocity, DIR_RIGHT}; use crate::system::window_bounds; -use bevy::math::Vec2; -use bevy::prelude::{Component, DetectChangesMut, Input, KeyCode, Query, Res, With}; +use bevy::math::{Vec2, Vec3Swizzles}; +use bevy::prelude::{Component, DetectChangesMut, Input, KeyCode, Query, Res, Transform, With}; #[derive(Component)] pub struct Player; @@ -17,7 +16,9 @@ macro_rules! any_pressed { } pub fn process_player_input( - mut player_query: Query<&mut Velocity, With<Player>>, + mut spawner: EntitySpawner, + mut player_velocity_query: Query<&mut Velocity, With<Player>>, + player_position_query: Query<&Transform, With<Player>>, input: Res<Input<KeyCode>>, ) { let mut delta = Vec2::default(); @@ -35,7 +36,17 @@ pub fn process_player_input( delta.y += 1.0; } - for mut velocity in &mut player_query { + if input.just_pressed(KeyCode::Space) { + for position in &player_position_query { + spawner.spawn_projectile( + position.translation.xy() + Vec2::new(15.0, 0.0), + DIR_RIGHT, + (CollisionGroup::FriendlyProjectile, DespawnOffScreen::all()), + ); + } + } + + for mut velocity in &mut player_velocity_query { if delta != Vec2::ZERO { *velocity = Velocity::default() * delta; } else if **(velocity.bypass_change_detection()) != Vec2::ZERO { diff --git a/src/entities/spawning.rs b/src/entities/spawning.rs index 7dc9980ada4b6f5bb974a4169636579441832668..19165ff3b9a963f1efe2896a6ed9fff496adc884 100644 --- a/src/entities/spawning.rs +++ b/src/entities/spawning.rs @@ -1,14 +1,72 @@ use crate::entities::motion::Velocity; use crate::entities::{BoxSize, CollisionGroup, Player}; -use crate::system::{Background, BACKGROUND_HEIGHT, BACKGROUND_WIDTH}; +use crate::system::{Background, DawnBringerPalette, BACKGROUND_HEIGHT, BACKGROUND_WIDTH}; +use crate::utilities::translate_rect; use bevy::ecs::system::SystemParam; -use bevy::prelude::{AssetServer, Commands, Entity, Res, Sprite, SpriteBundle, Transform, Vec2}; +use bevy::math::Vec3Swizzles; +use bevy::prelude::{ + AssetServer, Bundle, Changed, Commands, Component, DespawnRecursiveExt, Entity, + OrthographicProjection, Query, Rect, Res, Sprite, SpriteBundle, Transform, Vec2, +}; +use bevy_prototype_lyon::draw::Stroke; +use bevy_prototype_lyon::prelude::{Fill, GeometryBuilder, ShapeBundle}; +use bevy_prototype_lyon::shapes; static Z_PLAYER: f32 = 300.0; static Z_ITEMS: f32 = 200.0; static Z_ENEMY: f32 = 250.0; static Z_BACKGROUND: f32 = 50.0; +#[derive(Copy, Clone, Debug, Component)] +pub struct DespawnOffScreen { + left: bool, + right: bool, + top: bool, + bottom: bool, +} + +impl Default for DespawnOffScreen { + fn default() -> Self { + Self { + left: true, + right: true, + top: true, + bottom: true, + } + } +} + +impl DespawnOffScreen { + pub fn all() -> Self { + Self::default() + } + + pub fn right() -> Self { + Self { + left: false, + right: true, + top: false, + bottom: false, + } + } + + pub fn left() -> Self { + Self { + left: true, + right: false, + top: false, + bottom: false, + } + } + + pub fn entity_should_despawn(&self, entity_pos: Vec2, camera_bounds: Rect) -> bool { + self.left && entity_pos.x < camera_bounds.min.x + || self.top && entity_pos.y > camera_bounds.max.y + || self.right && entity_pos.x > camera_bounds.max.x + || self.bottom && entity_pos.y < camera_bounds.min.y + } +} + #[derive(SystemParam)] pub struct EntitySpawner<'w, 's> { commands: Commands<'w, 's>, @@ -62,4 +120,56 @@ impl<'w, 's> EntitySpawner<'w, 's> { )) .id() } + + pub fn spawn_projectile( + &mut self, + position: Vec2, + velocity_direction: Vec2, + rest: impl Bundle, + ) -> Entity { + let projectile_shape = shapes::RoundedPolygon { + radius: 2.0, + closed: true, + points: vec![ + Vec2::new(-7.0, -2.0), + Vec2::new(-7.0, 2.0), + Vec2::new(7.0, 2.0), + Vec2::new(7.0, -2.0), + ], + }; + + self.commands + .spawn(( + ShapeBundle { + path: GeometryBuilder::build_as(&projectile_shape), + transform: Transform::from_translation(position.extend(Z_ITEMS)), + ..Default::default() + }, + BoxSize::from(Vec2::new(10.0, 10.0)), + Fill::color(DawnBringerPalette::MID_BLUE), + Stroke::new(DawnBringerPalette::MIDNIGHT_BLUE, 2.0), + Velocity::from(Vec2::new(100.0, 0.0) * velocity_direction), + rest, + )) + .id() + } +} + +pub fn apply_despawn_boundaries( + mut commands: Commands, + camera_query: Query<(&OrthographicProjection, &Transform)>, + bounds_query: Query<(Entity, &DespawnOffScreen, &Transform), Changed<Transform>>, +) { + let (camera_ortho, camera_position) = match camera_query.get_single() { + Ok(value) => value, + Err(_) => return, + }; + + let viewport = translate_rect(camera_ortho.area, camera_position.translation.xy()); + + for (entity, boundary, transform) in &bounds_query { + if boundary.entity_should_despawn(transform.translation.xy(), viewport) { + commands.entity(entity).despawn_recursive(); + } + } } diff --git a/src/main.rs b/src/main.rs index 2f567b59d415c5425bcedbc7c38b6428a59fce8a..4e2a976576e3cf4f96df982446c18d6f406eca1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,5 +17,6 @@ fn main() { })) .add_plugin(shoot_the_revival::system::SystemPlugin) .add_plugin(shoot_the_revival::entities::EntityPlugin) + .add_plugin(bevy_prototype_lyon::plugin::ShapePlugin) .run(); } diff --git a/src/utilities.rs b/src/utilities.rs index 9d1010488208d59005b16f472744cb85518087c4..f6826c967020795028c8735a3e72cdb5ad23ec69 100644 --- a/src/utilities.rs +++ b/src/utilities.rs @@ -1,3 +1,5 @@ +use bevy::math::Vec2; +use bevy::prelude::Rect; #[macro_export] macro_rules! deref_as { ($outer:ty => $inner:ty) => { @@ -18,3 +20,7 @@ macro_rules! deref_as { } } } + +pub fn translate_rect(rect: Rect, translation: Vec2) -> Rect { + Rect::from_corners(rect.min + translation, rect.max + translation) +}