diff --git a/src/game/level.rs b/src/game/level.rs deleted file mode 100644 index db6e32e..0000000 --- a/src/game/level.rs +++ /dev/null @@ -1,60 +0,0 @@ - -use bevy::prelude::*; -use avian3d::prelude::*; - -mod target_wall; - -pub struct LevelPlugin; - -impl Plugin for LevelPlugin -{ - fn build(&self, app: &mut App) - { - app.add_plugins(target_wall::TargetWallPlugin); - app.add_systems(Startup, setup_level); - } -} - -fn setup_level( - mut commands: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, -) { - const GROUND_SIZE: f32 = 100.0; - - // spawn the ground plane - commands.spawn(( - Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(GROUND_SIZE * 0.5)))), - MeshMaterial3d(materials.add(StandardMaterial { - base_color: Color::srgb(0.761, 0.698, 0.502), - ..default() - })), - Transform::IDENTITY, - Collider::cuboid(GROUND_SIZE, 0.0, GROUND_SIZE), - RigidBody::Static, - )); - - // spawn a box - commands.spawn(( - Mesh3d(meshes.add(Cuboid::from_corners( - Vec3::new(-0.5, -0.5, -0.5), Vec3::new(0.5, 0.5, 0.5) - ))), - MeshMaterial3d(materials.add(StandardMaterial { - base_color: Color::srgb(0.7, 0.1, 0.1), - ..default() - })), - Transform::from_xyz(2.0, 0.5, -5.0), - Collider::cuboid(1.0, 1.0, 1.0), - RigidBody::Dynamic, - )); - - // spawn a light source - commands.spawn(( - DirectionalLight { - illuminance: light_consts::lux::AMBIENT_DAYLIGHT, - shadows_enabled: true, - ..default() - }, - Transform::from_xyz(100.0, 200.0, 100.0).looking_at(Vec3::ZERO, Vec3::Y), - )); -} diff --git a/src/game/mod.rs b/src/game/mod.rs index 23ba98d..6328964 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,8 +1,15 @@ +use bevy::input::ButtonState; use bevy::prelude::*; +use bevy::input::mouse::MouseButtonInput; +use avian3d::prelude::*; -mod player; -mod level; +use crate::physics::BulletFiredEvent; +use crate::ui::camera::CameraController; +use crate::ui::cursor::CursorGrabState; + +mod target_wall; +use target_wall::TargetWallPlugin; pub struct GamePlugin; @@ -10,7 +17,92 @@ impl Plugin for GamePlugin { fn build(&self, app: &mut App) { - app.add_plugins(player::PlayerPlugin); - app.add_plugins(level::LevelPlugin); + app.add_plugins(TargetWallPlugin); + + app.add_systems(Startup, setup_level); + + app.add_systems(Update, do_shoot_on_left_click + .run_if(in_state(CursorGrabState(true))) + .run_if(on_event::)) + ; } } + +fn setup_level( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + const GROUND_SIZE: f32 = 100.0; + + // spawn the camera + commands.spawn(( + CameraController { + sensitivity: Vec2::new(0.0007, 0.0007), + }, + Camera3d::default(), + Transform::from_xyz(0.0, 1.75, 2.0), + )); + + // spawn the ground plane + commands.spawn(( + Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(GROUND_SIZE * 0.5)))), + MeshMaterial3d(materials.add(StandardMaterial { + base_color: Color::srgb(0.761, 0.698, 0.502), + ..default() + })), + Transform::IDENTITY, + Collider::cuboid(GROUND_SIZE, 0.0, GROUND_SIZE), + RigidBody::Static, + )); + + // spawn some boxes to shoot at + for x in [0.0, 1.5, 3.0] + { + commands.spawn(( + Mesh3d(meshes.add(Cuboid::from_corners( + Vec3::ZERO, Vec3::ONE, + ))), + MeshMaterial3d(materials.add(StandardMaterial { + base_color: Color::srgb(0.7, 0.1, 0.1), + ..default() + })), + Transform::from_xyz(x, 0.5, -2.0), + Collider::cuboid(1.0, 1.0, 1.0), + RigidBody::Dynamic, + )); + } + + // spawn a light source + commands.spawn(( + DirectionalLight { + illuminance: light_consts::lux::AMBIENT_DAYLIGHT, + shadows_enabled: true, + ..default() + }, + Transform::from_xyz(100.0, 200.0, 100.0).looking_at(Vec3::ZERO, Vec3::Y), + )); +} + +fn do_shoot_on_left_click( + camera_query: Single<&GlobalTransform, With>, + mut mouse_events: EventReader, + mut bullet_events: EventWriter, +) { + let camera_transform = camera_query.into_inner(); + + let bullet_fired_event = BulletFiredEvent { + position: camera_transform.translation() + Dir3::NEG_X * 2.0, + direction: camera_transform.forward(), + speed: 200.0, + radius: 0.008, + density: 11.0, + }; + + bullet_events.send_batch(mouse_events.read() + .filter(|event| event.button == MouseButton::Left) + .filter(|event| event.state == ButtonState::Pressed) + .map(|_| bullet_fired_event) + .collect::>() + ); +} diff --git a/src/game/player.rs b/src/game/player.rs deleted file mode 100644 index b364f24..0000000 --- a/src/game/player.rs +++ /dev/null @@ -1,68 +0,0 @@ - -use bevy::prelude::*; -use bevy::input::{mouse::MouseButtonInput, ButtonState}; -use avian3d::prelude::*; - -use crate::physics::BulletFiredEvent; -use crate::ui::camera::CameraController; -use crate::ui::cursor::CursorGrabState; - -pub struct PlayerPlugin; - -impl Plugin for PlayerPlugin -{ - fn build(&self, app: &mut App) - { - app.add_systems(Startup, setup_player); - app.add_systems(Update, do_shoot_on_left_click - .run_if(in_state(CursorGrabState(true))) - .run_if(on_event::) - ); - } -} - -#[derive(Component)] -pub struct Player; - -fn setup_player( - mut commands: Commands, -) { - commands - .spawn(( - Player, - Transform::from_xyz(0.0, 1.0, 0.0), - Collider::capsule(0.5, 1.0), - RigidBody::Kinematic, - Dominance(32), - Visibility::default(), - )) - .with_child(( - CameraController { - sensitivity: Vec2::new(0.0007, 0.0007), - }, - Camera3d::default(), - Transform::from_xyz(0.0, 0.75, 0.0), - )) - ; -} - -fn do_shoot_on_left_click( - query: Single<&GlobalTransform, With>, - mut mouse_events: EventReader, - mut bullet_events: EventWriter, -) { - let transform = query.into_inner(); - - for _ in mouse_events.read() - .filter(|event| event.button == MouseButton::Left) - .filter(|event| event.state == ButtonState::Pressed) - { - bullet_events.send(BulletFiredEvent { - position: transform.translation() + transform.forward() * 0.5, - direction: transform.forward(), - radius: 0.008, - density: 11.0, - velocity: 910.0, - }); - } -} diff --git a/src/game/level/target_wall.rs b/src/game/target_wall.rs similarity index 95% rename from src/game/level/target_wall.rs rename to src/game/target_wall.rs index a4f3285..df29f92 100644 --- a/src/game/level/target_wall.rs +++ b/src/game/target_wall.rs @@ -150,6 +150,7 @@ fn setup_target_wall( } fn on_targets_shot( + mut commands: Commands, mut collision_events: EventReader, mut query_set: ParamSet<( Query>, @@ -163,7 +164,7 @@ fn on_targets_shot( // between a bullet and a target // let bullet_query = &mut query_set.p0(); - let Ok(_) = collision.query_unique(bullet_query) else { + let Ok(bullet_entity) = collision.query_unique(bullet_query) else { continue; // the collision doesn't involve a bullet }; // @@ -174,5 +175,9 @@ fn on_targets_shot( // move the target *target_transform = target_resources.random_transform(target_handle); + + // despawn the bullet + // TODO: this should be handled by the projectiles system + commands.entity(bullet_entity).despawn_recursive(); } } diff --git a/src/physics/mod.rs b/src/physics/mod.rs index a08f6bd..bdb339b 100644 --- a/src/physics/mod.rs +++ b/src/physics/mod.rs @@ -12,14 +12,14 @@ pub struct PhysicsPlugins; impl PluginGroup for PhysicsPlugins { - fn build(self) -> bevy::app::PluginGroupBuilder + fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() .add_group(avian3d::PhysicsPlugins::default()) .add(avian3d::debug_render::PhysicsDebugPlugin::default()) - .add(WorldBoundsPlugin) .add(ProjectilesPlugin) + .add(WorldBoundsPlugin) } } diff --git a/src/physics/projectiles.rs b/src/physics/projectiles.rs index c9b7e51..524d5bb 100644 --- a/src/physics/projectiles.rs +++ b/src/physics/projectiles.rs @@ -2,6 +2,8 @@ use bevy::prelude::*; use avian3d::prelude::*; +use crate::util::ecs::PathTracer; + pub struct ProjectilesPlugin; impl Plugin for ProjectilesPlugin @@ -38,14 +40,14 @@ fn setup_projectiles( }); } -#[derive(Event, Debug)] +#[derive(Event, Copy, Clone, PartialEq, Debug)] pub struct BulletFiredEvent { pub position: Vec3, pub direction: Dir3, + pub speed: f32, pub radius: f32, pub density: f32, - pub velocity: f32, } #[derive(Component)] @@ -71,9 +73,10 @@ fn on_bullet_fired_event( transform, RigidBody::Dynamic, ColliderDensity(event.density), - LinearVelocity(event.velocity * event.direction), - SpeculativeMargin(0.0), + SpeculativeMargin(event.radius), SweptCcd::LINEAR, + LinearVelocity(event.speed * event.direction), + PathTracer::default(), ) }; diff --git a/src/util/ecs/despawn_timer.rs b/src/util/ecs/despawn_timer.rs new file mode 100644 index 0000000..5b730b2 --- /dev/null +++ b/src/util/ecs/despawn_timer.rs @@ -0,0 +1,30 @@ + +use bevy::prelude::*; + +pub struct DespawnTimerPlugin; + +impl Plugin for DespawnTimerPlugin +{ + fn build(&self, app: &mut App) + { + app.add_systems(Update, do_despawn_timer_tick_and_despawn); + } +} + +#[derive(Component)] +pub struct DespawnTimer(pub Timer); + +fn do_despawn_timer_tick_and_despawn( + mut commands: Commands, + mut query: Query<(Entity, &mut DespawnTimer)>, + time: Res