use std::collections::VecDeque; use bevy::prelude::*; use bevy::math::NormedVectorSpace; use avian3d::prelude::*; pub struct ProjectilePlugin; impl Plugin for ProjectilePlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, setup_projectiles); app.insert_resource(ProjectileSpawner::default()); app.add_systems(PreUpdate, do_projectile_spawn); app.add_event::(); app.add_systems(PhysicsSchedule, do_projectile_hitscan_flight.in_set(PhysicsStepSet::Last)); } } #[derive(Resource)] pub struct ProjectileResources { pub mesh: Mesh3d, pub material: MeshMaterial3d, } fn setup_projectiles( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { commands.insert_resource(ProjectileResources { mesh: Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 2.0))), material: MeshMaterial3d(materials.add(StandardMaterial { base_color: Color::srgb(0.3, 0.3, 0.3), ..default() })), }); } pub struct ProjectileData { pub position: Vec3, pub direction: Dir3, pub speed: f32, pub mass: f32, } #[derive(Resource, Default)] pub struct ProjectileSpawner { queue: VecDeque, } impl ProjectileSpawner { pub fn spawn(&mut self, data: ProjectileData) { self.queue.push_back(data); } } #[derive(Component)] pub struct Projectile; fn do_projectile_spawn( mut commands: Commands, mut spawner: ResMut, resources: Res, ) { while let Some(spawn_data) = spawner.queue.pop_front() { let rotation = Quat::from_rotation_arc(Vec3::NEG_Z, spawn_data.direction.into()); let transform = Transform::from_translation(spawn_data.position).with_rotation(rotation); commands.spawn(( Projectile, resources.mesh.clone(), resources.material.clone(), transform, LinearVelocity(spawn_data.direction * spawn_data.speed), Mass(spawn_data.mass), )); } } #[derive(Event)] #[allow(unused)] pub struct ProjectileHitEvent { pub entity: Entity, pub normal: Vec3, } fn do_projectile_hitscan_flight( mut commands: Commands, mut query: Query<(Entity, &mut Transform, &LinearVelocity), With>, mut event_writer: EventWriter, spatial_query: SpatialQuery, time: Res