Skip to content

Commit

Permalink
Implement basic sfx system
Browse files Browse the repository at this point in the history
  • Loading branch information
rukai committed Dec 30, 2020
1 parent 3652264 commit b98da2d
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 26 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Expand Up @@ -4,3 +4,4 @@
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
3 changes: 3 additions & 0 deletions assets/audio/sfx/Toriel/dash.ogg
Git LFS file not shown
3 changes: 3 additions & 0 deletions assets/audio/sfx/Toriel/die.wav
Git LFS file not shown
3 changes: 3 additions & 0 deletions assets/audio/sfx/Toriel/hit.wav
Git LFS file not shown
3 changes: 3 additions & 0 deletions assets/audio/sfx/Toriel/jump.ogg
Git LFS file not shown
3 changes: 3 additions & 0 deletions assets/audio/sfx/Toriel/land.ogg
Git LFS file not shown
3 changes: 3 additions & 0 deletions assets/audio/sfx/Toriel/walk.ogg
Git LFS file not shown
2 changes: 1 addition & 1 deletion canon_collision/src/app.rs
Expand Up @@ -254,7 +254,7 @@ fn run(mut cli_results: CLIResults, event_rx: Receiver<WindowEvent<'static>>, re
let reset_deadzones = game.check_reset_deadzones();
input.step(&game.tas, &ai_inputs, &mut netplay, reset_deadzones);

if let GameState::Quit (resume_menu_inner) = game.step(&mut config, &mut input, &os_input, command_line.block(), &netplay) {
if let GameState::Quit (resume_menu_inner) = game.step(&mut config, &mut input, &os_input, command_line.block(), &netplay, &mut audio) {
resume_menu = Some(resume_menu_inner)
}
if let Err(_) = render_tx.send(game.graphics_message(&config, &command_line)) {
Expand Down
25 changes: 21 additions & 4 deletions canon_collision/src/audio.rs
Expand Up @@ -5,12 +5,13 @@ use audiotags::Tag;
use kira::instance::{InstanceId, InstanceSettings, StopInstanceSettings};
use kira::manager::{AudioManager, AudioManagerSettings};
use kira::playable::PlayableSettings;
use kira::Value;
use rand::seq::IteratorRandom;
use rand;
use treeflection::{Node, NodeRunner, NodeToken};

use canon_collision_lib::assets::Assets;
use crate::entity::Entity;
use canon_collision_lib::entity_def::EntityDef;

pub struct Audio {
manager: AudioManager,
Expand All @@ -21,7 +22,7 @@ pub struct Audio {
impl Audio {
pub fn new(assets: Assets) -> Self {
let manager = AudioManager::new(AudioManagerSettings::default()).unwrap();
let path = assets.path().join("audio").join("music");
let path = assets.path().join("audio");

Audio {
manager,
Expand All @@ -30,8 +31,24 @@ impl Audio {
}
}

/// TODO: Load all sounds effects on startup
/// TODO: Random sfx selection from a pool?
/// TODO: How to handle rollback?
pub fn _play_sound_effect(&mut self, _entity: Entity, _sfx_name: &str) {
/// TODO: I could probably add a foo.txt for a foo.mp3 that contains a relative path to another mp3 file
pub fn play_sound_effect(&mut self, entity: &EntityDef, sfx_name: &str, volume: Value<f64>, pitch: Value<f64>) {
self.play_sound_effect_inner(entity, sfx_name, volume, pitch).unwrap();
}

pub fn play_sound_effect_inner(&mut self, entity: &EntityDef, sfx_name: &str, volume: Value<f64>, pitch: Value<f64>) -> Result<(), String> {
let folder = entity.name.replace(" ", "");
let path = self.path.join("sfx").join(&folder).join(sfx_name);

let playable_settings = PlayableSettings::default();
let new_sound = self.manager.load_sound(&path, playable_settings).map_err(|x| format!("Failed to load {:?}. {}", path, x))?;

let instance_settings = InstanceSettings::default().volume(volume).pitch(pitch);
self.manager.play(new_sound, instance_settings).map_err(|x| x.to_string())?;
Ok(())
}

/// Folders can contain music organized by stage/menu or fighter
Expand All @@ -50,7 +67,7 @@ impl Audio {

fn play_bgm_inner(&mut self, folder: &str) -> Result<BGMMetadata, String> {
let folder = folder.replace(" ", "");
let read_dir = fs::read_dir(self.path.join(&folder)).map_err(|x| x.to_string())?;
let read_dir = fs::read_dir(self.path.join("music").join(&folder)).map_err(|x| x.to_string())?;
let chosen_file = read_dir
.filter_map(|x| x.ok())
.filter(|x|
Expand Down
27 changes: 25 additions & 2 deletions canon_collision/src/entity/fighters/player.rs
Expand Up @@ -18,6 +18,7 @@ use canon_collision_lib::stage::{Stage, Surface};

use treeflection::KeyedContextVec;
use rand::Rng;
use kira::Value;

use std::f32;
use std::f32::consts::PI;
Expand Down Expand Up @@ -249,12 +250,14 @@ impl Player {
for col_result in col_results {
match col_result {
&CollisionResult::HitAtk { ref hitbox, ref point, .. } => {
context.audio.play_sound_effect(&context.entity_def, "hit.wav", Value::Random(0.15, 0.2), Value::Random(0.90, 1.1));
self.hit_particles(point.clone(), hitbox);
}
&CollisionResult::HitDef { ref hitbox, ref hurtbox, entity_atk_i } => {
set_action = self.launch(context, state, hitbox, hurtbox, entity_atk_i);
}
&CollisionResult::HitShieldAtk { ref hitbox, ref power_shield, entity_defend_i } => {
context.audio.play_sound_effect(&context.entity_def, "hit.wav", Value::Random(0.15, 0.2), Value::Random(0.90, 1.1));
let entity_def = &context.entities[entity_defend_i];
if let Some(player_def) = &entity_def.ty.get_player() {
if let &Some(ref power_shield) = power_shield {
Expand Down Expand Up @@ -459,7 +462,7 @@ impl Player {
PlayerAction::Crouch => self.crouch_action(context, state),
PlayerAction::Walk => self.walk_action(context, state),
PlayerAction::Dash => self.dash_action(context, state),
PlayerAction::Run => self.run_action(context),
PlayerAction::Run => self.run_action(context, state),
PlayerAction::RunEnd => self.run_end_action(context, state),
PlayerAction::TiltTurn => self.tilt_turn_action(context, state),
PlayerAction::SmashTurn => self.smash_turn_action(context, state),
Expand Down Expand Up @@ -665,6 +668,9 @@ impl Player {
}

fn jump_action(&mut self, context: &mut StepContext, state: &ActionState) -> Option<ActionResult> {
if state.frame == 0 {
context.audio.play_sound_effect(&context.entity_def, "jump.ogg", Value::Random(0.15, 0.2), Value::Random(0.90, 1.1));
}
None
.or_else(|| self.check_attacks_aerial(context))
.or_else(|| self.check_special_air(context))
Expand Down Expand Up @@ -918,13 +924,19 @@ impl Player {
}

fn attack_land_action(&mut self, context: &mut StepContext, state: &ActionState) -> Option<ActionResult> {
if state.frame == 0 {
context.audio.play_sound_effect(&context.entity_def, "land.ogg", Value::Random(0.05, 0.1), Value::Random(0.90, 1.1));
}
let frame = state.frame + self.land_frame_skip as i64 + 1;

self.land_action(context, state)
.or_else(|| ActionResult::set_frame(frame))
}

fn land_action(&mut self, context: &mut StepContext, state: &ActionState) -> Option<ActionResult> {
if state.frame == 0 {
context.audio.play_sound_effect(&context.entity_def, "land.ogg", Value::Random(0.05, 0.1), Value::Random(0.90, 1.1));
}
self.land_particles(context, state);

if state.interruptible(&context.entity_def) {
Expand Down Expand Up @@ -975,6 +987,10 @@ impl Player {
}

fn walk_action(&mut self, context: &mut StepContext, state: &ActionState) -> Option<ActionResult> {
if state.frame_no_restart % 20 == 0 {
context.audio.play_sound_effect(&context.entity_def, "walk.ogg", Value::Random(0.01, 0.03), Value::Random(0.95, 1.05));
}

if context.input[0].stick_x == 0.0 {
ActionResult::set_action(PlayerAction::Idle)
} else {
Expand Down Expand Up @@ -1009,6 +1025,9 @@ impl Player {
}

fn dash_action(&mut self, context: &mut StepContext, state: &ActionState) -> Option<ActionResult> {
if state.frame == 0 {
context.audio.play_sound_effect(&context.entity_def, "dash.ogg", Value::Random(0.15, 0.2), Value::Random(0.95, 1.05));
}
self.dash_particles(context, state);
if state.frame == 1 {
self.body.x_vel = self.relative_f(context.entity_def.dash_init_vel);
Expand Down Expand Up @@ -1063,7 +1082,10 @@ impl Player {
.or_else(|| self.check_smash_turn(context))
}

fn run_action(&mut self, context: &mut StepContext) -> Option<ActionResult> {
fn run_action(&mut self, context: &mut StepContext, state: &ActionState) -> Option<ActionResult> {
if state.frame_no_restart % 17 == 0 {
context.audio.play_sound_effect(&context.entity_def, "walk.ogg", Value::Random(0.03, 0.1), Value::Random(0.95, 1.05));
}
None
.or_else(|| self.check_jump(context))
.or_else(|| self.check_shield(context))
Expand Down Expand Up @@ -2141,6 +2163,7 @@ impl Player {
}

fn die(&mut self, context: &mut StepContext, game_frame: usize, goal: Goal) -> Option<ActionResult> {
context.audio.play_sound_effect(&context.entity_def, "die.wav", Value::Random(0.30, 0.4), Value::Random(0.90, 1.1));
self.body = if context.stage.respawn_points.len() == 0 {
Body::new(
Location::Airbourne { x: 0.0, y: 0.0 },
Expand Down
2 changes: 2 additions & 0 deletions canon_collision/src/entity/mod.rs
Expand Up @@ -14,6 +14,7 @@ use fighters::Fighter;
use components::action_state::{ActionState, Hitlag};
use components::body::Body;

use crate::audio::Audio;
use crate::collision::collision_box::CollisionResult;
use crate::graphics;
use crate::particle::Particle;
Expand Down Expand Up @@ -660,6 +661,7 @@ pub struct StepContext<'a> {
pub rng: &'a mut ChaChaRng,
pub new_entities: &'a mut Vec<Entity>,
pub messages: &'a mut Vec<Message>,
pub audio: &'a mut Audio,
pub delete_self: bool,
}

Expand Down
43 changes: 24 additions & 19 deletions canon_collision/src/game.rs
Expand Up @@ -170,7 +170,7 @@ impl Game {
}
}

pub fn step(&mut self, config: &mut Config, input: &mut Input, os_input: &WinitInputHelper, os_input_blocked: bool, netplay: &Netplay) -> GameState {
pub fn step(&mut self, config: &mut Config, input: &mut Input, os_input: &WinitInputHelper, os_input_blocked: bool, netplay: &Netplay, audio: &mut Audio) -> GameState {
if os_input.held_alt() && os_input.key_pressed(VirtualKeyCode::Return) {
config.fullscreen = !config.fullscreen;
config.save();
Expand All @@ -184,12 +184,12 @@ impl Game {
{
let state = self.state.clone();
match state {
GameState::Local => self.step_local(input, netplay),
GameState::Netplay => self.step_netplay(input, netplay),
GameState::Local => self.step_local(input, netplay, audio),
GameState::Netplay => self.step_netplay(input, netplay, audio),
GameState::ReplayForwardsFromHistory => self.step_replay_forwards_from_history(input),
GameState::ReplayForwardsFromInput => self.step_replay_forwards_from_input(input, netplay),
GameState::ReplayForwardsFromInput => self.step_replay_forwards_from_input(input, netplay, audio),
GameState::ReplayBackwards => self.step_replay_backwards(input),
GameState::StepThenPause => { self.step_local(input, netplay); self.state = GameState::Paused; }
GameState::StepThenPause => { self.step_local(input, netplay, audio); self.state = GameState::Paused; }
GameState::StepForwardThenPause => { self.step_replay_forwards_from_history(input); self.state = GameState::Paused; }
GameState::StepBackwardThenPause => { self.step_replay_backwards(input); self.state = GameState::Paused; }
GameState::Paused => self.step_pause(input),
Expand All @@ -202,7 +202,7 @@ impl Game {
GameState::ReplayForwardsFromHistory => self.step_replay_forwards_os_input(os_input),
GameState::ReplayForwardsFromInput => self.step_replay_forwards_os_input(os_input),
GameState::ReplayBackwards => self.step_replay_backwards_os_input(os_input),
GameState::Paused => self.step_pause_os_input(input, os_input, netplay),
GameState::Paused => self.step_pause_os_input(input, os_input, netplay, audio),
GameState::Quit (_) => unreachable!(),

GameState::Netplay | GameState::StepThenPause |
Expand Down Expand Up @@ -299,7 +299,7 @@ impl Game {
}
}

fn step_local(&mut self, input: &mut Input, netplay: &Netplay) {
fn step_local(&mut self, input: &mut Input, netplay: &Netplay, audio: &mut Audio) {
self.entity_history.push(self.entities.clone());
self.stage_history.push(self.stage.clone());
self.current_frame += 1;
Expand All @@ -315,7 +315,7 @@ impl Game {
// run game loop
input.game_update(self.current_frame);
let player_inputs = &input.players(self.current_frame, netplay);
self.step_game(input, player_inputs);
self.step_game(input, player_inputs, audio);

if let Some(max_history_frames) = self.max_history_frames {
let extra_frames = self.entity_history.len().saturating_sub(max_history_frames);
Expand All @@ -338,7 +338,7 @@ impl Game {
}
}

fn step_netplay(&mut self, input: &mut Input, netplay: &Netplay) {
fn step_netplay(&mut self, input: &mut Input, netplay: &Netplay, audio: &mut Audio) {
if !netplay.skip_frame() {
self.current_frame += 1;

Expand All @@ -356,7 +356,7 @@ impl Game {

for frame in start..end {
let player_inputs = &input.players(frame, netplay);
self.step_game(input, player_inputs);
self.step_game(input, player_inputs, audio);

self.entity_history.push(self.entities.clone());
self.stage_history.push(self.stage.clone());
Expand All @@ -373,13 +373,13 @@ impl Game {
}
}

fn step_pause_os_input(&mut self, input: &mut Input, os_input: &WinitInputHelper, netplay: &Netplay) {
fn step_pause_os_input(&mut self, input: &mut Input, os_input: &WinitInputHelper, netplay: &Netplay, audio: &mut Audio) {
// game flow control
if os_input.key_pressed(VirtualKeyCode::J) {
self.step_replay_backwards(input);
}
else if os_input.held_shift() && os_input.key_pressed(VirtualKeyCode::K) {
self.step_replay_forwards_from_input(input, netplay);
self.step_replay_forwards_from_input(input, netplay, audio);
}
else if os_input.key_pressed(VirtualKeyCode::K) {
self.step_replay_forwards_from_history(input);
Expand All @@ -394,7 +394,7 @@ impl Game {
self.state = GameState::ReplayForwardsFromHistory;
}
else if os_input.key_pressed(VirtualKeyCode::Space) {
self.step_local(input, netplay);
self.step_local(input, netplay, audio);
}
else if os_input.key_pressed(VirtualKeyCode::U) {
self.saved_frame = self.current_frame;
Expand All @@ -407,11 +407,11 @@ impl Game {
}

if self.camera.dev_mode() {
self.step_editor(input, os_input, netplay);
self.step_editor(input, os_input, netplay, audio);
}
}

fn step_editor(&mut self, input: &mut Input, os_input: &WinitInputHelper, netplay: &Netplay) {
fn step_editor(&mut self, input: &mut Input, os_input: &WinitInputHelper, netplay: &Netplay, audio: &mut Audio) {
// set current edit state
if os_input.key_pressed(VirtualKeyCode::Key0) {
self.edit = Edit::Stage;
Expand Down Expand Up @@ -584,7 +584,7 @@ impl Game {
}
// We want to step just the entities current frame to simplify the animation work flow
// However we need to do a proper full step so that the history doesn't get mucked up.
self.step_local(input, netplay);
self.step_local(input, netplay, audio);
}
// delete frame
if os_input.key_pressed(VirtualKeyCode::N) {
Expand Down Expand Up @@ -1022,11 +1022,11 @@ impl Game {
}

/// next frame is advanced by using the input history on the current frame
fn step_replay_forwards_from_input(&mut self, input: &mut Input, netplay: &Netplay) {
fn step_replay_forwards_from_input(&mut self, input: &mut Input, netplay: &Netplay, audio: &mut Audio) {
if self.current_frame <= input.last_frame() {
self.current_frame += 1;
let player_inputs = &input.players(self.current_frame, netplay);
self.step_game(input, player_inputs);
self.step_game(input, player_inputs, audio);

self.update_frame();
}
Expand Down Expand Up @@ -1116,7 +1116,7 @@ impl Game {
seed
}

fn step_game(&mut self, input: &Input, player_inputs: &[PlayerInput]) {
fn step_game(&mut self, input: &Input, player_inputs: &[PlayerInput], audio: &mut Audio) {
let default_input = PlayerInput::empty();
{
let mut rng = ChaChaRng::from_seed(self.get_seed());
Expand Down Expand Up @@ -1146,6 +1146,7 @@ impl Game {
new_entities: &mut new_entities,
messages: &mut messages,
delete_self: false,
audio,
input,
};
entity.action_hitlag_step(&mut context);
Expand Down Expand Up @@ -1177,6 +1178,7 @@ impl Game {
new_entities: &mut new_entities,
messages: &mut messages,
delete_self: false,
audio,
input,
};
entity.item_grab(&mut context, hit_key, hit_id);
Expand Down Expand Up @@ -1207,6 +1209,7 @@ impl Game {
new_entities: &mut new_entities,
messages: &mut messages,
delete_self: false,
audio,
input,
};
entity.physics_step(&mut context, self.current_frame, self.rules.goal.clone());
Expand Down Expand Up @@ -1244,6 +1247,7 @@ impl Game {
new_entities: &mut new_entities,
messages: &mut messages,
delete_self: false,
audio,
input,
};
entity.step_collision(&mut context, &collision_results[key]);
Expand All @@ -1269,6 +1273,7 @@ impl Game {
new_entities: &mut new_entities,
messages: &mut vec!(),
delete_self: false,
audio,
input,
};
entity.process_message(message, &mut context);
Expand Down

0 comments on commit b98da2d

Please sign in to comment.