More footstep sfx based on distance

This commit is contained in:
jiminycrick 2021-01-19 14:50:18 -08:00
parent 03a1452d16
commit 7dda25a66d
14 changed files with 140 additions and 37 deletions

View File

@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Protection rating was moved to the top left of the loadout view
- Changed camera smoothing to be off by default.
- Fixed AI behavior so only humanoids will attempt to roll
- Footstep SFX is now dependant on distance moved, not time since last play
### Removed

View File

@ -64,7 +64,27 @@
],
threshold: 0.25,
),
Run: (
Run(Earth): (
files: [
"voxygen.audio.sfx.footsteps.stepdirt_1",
"voxygen.audio.sfx.footsteps.stepdirt_2",
"voxygen.audio.sfx.footsteps.stepdirt_3",
"voxygen.audio.sfx.footsteps.stepdirt_4",
"voxygen.audio.sfx.footsteps.stepdirt_5",
],
threshold: 1.6,
),
QuadRun(Earth): (
files: [
"voxygen.audio.sfx.footsteps.stepdirt_1",
"voxygen.audio.sfx.footsteps.stepdirt_2",
"voxygen.audio.sfx.footsteps.stepdirt_3",
"voxygen.audio.sfx.footsteps.stepdirt_4",
"voxygen.audio.sfx.footsteps.stepdirt_5",
],
threshold: 0.8,
),
Run(Grass): (
files: [
"voxygen.audio.sfx.footsteps.stepgrass_1",
"voxygen.audio.sfx.footsteps.stepgrass_2",
@ -73,9 +93,9 @@
"voxygen.audio.sfx.footsteps.stepgrass_5",
"voxygen.audio.sfx.footsteps.stepgrass_6",
],
threshold: 0.25,
threshold: 1.6,
),
QuadRun: (
QuadRun(Grass): (
files: [
"voxygen.audio.sfx.footsteps.stepgrass_1",
"voxygen.audio.sfx.footsteps.stepgrass_2",
@ -84,23 +104,41 @@
"voxygen.audio.sfx.footsteps.stepgrass_5",
"voxygen.audio.sfx.footsteps.stepgrass_6",
],
threshold: 0.12,
threshold: 0.8,
),
SnowRun: (
Run(Snow): (
files: [
"voxygen.audio.sfx.footsteps.snow_step_1",
"voxygen.audio.sfx.footsteps.snow_step_2",
"voxygen.audio.sfx.footsteps.snow_step_3",
],
threshold: 0.25,
threshold: 1.6,
),
QuadSnowRun: (
QuadRun(Snow): (
files: [
"voxygen.audio.sfx.footsteps.snow_step_1",
"voxygen.audio.sfx.footsteps.snow_step_2",
"voxygen.audio.sfx.footsteps.snow_step_3",
],
threshold: 0.12,
threshold: 0.8,
),
Run(Rock): (
files: [
"voxygen.audio.sfx.footsteps.stone_step_1",
"voxygen.audio.sfx.footsteps.stone_step_2",
"voxygen.audio.sfx.footsteps.stone_step_3",
"voxygen.audio.sfx.footsteps.stone_step_4",
],
threshold: 1.6,
),
QuadRun(Rock): (
files: [
"voxygen.audio.sfx.footsteps.stone_step_1",
"voxygen.audio.sfx.footsteps.stone_step_2",
"voxygen.audio.sfx.footsteps.stone_step_3",
"voxygen.audio.sfx.footsteps.stone_step_4",
],
threshold: 0.8,
),
//ExperienceGained: (
// files: [

BIN
assets/voxygen/audio/sfx/footsteps/stepdirt_1.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/audio/sfx/footsteps/stepdirt_2.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/audio/sfx/footsteps/stepdirt_3.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/audio/sfx/footsteps/stepdirt_4.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/audio/sfx/footsteps/stepdirt_5.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/audio/sfx/footsteps/stone_step_1.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/audio/sfx/footsteps/stone_step_2.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/audio/sfx/footsteps/stone_step_3.wav (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/audio/sfx/footsteps/stone_step_4.wav (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -10,6 +10,7 @@ use crate::{
use client::Client;
use common::{
comp::{Body, CharacterState, PhysicsState, Pos, Vel},
resources::DeltaTime,
terrain::{BlockKind, TerrainChunk},
vol::ReadVol,
};
@ -25,6 +26,7 @@ struct PreviousEntityState {
time: Instant,
on_ground: bool,
in_water: bool,
distance_travelled: f32,
}
impl Default for PreviousEntityState {
@ -34,6 +36,7 @@ impl Default for PreviousEntityState {
time: Instant::now(),
on_ground: true,
in_water: false,
distance_travelled: 0.0,
}
}
}
@ -112,6 +115,7 @@ impl EventMapper for MovementEventMapper {
underwater,
);
internal_state.time = Instant::now();
internal_state.distance_travelled = 0.0;
}
// update state to determine the next event. We only record the time (above) if
@ -123,6 +127,8 @@ impl EventMapper for MovementEventMapper {
} else {
internal_state.in_water = false;
}
let dt = ecs.fetch::<DeltaTime>().0;
internal_state.distance_travelled += vel.0.magnitude() * dt;
}
}
@ -156,14 +162,19 @@ impl MovementEventMapper {
/// any) needs to satisfy two conditions to be allowed to play:
/// 1. An sfx.ron entry exists for the movement (we need to know which sound
/// file(s) to play) 2. The sfx has not been played since it's timeout
/// threshold has elapsed, which prevents firing every tick
/// threshold has elapsed, which prevents firing every tick. For movement,
/// threshold is not a time, but a distance.
fn should_emit(
previous_state: &PreviousEntityState,
sfx_trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
) -> bool {
if let Some((event, item)) = sfx_trigger_item {
if &previous_state.event == event {
previous_state.time.elapsed().as_secs_f32() >= item.threshold
match event {
SfxEvent::Run(_) => previous_state.distance_travelled >= item.threshold,
SfxEvent::QuadRun(_) => previous_state.distance_travelled >= item.threshold,
_ => previous_state.time.elapsed().as_secs_f32() >= item.threshold,
}
} else {
true
}
@ -199,8 +210,11 @@ impl MovementEventMapper {
SfxEvent::Sneak
} else {
match underfoot_block_kind {
BlockKind::Snow => SfxEvent::SnowRun,
_ => SfxEvent::Run,
BlockKind::Snow => SfxEvent::Run(BlockKind::Snow),
BlockKind::Rock | BlockKind::WeakRock => SfxEvent::Run(BlockKind::Rock),
BlockKind::Sand => SfxEvent::Run(BlockKind::Sand),
BlockKind::Air => SfxEvent::Idle,
_ => SfxEvent::Run(BlockKind::Grass),
}
};
}
@ -230,8 +244,11 @@ impl MovementEventMapper {
SfxEvent::Swim
} else if physics_state.on_ground && vel.magnitude() > 0.1 {
match underfoot_block_kind {
BlockKind::Snow => SfxEvent::SnowRun,
_ => SfxEvent::Run,
BlockKind::Snow => SfxEvent::Run(BlockKind::Snow),
BlockKind::Rock | BlockKind::WeakRock => SfxEvent::Run(BlockKind::Rock),
BlockKind::Sand => SfxEvent::Run(BlockKind::Sand),
BlockKind::Air => SfxEvent::Idle,
_ => SfxEvent::Run(BlockKind::Grass),
}
} else {
SfxEvent::Idle
@ -248,8 +265,11 @@ impl MovementEventMapper {
SfxEvent::Swim
} else if physics_state.on_ground && vel.magnitude() > 0.1 {
match underfoot_block_kind {
BlockKind::Snow => SfxEvent::QuadSnowRun,
_ => SfxEvent::QuadRun,
BlockKind::Snow => SfxEvent::QuadRun(BlockKind::Snow),
BlockKind::Rock | BlockKind::WeakRock => SfxEvent::QuadRun(BlockKind::Rock),
BlockKind::Sand => SfxEvent::QuadRun(BlockKind::Sand),
BlockKind::Air => SfxEvent::Idle,
_ => SfxEvent::QuadRun(BlockKind::Grass),
}
} else {
SfxEvent::Idle

View File

@ -26,14 +26,17 @@ fn config_but_played_since_threshold_no_emit() {
// Triggered a 'Run' 0 seconds ago
let previous_state = PreviousEntityState {
event: SfxEvent::Run,
event: SfxEvent::Run(BlockKind::Grass),
time: Instant::now(),
on_ground: true,
in_water: false,
distance_travelled: 0.0,
};
let result =
MovementEventMapper::should_emit(&previous_state, Some((&SfxEvent::Run, &trigger_item)));
let result = MovementEventMapper::should_emit(
&previous_state,
Some((&SfxEvent::Run(BlockKind::Grass), &trigger_item)),
);
assert_eq!(result, false);
}
@ -50,10 +53,13 @@ fn config_and_not_played_since_threshold_emits() {
time: Instant::now().checked_add(Duration::from_secs(1)).unwrap(),
on_ground: true,
in_water: false,
distance_travelled: 0.0,
};
let result =
MovementEventMapper::should_emit(&previous_state, Some((&SfxEvent::Run, &trigger_item)));
let result = MovementEventMapper::should_emit(
&previous_state,
Some((&SfxEvent::Run(BlockKind::Grass), &trigger_item)),
);
assert_eq!(result, true);
}
@ -66,16 +72,19 @@ fn same_previous_event_elapsed_emits() {
};
let previous_state = PreviousEntityState {
event: SfxEvent::Run,
event: SfxEvent::Run(BlockKind::Grass),
time: Instant::now()
.checked_sub(Duration::from_millis(500))
.checked_sub(Duration::from_millis(1800))
.unwrap(),
on_ground: true,
in_water: false,
distance_travelled: 2.0,
};
let result =
MovementEventMapper::should_emit(&previous_state, Some((&SfxEvent::Run, &trigger_item)));
let result = MovementEventMapper::should_emit(
&previous_state,
Some((&SfxEvent::Run(BlockKind::Grass), &trigger_item)),
);
assert_eq!(result, true);
}
@ -93,6 +102,7 @@ fn maps_idle() {
time: Instant::now(),
on_ground: true,
in_water: false,
distance_travelled: 0.0,
},
Vec3::zero(),
BlockKind::Grass,
@ -114,12 +124,13 @@ fn maps_run_with_sufficient_velocity() {
time: Instant::now(),
on_ground: true,
in_water: false,
distance_travelled: 0.0,
},
Vec3::new(0.5, 0.8, 0.0),
BlockKind::Grass,
);
assert_eq!(result, SfxEvent::Run);
assert_eq!(result, SfxEvent::Run(BlockKind::Grass));
}
#[test]
@ -135,6 +146,7 @@ fn does_not_map_run_with_insufficient_velocity() {
time: Instant::now(),
on_ground: true,
in_water: false,
distance_travelled: 0.0,
},
Vec3::new(0.02, 0.0001, 0.0),
BlockKind::Grass,
@ -153,6 +165,7 @@ fn does_not_map_run_with_sufficient_velocity_but_not_on_ground() {
time: Instant::now(),
on_ground: false,
in_water: false,
distance_travelled: 0.0,
},
Vec3::new(0.5, 0.8, 0.0),
BlockKind::Grass,
@ -183,10 +196,11 @@ fn maps_roll() {
..Default::default()
},
&PreviousEntityState {
event: SfxEvent::Run,
event: SfxEvent::Run(BlockKind::Grass),
time: Instant::now(),
on_ground: true,
in_water: false,
distance_travelled: 0.0,
},
Vec3::new(0.5, 0.5, 0.0),
BlockKind::Grass,
@ -208,12 +222,13 @@ fn maps_land_on_ground_to_run() {
time: Instant::now(),
on_ground: false,
in_water: false,
distance_travelled: 0.0,
},
Vec3::zero(),
BlockKind::Grass,
);
assert_eq!(result, SfxEvent::Run);
assert_eq!(result, SfxEvent::Run(BlockKind::Grass));
}
#[test]
@ -226,6 +241,7 @@ fn maps_glider_open() {
time: Instant::now(),
on_ground: false,
in_water: false,
distance_travelled: 0.0,
},
Vec3::zero(),
BlockKind::Grass,
@ -244,6 +260,7 @@ fn maps_glide() {
time: Instant::now(),
on_ground: false,
in_water: false,
distance_travelled: 0.0,
},
Vec3::zero(),
BlockKind::Grass,
@ -262,6 +279,7 @@ fn maps_glider_close_when_closing_mid_flight() {
time: Instant::now(),
on_ground: false,
in_water: false,
distance_travelled: 0.0,
},
Vec3::zero(),
BlockKind::Grass,
@ -284,6 +302,7 @@ fn maps_glider_close_when_landing() {
time: Instant::now(),
on_ground: false,
in_water: false,
distance_travelled: 0.0,
},
Vec3::zero(),
BlockKind::Grass,
@ -303,7 +322,7 @@ fn maps_quadrupeds_running() {
BlockKind::Grass,
);
assert_eq!(result, SfxEvent::Run);
assert_eq!(result, SfxEvent::Run(BlockKind::Grass));
}
#[test]

View File

@ -22,7 +22,7 @@
//! The following snippet details some entries in the configuration and how they
//! map to the sound files:
//! ```ignore
//! Run: (
//! Run(Grass): ( // depends on underfoot block
//! files: [
//! "voxygen.audio.sfx.footsteps.stepgrass_1",
//! "voxygen.audio.sfx.footsteps.stepgrass_2",
@ -31,13 +31,13 @@
//! "voxygen.audio.sfx.footsteps.stepgrass_5",
//! "voxygen.audio.sfx.footsteps.stepgrass_6",
//! ],
//! threshold: 0.25, // wait 0.25s between plays
//! threshold: 1.6, // travelled distance before next play
//! ),
//! Wield(Sword): ( // depends on the player's weapon
//! files: [
//! "voxygen.audio.sfx.weapon.sword_out",
//! ],
//! threshold: 0.5,
//! threshold: 0.5, // wait 0.5s between plays
//! ),
//! ...
//! ```
@ -95,7 +95,7 @@ use common::{
object, Body, CharacterAbilityType, InventoryUpdateEvent,
},
outcome::Outcome,
terrain::TerrainChunk,
terrain::{BlockKind, TerrainChunk},
};
use common_sys::state::State;
use event_mapper::SfxEventMapper;
@ -147,10 +147,8 @@ pub enum SfxEvent {
RunningWater,
Idle,
Swim,
Run,
QuadRun,
SnowRun,
QuadSnowRun,
Run(BlockKind),
QuadRun(BlockKind),
Roll,
Sneak,
Climb,