diff --git a/src/asteroids.rs b/src/asteroids.rs index 0acfd94..06cf0fb 100644 --- a/src/asteroids.rs +++ b/src/asteroids.rs @@ -1,19 +1,12 @@ //! This is the module containing all the rock-related things. //! Not... not the whole game. -use bevy_rapier2d::prelude::*; use rand::{Rng, SeedableRng}; use std::time::Duration; use bevy::prelude::*; -use crate::{ - GameAssets, Lifetime, WorldSize, - config::ASTEROID_LIFETIME, - events::{AsteroidDestroy, SpawnAsteroid}, - objects::{Asteroid, AsteroidSize}, - physics::Velocity, -}; +use crate::{WorldSize, events::SpawnAsteroid, objects::AsteroidSize}; #[derive(Resource)] pub struct AsteroidSpawner { @@ -84,77 +77,3 @@ pub fn tick_asteroid_manager( events.write(SpawnAsteroid { pos, vel, size }); } } - -/// Utility function to spawn a single asteroid of a given type -/// TODO: convert to an event listener monitoring for "spawn asteroid" events -/// from the `fn tick_asteroid_manager(...)` system. -pub fn spawn_asteroid( - mut events: EventReader, - mut commands: Commands, - game_assets: Res, -) { - for spawn in events.read() { - let (mesh, material) = match spawn.size { - AsteroidSize::Small => game_assets.asteroid_small(), - AsteroidSize::Medium => game_assets.asteroid_medium(), - AsteroidSize::Large => game_assets.asteroid_large(), - }; - - let collider_radius = match spawn.size { - AsteroidSize::Small => 10.0, - AsteroidSize::Medium => 20.0, - AsteroidSize::Large => 40.0, - }; - - commands.spawn(( - Asteroid(spawn.size), - Collider::ball(collider_radius), - Sensor, - Transform::from_translation(spawn.pos.extend(0.0)), - Velocity(spawn.vel), - Mesh2d(mesh), - MeshMaterial2d(material), - Lifetime(Timer::from_seconds(ASTEROID_LIFETIME, TimerMode::Once)), - )); - } -} - -/// Event listener for asteroid destruction events. Shrinks and multiplies -/// asteroids until they vanish. -/// -/// - Large -> 2x Medium -/// - Medium -> 2x Small -/// - Small -> (despawned) -/// -/// The velocity of the child asteroids is scattered somewhat, as if they were -/// explosively pushed apart. -pub fn split_asteroids( - mut destroy_events: EventReader, - mut respawn_events: EventWriter, - mut commands: Commands, - query: Query<(&Transform, &Asteroid, &Velocity)>, -) { - for event in destroy_events.read() { - if let Ok((transform, rock, velocity)) = query.get(event.0) { - let next_size = rock.0.next(); - if let Some(size) = next_size { - let pos = transform.translation.xy(); - let left_offset = Vec2::from_angle(0.4); - let right_offset = Vec2::from_angle(-0.4); - respawn_events.write(SpawnAsteroid { - pos, - vel: left_offset.rotate(velocity.0), - size, - }); - respawn_events.write(SpawnAsteroid { - pos, - vel: right_offset.rotate(velocity.0), - size, - }); - } - // Always despawn the asteroid. New ones (may) be spawned in it's - // place, but this one is gone. - commands.entity(event.0).despawn(); - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 841a769..7616d0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,8 +55,8 @@ impl Plugin for AsteroidPlugin { input_ship_shoot, physics::wrap_entities, asteroids::tick_asteroid_manager, - asteroids::spawn_asteroid.after(asteroids::tick_asteroid_manager), - asteroids::split_asteroids, + objects::spawn_asteroid.after(asteroids::tick_asteroid_manager), + objects::split_asteroids, ship::bullet_impact_listener, ship::ship_impact_listener, collision_listener, diff --git a/src/objects.rs b/src/objects.rs index 2af4610..385df61 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -3,8 +3,25 @@ //! Asteroids, the player's ship, and such. use bevy::{ - ecs::component::Component, + ecs::{ + component::Component, + event::{EventReader, EventWriter}, + system::{Commands, Query, Res}, + }, + math::{Vec2, Vec3Swizzles}, prelude::{Deref, DerefMut}, + render::mesh::Mesh2d, + sprite::MeshMaterial2d, + time::{Timer, TimerMode}, + transform::components::Transform, +}; +use bevy_rapier2d::prelude::{Collider, Sensor}; + +use crate::{ + GameAssets, Lifetime, + config::ASTEROID_LIFETIME, + events::{AsteroidDestroy, SpawnAsteroid}, + physics::Velocity, }; #[derive(Component, Deref, DerefMut)] @@ -32,3 +49,75 @@ pub struct Ship; #[derive(Component)] pub struct Bullet; + +/// Responds to [`SpawnAsteroid`] events, spawning as specified +pub fn spawn_asteroid( + mut events: EventReader, + mut commands: Commands, + game_assets: Res, +) { + for spawn in events.read() { + let (mesh, material) = match spawn.size { + AsteroidSize::Small => game_assets.asteroid_small(), + AsteroidSize::Medium => game_assets.asteroid_medium(), + AsteroidSize::Large => game_assets.asteroid_large(), + }; + + let collider_radius = match spawn.size { + AsteroidSize::Small => 10.0, + AsteroidSize::Medium => 20.0, + AsteroidSize::Large => 40.0, + }; + + commands.spawn(( + Asteroid(spawn.size), + Collider::ball(collider_radius), + Sensor, + Transform::from_translation(spawn.pos.extend(0.0)), + Velocity(spawn.vel), + Mesh2d(mesh), + MeshMaterial2d(material), + Lifetime(Timer::from_seconds(ASTEROID_LIFETIME, TimerMode::Once)), + )); + } +} + +/// Event listener for asteroid destruction events. Shrinks and multiplies +/// asteroids until they vanish. +/// +/// - Large -> 2x Medium +/// - Medium -> 2x Small +/// - Small -> (despawned) +/// +/// The velocity of the child asteroids is scattered somewhat, as if they were +/// explosively pushed apart. +pub fn split_asteroids( + mut destroy_events: EventReader, + mut respawn_events: EventWriter, + mut commands: Commands, + query: Query<(&Transform, &Asteroid, &Velocity)>, +) { + for event in destroy_events.read() { + if let Ok((transform, rock, velocity)) = query.get(event.0) { + let next_size = rock.0.next(); + if let Some(size) = next_size { + let pos = transform.translation.xy(); + let left_offset = Vec2::from_angle(0.4); + let right_offset = Vec2::from_angle(-0.4); + respawn_events.write(SpawnAsteroid { + pos, + vel: left_offset.rotate(velocity.0), + size, + }); + respawn_events.write(SpawnAsteroid { + pos, + vel: right_offset.rotate(velocity.0), + size, + }); + } + // Always despawn the asteroid. New ones (may) be spawned in it's + // place, but this one is gone. + commands.entity(event.0).despawn(); + } + } +}