Add button sounds

This commit is contained in:
Evan Pratten 2021-10-03 14:07:23 -04:00
parent d640e48996
commit dc284adbd8
13 changed files with 145 additions and 60 deletions

Binary file not shown.

View File

@ -1,9 +1,10 @@
use std::{cell::RefCell, sync::mpsc::Sender};
use std::{cell::RefCell, collections::HashMap, sync::mpsc::Sender};
use chrono::{DateTime, Duration, Utc};
use discord_sdk::activity::ActivityBuilder;
use raylib::audio::Sound;
use crate::{progress::ProgressData, utilities::non_ref_raylib::HackedRaylibHandle, GameConfig};
use crate::{GameConfig, progress::ProgressData, utilities::{audio_player::AudioPlayer, non_ref_raylib::HackedRaylibHandle}};
#[derive(Debug)]
pub enum ControlFlag {
@ -11,12 +12,15 @@ pub enum ControlFlag {
SwitchLevel(usize),
UpdateLevelStart(DateTime<Utc>),
SaveProgress,
MaybeUpdateHighScore(usize, Duration)
MaybeUpdateHighScore(usize, Duration),
SoundTrigger(String)
}
#[derive(Debug)]
pub struct GameContext {
pub renderer: RefCell<HackedRaylibHandle>,
pub audio: AudioPlayer,
pub sounds: HashMap<String, Sound>,
pub config: GameConfig,
pub player_progress: ProgressData,
pub current_level: usize,

View File

@ -70,7 +70,7 @@
)]
#![clippy::msrv = "1.57.0"]
use std::{borrow::BorrowMut, cell::RefCell, sync::mpsc::TryRecvError};
use std::{borrow::BorrowMut, cell::RefCell, collections::HashMap, sync::mpsc::TryRecvError};
use chrono::Utc;
use discord_sdk::activity::ActivityBuilder;
@ -78,10 +78,21 @@ use raylib::prelude::*;
use tracing::{error, info, warn};
use utilities::discord::DiscordConfig;
use crate::{context::GameContext, discord_rpc::{maybe_set_discord_presence, try_connect_to_local_discord}, progress::ProgressData, scenes::{build_screen_state_machine, Scenes}, utilities::{audio_player::AudioPlayer, datastore::load_music_from_internal_data, game_config::FinalShaderConfig, shaders::{
use crate::{
context::GameContext,
discord_rpc::{maybe_set_discord_presence, try_connect_to_local_discord},
progress::ProgressData,
scenes::{build_screen_state_machine, Scenes},
utilities::{
audio_player::AudioPlayer,
datastore::{load_music_from_internal_data, load_sound_from_internal_data},
game_config::FinalShaderConfig,
shaders::{
shader::ShaderWrapper,
util::{dynamic_screen_texture::DynScreenTexture, render_texture::render_to_texture},
}}};
},
},
};
#[macro_use]
extern crate thiserror;
@ -163,10 +174,23 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box<dyn std:
rl.set_target_fps(60);
raylib_thread = thread;
// Init the audio subsystem
let mut audio_system = AudioPlayer::new(RaylibAudio::init_audio_device());
audio_system.set_master_volume(0.4);
// Load any other sounds
let mut sounds = HashMap::new();
sounds.insert(
"button-press".to_string(),
load_sound_from_internal_data("audio/button-press.mp3").unwrap(),
);
// Build the game context
context = Box::new(GameContext {
renderer: RefCell::new(rl.into()),
config: game_config.clone(),
audio: audio_system,
sounds,
current_level: 0,
player_progress: save_file,
level_start_time: Utc::now(),
@ -175,15 +199,16 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box<dyn std:
});
}
// Init the audio subsystem
let mut audio_system = AudioPlayer::new(RaylibAudio::init_audio_device());
audio_system.set_master_volume(0.4);
// Load the game's main song
let mut main_song = load_music_from_internal_data(&mut context.renderer.borrow_mut(), &raylib_thread, "audio/soundtrack.mp3").unwrap();
let mut main_song = load_music_from_internal_data(
&mut context.renderer.borrow_mut(),
&raylib_thread,
"audio/soundtrack.mp3",
)
.unwrap();
// Start the song
audio_system.play_music_stream(&mut main_song);
context.audio.play_music_stream(&mut main_song);
// Get the main state machine
info!("Setting up the scene management state machine");
@ -225,9 +250,9 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box<dyn std:
puffin::GlobalProfiler::lock().new_frame();
// Update the audio
audio_system.update_music_stream(&mut main_song);
if !audio_system.is_music_playing(&main_song) {
audio_system.play_music_stream(&mut main_song);
context.audio.update_music_stream(&mut main_song);
if !context.audio.is_music_playing(&main_song) {
context.audio.play_music_stream(&mut main_song);
}
// Update the GPU texture that we draw to. This handles screen resizing and some other stuff
@ -348,6 +373,11 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box<dyn std:
.player_progress
.maybe_write_new_time(level, &time);
}
context::ControlFlag::SoundTrigger(name) => {
context.audio.play_sound(
context.sounds.get(&name).unwrap(),
);
}
}
}
}

View File

@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets};
use pkg_version::pkg_version_major;
use raylib::prelude::*;
use crate::{
context::GameContext,
utilities::{
use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{
datastore::{load_texture_from_internal_data, ResourceLoadError},
game_version::get_version_string,
math::interpolate_exp,
non_ref_raylib::HackedRaylibHandle,
render_layer::ScreenSpaceRender,
},
GameConfig,
};
}};
use super::{Scenes, ScreenError};
use tracing::{debug, error, info, trace};
@ -69,6 +65,10 @@ impl Action<Scenes, ScreenError, GameContext> for DeathScreen {
self.timer_value = format!("{:02}:{:02}", elapsed.num_minutes(), elapsed.num_seconds() % 60);
if self.is_retry_pressed {
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
Ok(ActionFlag::SwitchState(Scenes::InGameScene))
} else {
Ok(ActionFlag::Continue)

View File

@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets};
use pkg_version::pkg_version_major;
use raylib::prelude::*;
use crate::{
context::GameContext,
utilities::{
use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{
datastore::{load_texture_from_internal_data, ResourceLoadError},
game_version::get_version_string,
math::interpolate_exp,
non_ref_raylib::HackedRaylibHandle,
render_layer::ScreenSpaceRender,
},
GameConfig,
};
}};
use super::{Scenes, ScreenError};
use tracing::{debug, error, info, trace};
@ -66,6 +62,10 @@ impl Action<Scenes, ScreenError, GameContext> for HowToPlayScreen {
self.render_screen_space(&mut context.renderer.borrow_mut(), &context.config);
if self.is_btm_pressed {
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
Ok(ActionFlag::SwitchState(Scenes::MainMenuScreen))
} else {
Ok(ActionFlag::Continue)

View File

@ -71,12 +71,28 @@ impl Action<Scenes, ScreenError, GameContext> for MainMenuScreen {
self.render_screen_space(&mut context.renderer.borrow_mut(), &context.config);
if self.is_start_pressed {
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
Ok(ActionFlag::SwitchState(Scenes::InGameScene))
} else if self.is_htp_pressed {
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
Ok(ActionFlag::SwitchState(Scenes::HowToPlayScreen))
} else if self.is_options_pressed {
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
Ok(ActionFlag::SwitchState(Scenes::OptionsScreen))
} else if self.is_quit_pressed {
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
context.flag_send.send(Some(ControlFlag::Quit)).unwrap();
Ok(ActionFlag::Continue)
} else {

View File

@ -6,13 +6,7 @@ use self::{
death_screen::DeathScreen, win_screen::WinScreen,
next_level_screen::NextLevelScreen
};
use crate::{
context::GameContext,
utilities::{
datastore::{load_texture_from_internal_data, ResourceLoadError},
non_ref_raylib::HackedRaylibHandle,
},
};
use crate::{context::GameContext, utilities::{datastore::{ResourceLoadError, load_music_from_internal_data, load_sound_from_internal_data, load_texture_from_internal_data}, non_ref_raylib::HackedRaylibHandle}};
use dirty_fsm::StateMachine;
use raylib::{texture::Texture2D, RaylibThread};

View File

@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets};
use pkg_version::pkg_version_major;
use raylib::prelude::*;
use crate::{
context::GameContext,
utilities::{
use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{
datastore::{load_texture_from_internal_data, ResourceLoadError},
game_version::get_version_string,
math::interpolate_exp,
non_ref_raylib::HackedRaylibHandle,
render_layer::ScreenSpaceRender,
},
GameConfig,
};
}};
use super::{Scenes, ScreenError};
use tracing::{debug, error, info, trace};
@ -87,6 +83,10 @@ impl Action<Scenes, ScreenError, GameContext> for NextLevelScreen {
);
if self.is_next_pressed {
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
Ok(ActionFlag::SwitchState(Scenes::InGameScene))
} else {
Ok(ActionFlag::Continue)

View File

@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets};
use pkg_version::pkg_version_major;
use raylib::prelude::*;
use crate::{
context::GameContext,
utilities::{
use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{
datastore::{load_texture_from_internal_data, ResourceLoadError},
game_version::get_version_string,
math::interpolate_exp,
non_ref_raylib::HackedRaylibHandle,
render_layer::ScreenSpaceRender,
},
GameConfig,
};
}};
use super::{Scenes, ScreenError};
use tracing::{debug, error, info, trace};
@ -69,6 +65,10 @@ impl Action<Scenes, ScreenError, GameContext> for OptionsScreen {
self.render_screen_space(&mut context.renderer.borrow_mut(), &context.config);
if self.is_btm_pressed {
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
Ok(ActionFlag::SwitchState(Scenes::MainMenuScreen))
} else {
Ok(ActionFlag::Continue)

View File

@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets};
use pkg_version::pkg_version_major;
use raylib::prelude::*;
use crate::{
context::GameContext,
utilities::{
use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{
datastore::{load_texture_from_internal_data, ResourceLoadError},
game_version::get_version_string,
math::interpolate_exp,
non_ref_raylib::HackedRaylibHandle,
render_layer::ScreenSpaceRender,
},
GameConfig,
};
}};
use super::{Scenes, ScreenError};
use tracing::{debug, error, info, trace};
@ -82,6 +78,10 @@ impl Action<Scenes, ScreenError, GameContext> for PauseScreen {
&& Rectangle::new(centered_x_paused, centered_y_paused, 435.0, 80.0)
.check_collision_point_rec(mouse_position)
{
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
return Ok(ActionFlag::SwitchState(Scenes::InGameScene));
}
//For Menu
@ -89,6 +89,10 @@ impl Action<Scenes, ScreenError, GameContext> for PauseScreen {
&& Rectangle::new(centered_x_menu, centered_y_menu, 200.0, 50.0)
.check_collision_point_rec(mouse_position)
{
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
return Ok(ActionFlag::SwitchState(Scenes::MainMenuScreen));
}

View File

@ -6,17 +6,13 @@ use discord_sdk::activity::{ActivityBuilder, Assets};
use pkg_version::pkg_version_major;
use raylib::prelude::*;
use crate::{
context::GameContext,
utilities::{
use crate::{GameConfig, context::{ControlFlag, GameContext}, utilities::{
datastore::{load_texture_from_internal_data, ResourceLoadError},
game_version::get_version_string,
math::interpolate_exp,
non_ref_raylib::HackedRaylibHandle,
render_layer::ScreenSpaceRender,
},
GameConfig,
};
}};
use super::{Scenes, ScreenError};
use tracing::{debug, error, info, trace};
@ -69,6 +65,10 @@ impl Action<Scenes, ScreenError, GameContext> for WinScreen {
self.counter += 1;
if self.is_menu_pressed {
context
.flag_send
.send(Some(ControlFlag::SoundTrigger("button-press".to_string())))
.unwrap();
Ok(ActionFlag::SwitchState(Scenes::MainMenuScreen))
} else {
Ok(ActionFlag::Continue)

View File

@ -1,6 +1,7 @@
use raylib::audio::RaylibAudio;
/// A thin wrapper around `raylib::core::audio::RaylibAudio` that keeps track of the volume of its audio channels.
#[derive(Debug)]
pub struct AudioPlayer {
backend: RaylibAudio,

View File

@ -1,6 +1,10 @@
use std::{io::Write, path::Path};
use raylib::{RaylibHandle, RaylibThread, audio::Music, texture::Texture2D};
use raylib::{
audio::{Music, Sound},
texture::Texture2D,
RaylibHandle, RaylibThread,
};
use tempfile::{tempdir, NamedTempFile};
use tracing::debug;
@ -68,7 +72,6 @@ pub fn load_texture_from_internal_data(
Ok(texture)
}
pub fn load_music_from_internal_data(
raylib_handle: &mut RaylibHandle,
thread: &RaylibThread,
@ -103,3 +106,36 @@ pub fn load_music_from_internal_data(
Ok(texture)
}
pub fn load_sound_from_internal_data(
path: &str,
) -> Result<Sound, ResourceLoadError> {
// Create a temp file path to work with
let temp_dir = tempdir()?;
debug!(
"Created temporary directory for passing embedded data to Raylib: {}",
temp_dir.path().display()
);
let tmp_path = temp_dir.path().join(Path::new(path).file_name().unwrap());
// Unpack the raw sound data to a real file on the local filesystem so raylib will read it correctly
std::fs::write(
&tmp_path,
&StaticGameData::get(path)
.ok_or(ResourceLoadError::AssetNotFound(path.to_string()))?
.data,
)?;
// Call through via FFI to re-load the file
let texture =
Sound::load_sound(tmp_path.to_str().unwrap()).map_err(ResourceLoadError::Generic)?;
// Close the file
debug!(
"Dropping temporary directory: {}",
temp_dir.path().display()
);
temp_dir.close()?;
Ok(texture)
}