Merge branch 'james/footsteps-sfx' into 'master'

Footstep SFX and repetitions based on distance instead of time

See merge request veloren/veloren!1705
This commit is contained in:
Justin Shipsey 2021-01-20 05:05:23 +00:00
commit 49cc3f3fa7
14 changed files with 113 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: [

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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,