Compare commits
6 Commits
70f2313766
...
33828d6a85
| Author | SHA1 | Date | |
|---|---|---|---|
| 33828d6a85 | |||
| 5e2018d3e4 | |||
| 7c877264a2 | |||
| 16842c13f7 | |||
| e199db16eb | |||
| f17910daef |
31
src/lib.rs
31
src/lib.rs
@@ -1,3 +1,7 @@
|
||||
//! Asteroids, implemented as a Bevy plugin.
|
||||
//!
|
||||
//! Compile-time configurables can be found in the [`config`] module.
|
||||
|
||||
pub mod config;
|
||||
mod events;
|
||||
mod machinery;
|
||||
@@ -24,6 +28,7 @@ use bevy_rapier2d::{
|
||||
use machinery::Lifetime;
|
||||
use resources::{GameAssets, Lives, Score, WorldSize};
|
||||
|
||||
/// The main game plugin.
|
||||
pub struct AsteroidPlugin;
|
||||
|
||||
impl Plugin for AsteroidPlugin {
|
||||
@@ -35,10 +40,7 @@ impl Plugin for AsteroidPlugin {
|
||||
RapierDebugRenderPlugin::default(),
|
||||
))
|
||||
.insert_resource(ClearColor(BACKGROUND_COLOR))
|
||||
.insert_resource(WorldSize {
|
||||
width: WINDOW_SIZE.x,
|
||||
height: WINDOW_SIZE.y,
|
||||
})
|
||||
.insert_resource(WorldSize::default())
|
||||
.insert_resource(Lives(3))
|
||||
.register_type::<Lives>()
|
||||
.insert_resource(Score(0))
|
||||
@@ -90,6 +92,7 @@ fn debug_collision_event_printer(mut collision_events: EventReader<CollisionEven
|
||||
}
|
||||
}
|
||||
|
||||
/// The game's main state tracking mechanism, registered with Bevy as a [`State`](`bevy::prelude::State`).
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, States)]
|
||||
pub enum GameState {
|
||||
TitleScreen, // Program is started. Present title screen and await user start
|
||||
@@ -102,9 +105,9 @@ fn spawn_camera(mut commands: Commands) {
|
||||
commands.spawn(Camera2d);
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if "W" is pressed and increases velocity accordingly.
|
||||
*/
|
||||
/// Player's thruster control system.
|
||||
///
|
||||
/// Checks if "W" is pressed and increases velocity accordingly.
|
||||
fn input_ship_thruster(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut query: Query<(&mut physics::Velocity, &Transform, &mut Children), With<Ship>>,
|
||||
@@ -135,10 +138,10 @@ fn input_ship_thruster(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if "A" or "D" is pressed and updates the player's Rotation component accordingly
|
||||
Does *not* rotate the graphical widget! (that's done by the `apply_rotation_to_mesh` system)
|
||||
*/
|
||||
/// Player's rotation control system.
|
||||
///
|
||||
/// Checks if "A" or "D" is pressed and updates the player's [`AngularVelocity`]
|
||||
/// component accordingly.
|
||||
fn input_ship_rotation(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut query: Query<&mut AngularVelocity, With<Ship>>,
|
||||
@@ -157,6 +160,12 @@ fn input_ship_rotation(
|
||||
}
|
||||
}
|
||||
|
||||
/// Player's gun trigger.
|
||||
///
|
||||
/// Checks if the spacebar has just been pressed, spawning a bullet if so.
|
||||
///
|
||||
/// TODO: Hook up a timer to control weapon fire-rate. Something will have to
|
||||
/// tick those timers. Maybe this system?
|
||||
fn input_ship_shoot(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
ship: Single<(&Transform, &physics::Velocity), With<Ship>>,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//! These are Systems that power the main game mechanics (and some misc items to support them)
|
||||
|
||||
//! Systems, Components, and any other items for powering the game logic.
|
||||
//!
|
||||
//! Where the objects (ship, asteroid, etc) carry their own behavioral systems,
|
||||
//! the *game* keeps its main logic here. Its for ambient behaviors, like
|
||||
//! asteroid spawning, or eventually the flying saucer spawns.
|
||||
use rand::{Rng, SeedableRng};
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -7,6 +10,12 @@ use bevy::prelude::*;
|
||||
|
||||
use crate::{WorldSize, events::SpawnAsteroid, objects::AsteroidSize};
|
||||
|
||||
/// Asteroid spawning parameters and state.
|
||||
///
|
||||
/// This struct keeps track of the rng and timer for spawning asteroids. In the
|
||||
/// future it may contain additional fields to allow for more control.
|
||||
///
|
||||
/// It's values are operated by the [`tick_asteroid_manager`] system.
|
||||
#[derive(Resource)]
|
||||
pub struct AsteroidSpawner {
|
||||
rng: std::sync::Mutex<rand::rngs::StdRng>,
|
||||
@@ -26,8 +35,8 @@ impl AsteroidSpawner {
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the asteroid spawn timer and spawn any asteroids
|
||||
/// that are due this frame.
|
||||
/// Update the asteroid spawn timer in the [`AsteroidSpawner`] resource, and
|
||||
/// spawns any asteroids that are due this frame.
|
||||
pub fn tick_asteroid_manager(
|
||||
mut events: EventWriter<SpawnAsteroid>,
|
||||
mut spawner: ResMut<AsteroidSpawner>,
|
||||
@@ -46,7 +55,7 @@ pub fn tick_asteroid_manager(
|
||||
let spawn_angle = rng.random_range(0.0..(std::f32::consts::PI * 2.0));
|
||||
// Rho will be the radius of a circle bordering the viewport, multiplied by 1.2
|
||||
// TODO: Use view diagonal to get a minimally sized circle around the play area
|
||||
let spawn_distance = play_area.width.max(play_area.height) / 2.0;
|
||||
let spawn_distance = play_area.x.max(play_area.y) / 2.0;
|
||||
|
||||
// Convert polar to Cartesian, use as position
|
||||
let pos = Vec2::new(
|
||||
@@ -77,9 +86,11 @@ pub fn tick_asteroid_manager(
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a lifetime on an entity to automatically delete it after expiration.
|
||||
#[derive(Component)]
|
||||
pub struct Lifetime(pub Timer);
|
||||
|
||||
/// System to tick the [`Lifetime`] timers and despawn expired entities.
|
||||
pub fn tick_lifetimes(
|
||||
mut commands: Commands,
|
||||
time: Res<Time>,
|
||||
|
||||
@@ -28,6 +28,7 @@ use crate::{
|
||||
physics::{Velocity, Wrapping},
|
||||
};
|
||||
|
||||
/// The asteroid, defined entirely by [it's size](`AsteroidSize`).
|
||||
#[derive(Component, Deref, DerefMut)]
|
||||
pub struct Asteroid(pub AsteroidSize);
|
||||
|
||||
@@ -39,6 +40,8 @@ pub enum AsteroidSize {
|
||||
}
|
||||
|
||||
impl AsteroidSize {
|
||||
/// Convenience util to get the "next smallest" size. Useful for splitting
|
||||
/// after collision.
|
||||
pub fn next(&self) -> Option<Self> {
|
||||
match self {
|
||||
AsteroidSize::Small => None,
|
||||
@@ -48,9 +51,11 @@ impl AsteroidSize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Marker component for the player's ship.
|
||||
#[derive(Component)]
|
||||
pub struct Ship;
|
||||
|
||||
/// Marker component for bullets.
|
||||
#[derive(Component)]
|
||||
pub struct Bullet;
|
||||
|
||||
@@ -126,6 +131,11 @@ pub fn split_asteroids(
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns the player at the world origin. Used during the state change to
|
||||
/// [`GameState::Playing`] to spawn the player.
|
||||
///
|
||||
/// This only spawns the player. For player **re**-spawn activity, see the
|
||||
/// [`ship_impact_listener()`] system.
|
||||
pub fn spawn_player(mut commands: Commands, game_assets: Res<GameAssets>) {
|
||||
commands
|
||||
.spawn((
|
||||
@@ -160,6 +170,9 @@ pub fn bullet_impact_listener(mut commands: Commands, mut events: EventReader<Bu
|
||||
|
||||
/// Watch for [`ShipDestroy`] events and update game state accordingly.
|
||||
///
|
||||
/// One life is taken from the counter, asteroids are cleared, and the player
|
||||
/// is placed back at the origin. If lives reach 0, this system will change
|
||||
/// states to [`GameState::GameOver`].
|
||||
/// - Subtract a life
|
||||
/// - Check life count. If 0, go to game-over state
|
||||
/// - Clear all asteroids
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
//! Custom physics items
|
||||
//!
|
||||
//! TODO: Refactor in terms of Rapier2D, *or* implement colliders and remove it.
|
||||
//!
|
||||
//! Position, both translation and rotation, are tracked by the built-in
|
||||
//! Bevy [`Transform`] component. This module adds a (Linear) [`Velocity`] and
|
||||
//! [`AngularVelocity`] component to the mix.
|
||||
//! These two components are operated by simple integrator systems to
|
||||
//! accumulate translation and rotation from the velocities.
|
||||
//!
|
||||
//! The [`Wrapping`] component marks things that should wrap around the world
|
||||
//! boundaries. It's a kind of physical property of this world, so I'm putting
|
||||
//! it here.
|
||||
//!
|
||||
//! Collisions are also considered physics. After all, I got *a physics engine*
|
||||
//! to detect them for me (so that I don't have to write clipping code).
|
||||
|
||||
use crate::{
|
||||
WorldSize, events,
|
||||
@@ -42,9 +56,9 @@ pub(crate) fn wrap_entities(
|
||||
mut query: Query<&mut Transform, With<Wrapping>>,
|
||||
world_size: Res<WorldSize>,
|
||||
) {
|
||||
let right = world_size.width / 2.0;
|
||||
let right = world_size.x / 2.0;
|
||||
let left = -right;
|
||||
let top = world_size.height / 2.0;
|
||||
let top = world_size.y / 2.0;
|
||||
let bottom = -top;
|
||||
|
||||
for mut pos in query.iter_mut() {
|
||||
|
||||
@@ -10,7 +10,7 @@ use bevy::{
|
||||
Vec2,
|
||||
primitives::{Circle, Triangle2d},
|
||||
},
|
||||
prelude::{Deref, Reflect, ReflectResource},
|
||||
prelude::{Deref, DerefMut, Reflect, ReflectResource},
|
||||
render::mesh::Mesh,
|
||||
sprite::ColorMaterial,
|
||||
};
|
||||
@@ -19,7 +19,7 @@ use bevy_inspector_egui::inspector_options::ReflectInspectorOptions;
|
||||
|
||||
use crate::{
|
||||
ASTEROID_SMALL_COLOR, BULLET_COLOR, PLAYER_SHIP_COLOR, SHIP_THRUSTER_COLOR_ACTIVE,
|
||||
SHIP_THRUSTER_COLOR_INACTIVE,
|
||||
SHIP_THRUSTER_COLOR_INACTIVE, config::WINDOW_SIZE,
|
||||
};
|
||||
|
||||
#[derive(Resource, Debug, Deref, Clone, Copy)]
|
||||
@@ -41,10 +41,13 @@ impl From<Lives> for String {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct WorldSize {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
#[derive(Deref, DerefMut, Resource)]
|
||||
pub struct WorldSize(Vec2);
|
||||
|
||||
impl Default for WorldSize {
|
||||
fn default() -> Self {
|
||||
WorldSize(Vec2::new(WINDOW_SIZE.x, WINDOW_SIZE.y))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
|
||||
Reference in New Issue
Block a user