blackout/src/sound/footstep.rs

135 lines
4.4 KiB
Rust

use std::collections::HashMap;
use bevy::{prelude::*, transform::TransformSystem};
use bevy_synthizer::{Audio, Buffer, Sound};
use rand::prelude::*;
use crate::core::PointLike;
#[derive(Component, Clone, Debug)]
pub struct Footstep {
pub audio: Option<Audio>,
pub step_length: f32,
pub gain: f64,
pub pitch: Option<f64>,
pub pitch_variation: Option<f64>,
}
impl Default for Footstep {
fn default() -> Self {
Self {
audio: default(),
step_length: 0.8,
gain: 1.,
pitch: None,
pitch_variation: Some(0.15),
}
}
}
#[derive(Component, Clone, Default, Deref, DerefMut, Debug)]
pub struct Footsteps(pub Vec<Audio>);
impl Footsteps {
pub fn from_handles(handles: Vec<Handle<Buffer>>) -> Self {
Self(
handles
.iter()
.cloned()
.map(|v| v.into())
.collect::<Vec<Audio>>(),
)
}
}
fn added(mut commands: Commands, footsteps: Query<(Entity, &Footstep), Added<Footstep>>) {
for (entity, footstep) in &footsteps {
if let Some(audio) = &footstep.audio {
let mut pitch = 1.;
if let Some(pitch_variation) = footstep.pitch_variation {
pitch -= pitch_variation / 2.;
pitch += random::<f64>() * pitch_variation;
}
commands.entity(entity).insert(Sound {
audio: audio.clone(),
gain: footstep.gain,
pitch,
paused: true,
..default()
});
}
}
}
fn update(
mut commands: Commands,
mut last_step_distance: Local<HashMap<Entity, (f32, GlobalTransform)>>,
mut query: Query<(
Entity,
&Footstep,
&Parent,
Option<&mut Sound>,
Option<&Footsteps>,
)>,
transforms_storage: Query<&GlobalTransform>,
) {
for (entity, footstep, parent, sound, footsteps) in &mut query {
if let Ok(transform) = transforms_storage.get(**parent) {
if let Some(last) = last_step_distance.get(&entity) {
let distance = last.0 + (last.1.distance(transform));
if distance >= footstep.step_length {
last_step_distance.insert(entity, (0., *transform));
let audio = if let Some(footsteps) = footsteps {
if footsteps.len() == 1 {
Some(footsteps[0].clone())
} else if !footsteps.is_empty() {
let mut footsteps = footsteps.clone();
footsteps.shuffle(&mut thread_rng());
let mut audio = footsteps.first().unwrap().clone();
if let Some(sound) = sound {
while sound.audio == audio {
footsteps.shuffle(&mut thread_rng());
audio = footsteps[0].clone();
}
}
Some(audio)
} else {
None
}
} else {
footstep.audio.clone()
};
if let Some(audio) = audio {
let mut pitch = 1.;
if let Some(pitch_variation) = footstep.pitch_variation {
pitch -= pitch_variation / 2.;
pitch += random::<f64>() * pitch_variation;
}
commands.entity(entity).insert(Sound {
audio,
gain: footstep.gain,
pitch,
..default()
});
}
} else if last.1 != *transform {
last_step_distance.insert(entity, (distance, *transform));
}
} else {
last_step_distance.insert(entity, (0., *transform));
}
}
}
}
pub struct FootstepPlugin;
impl Plugin for FootstepPlugin {
fn build(&self, app: &mut App) {
app.add_systems(PreUpdate, added).add_systems(
FixedPostUpdate,
update.after(TransformSystem::TransformPropagate),
);
}
}