2022-05-23 16:51:44 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2022-08-03 14:43:07 +00:00
|
|
|
use bevy::{prelude::*, transform::TransformSystem};
|
2023-04-03 16:56:27 +00:00
|
|
|
use bevy_synthizer::{Audio, Buffer, Sound};
|
|
|
|
use rand::prelude::*;
|
2022-05-23 16:51:44 +00:00
|
|
|
|
2023-04-01 12:20:59 +00:00
|
|
|
use crate::core::PointLike;
|
2022-05-23 16:51:44 +00:00
|
|
|
|
2023-04-03 15:39:43 +00:00
|
|
|
#[derive(Component, Clone, Debug)]
|
2022-05-23 16:51:44 +00:00
|
|
|
pub struct Footstep {
|
2023-04-03 16:56:27 +00:00
|
|
|
pub audio: Option<Audio>,
|
2022-05-23 16:51:44 +00:00
|
|
|
pub step_length: f32,
|
2022-05-23 18:35:25 +00:00
|
|
|
pub gain: f64,
|
|
|
|
pub pitch: Option<f64>,
|
|
|
|
pub pitch_variation: Option<f64>,
|
2022-05-23 16:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Footstep {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2023-04-03 15:39:43 +00:00
|
|
|
audio: default(),
|
2022-05-23 16:51:44 +00:00
|
|
|
step_length: 0.8,
|
|
|
|
gain: 1.,
|
2022-05-23 18:35:25 +00:00
|
|
|
pitch: None,
|
2022-05-23 16:51:44 +00:00
|
|
|
pitch_variation: Some(0.15),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-03 16:56:27 +00:00
|
|
|
#[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>>(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-03 14:43:07 +00:00
|
|
|
fn added(mut commands: Commands, footsteps: Query<(Entity, &Footstep), Added<Footstep>>) {
|
2023-03-28 17:13:23 +00:00
|
|
|
for (entity, footstep) in &footsteps {
|
2023-04-03 16:56:27 +00:00
|
|
|
if let Some(audio) = &footstep.audio {
|
|
|
|
commands.entity(entity).insert(Sound {
|
|
|
|
audio: audio.clone(),
|
|
|
|
paused: true,
|
|
|
|
..default()
|
|
|
|
});
|
|
|
|
}
|
2022-05-23 16:51:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update(
|
2023-04-03 16:56:27 +00:00
|
|
|
mut commands: Commands,
|
2022-08-06 16:05:04 +00:00
|
|
|
mut last_step_distance: Local<HashMap<Entity, (f32, GlobalTransform)>>,
|
2023-04-03 16:56:27 +00:00
|
|
|
mut query: Query<(
|
|
|
|
Entity,
|
|
|
|
&Footstep,
|
|
|
|
&Parent,
|
|
|
|
Option<&mut Sound>,
|
|
|
|
Option<&Footsteps>,
|
|
|
|
)>,
|
2022-08-06 16:05:04 +00:00
|
|
|
transforms_storage: Query<&GlobalTransform>,
|
2022-05-23 16:51:44 +00:00
|
|
|
) {
|
2023-04-03 16:56:27 +00:00
|
|
|
for (entity, footstep, parent, sound, footsteps) in &mut query {
|
2022-08-06 16:05:04 +00:00
|
|
|
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));
|
2023-04-03 16:56:27 +00:00
|
|
|
if let Some(footsteps) = footsteps {
|
|
|
|
let audio = 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
|
|
|
|
};
|
|
|
|
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 let Some(mut sound) = sound {
|
|
|
|
sound.gain = footstep.gain;
|
|
|
|
sound.pitch = footstep.pitch.unwrap_or(1.);
|
|
|
|
if let Some(pitch_variation) = footstep.pitch_variation {
|
|
|
|
let mut pitch = sound.pitch - pitch_variation / 2.;
|
|
|
|
pitch += random::<f64>() * pitch_variation;
|
|
|
|
sound.pitch = pitch;
|
|
|
|
}
|
|
|
|
sound.paused = false;
|
|
|
|
sound.restart = true;
|
2022-05-23 18:35:25 +00:00
|
|
|
}
|
2022-08-06 16:05:04 +00:00
|
|
|
} else if last.1 != *transform {
|
|
|
|
last_step_distance.insert(entity, (distance, *transform));
|
2022-05-23 16:51:44 +00:00
|
|
|
}
|
2022-08-06 16:05:04 +00:00
|
|
|
} else {
|
|
|
|
last_step_distance.insert(entity, (0., *transform));
|
2022-05-23 16:51:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct FootstepPlugin;
|
|
|
|
|
|
|
|
impl Plugin for FootstepPlugin {
|
|
|
|
fn build(&self, app: &mut App) {
|
2023-04-03 15:39:43 +00:00
|
|
|
app.add_system(added.in_base_set(CoreSet::PreUpdate))
|
2023-03-28 16:57:37 +00:00
|
|
|
.add_system(
|
|
|
|
update
|
|
|
|
.after(TransformSystem::TransformPropagate)
|
|
|
|
.in_base_set(CoreSet::PostUpdate),
|
2022-05-23 16:51:44 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|