use std::collections::HashMap; use bevy::{asset::HandleId, prelude::*, transform::TransformSystem}; use bevy_openal::{Buffer, Sound, SoundState}; use rand::random; use crate::{commands::RunIfExistsExt, core::PointLike}; #[derive(Component, Clone, Debug, Reflect)] #[reflect(Component)] pub struct Footstep { pub sound: HandleId, pub step_length: f32, pub gain: f32, pub reference_distance: f32, pub max_distance: f32, pub rolloff_factor: f32, pub pitch: f32, pub pitch_variation: Option, } impl Default for Footstep { fn default() -> Self { Self { sound: "".into(), step_length: 0.8, gain: 1., reference_distance: 1., max_distance: f32::MAX, rolloff_factor: 1., pitch: 1., pitch_variation: Some(0.15), } } } #[derive(Bundle, Default)] pub struct FootstepBundle { pub footstep: Footstep, pub transform: Transform, pub global_transform: GlobalTransform, } fn added( mut commands: Commands, footsteps: Query<(Entity, &Footstep), Added>, assets: Res>, ) { for (entity, footstep) in footsteps.iter() { let buffer = assets.get_handle(footstep.sound); commands.run_if_exists(entity, move |mut entity| { entity.insert(Sound { buffer, state: SoundState::Stopped, ..default() }); }); } } fn update( mut last_step_distance: Local>, mut footsteps: Query<(Entity, &Footstep, &Parent, &mut Sound), Changed>, transforms_storage: Query<&Transform>, ) { for (entity, footstep, parent, mut sound) in footsteps.iter_mut() { let coordinates = transforms_storage.get(**parent).unwrap(); if let Some(last) = last_step_distance.get(&entity) { let distance = last.0 + (last.1.distance(coordinates)); if distance >= footstep.step_length { last_step_distance.insert(entity, (0., *coordinates)); sound.gain = footstep.gain; sound.reference_distance = footstep.reference_distance; sound.max_distance = footstep.max_distance; sound.rolloff_factor = footstep.rolloff_factor; sound.pitch = footstep.pitch; if let Some(pitch_variation) = footstep.pitch_variation { let mut pitch = footstep.pitch - pitch_variation / 2.; pitch += random::() * pitch_variation; sound.pitch = pitch; } sound.play(); } else if last.1 != *coordinates { last_step_distance.insert(entity, (distance, *coordinates)); } } else { last_step_distance.insert(entity, (0., *coordinates)); } } } pub struct FootstepPlugin; impl Plugin for FootstepPlugin { fn build(&self, app: &mut App) { app.register_type::() .add_system(added) .add_system_to_stage( CoreStage::PostUpdate, update.after(TransformSystem::TransformPropagate), ); } }