We have music!

This commit is contained in:
Evan Pratten 2022-04-02 19:37:07 -04:00
parent 928dea652e
commit ae4085c6ca
7 changed files with 157 additions and 64 deletions

Binary file not shown.

View File

@ -24,4 +24,4 @@ pub use json::{InternalJsonLoadError, load_json_structure};
mod sprite_types;
pub use sprite_types::{KnownSpriteType, load_known_sprite_types};
mod texture;
pub use texture::{load_texture_from_internal_data, ResourceLoadError};
pub use texture::{load_texture_from_internal_data, ResourceLoadError, load_music_from_internal_data, load_sound_from_internal_data};

View File

@ -4,7 +4,11 @@
use std::path::Path;
use raylib::{texture::Texture2D, RaylibHandle, RaylibThread};
use raylib::{
audio::{Music, RaylibAudio, Sound},
texture::Texture2D,
RaylibHandle, RaylibThread,
};
use tempfile::tempdir;
use crate::asset_manager::InternalData;
@ -62,3 +66,70 @@ pub fn load_texture_from_internal_data(
Ok(texture)
}
pub fn load_music_from_internal_data(
thread: &RaylibThread,
path: &str,
) -> Result<Music, 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,
&InternalData::get(path)
.ok_or(ResourceLoadError::AssetNotFound(path.to_string()))?
.data,
)?;
// Call through via FFI to re-load the file
let texture = Music::load_music_stream(thread, 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)
}
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,
&InternalData::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)
}

View File

@ -111,6 +111,11 @@ pub async fn handle_graphics_blocking<ConfigBuilder>(
constants,
)
.await;
// Handle exiting the game
if render_delegate.needs_exit {
break;
}
}
_ => backend_sm = RenderBackendStates::sm_failed(),
};

View File

@ -24,7 +24,7 @@ pub enum MenuStateSignal {
#[derive(Debug)]
pub struct MainMenu {
has_updated_discord_rpc: bool,
pub has_updated_discord_rpc: bool,
}
impl MainMenu {

View File

@ -23,6 +23,8 @@ mod test_fox;
/// This is a struct to allow for stateful data (like sub-screens) to be set up
pub struct SceneRenderDelegate {
menu_control_signal: MenuStateSignal,
pub needs_exit: bool,
audio_subsystem: RaylibAudio,
/* Scenes */
scene_test_fox: TestFoxScene,
scene_playable: PlayableScene,
@ -36,7 +38,9 @@ impl SceneRenderDelegate {
rl_thread: &RaylibThread,
constants: &ProjectConstants,
) -> Self {
// TODO: Stick any init code you want here.
// Set up audio
let audio_subsystem = RaylibAudio::init_audio_device();
audio_subsystem.set_master_volume(0.4);
// Init some scenes
let scene_test_fox = TestFoxScene::new(raylib, rl_thread);
@ -45,6 +49,8 @@ impl SceneRenderDelegate {
Self {
menu_control_signal: MenuStateSignal::DoMainMenu,
needs_exit: false,
audio_subsystem,
scene_test_fox,
scene_playable,
scene_main_menu,
@ -62,22 +68,29 @@ impl SceneRenderDelegate {
global_resources: &GlobalResources,
constants: &ProjectConstants,
) {
// Render the main menu if in it, otherwise, render the game
match self.menu_control_signal {
MenuStateSignal::StartGame => {
self.scene_playable
.render_frame(raylib, rl_thread, &discord, global_resources, constants)
.await;
self.scene_playable
.update_physics(raylib, constants)
.render_frame(raylib, rl_thread, &discord, global_resources, constants, &mut self.audio_subsystem)
.await;
self.scene_playable.update_physics(raylib, constants).await;
// Clear the menu system discord status
self.scene_main_menu.has_updated_discord_rpc = false;
}
MenuStateSignal::QuitGame => {
self.needs_exit = true;
}
MenuStateSignal::QuitGame => unimplemented!(),
MenuStateSignal::DoMainMenu => {
self.menu_control_signal = self
.scene_main_menu
.render_main_menu_frame(raylib, rl_thread, discord, global_resources, constants)
.await
.await;
// Clear the ingame discord status
self.scene_playable.has_updated_discord_rpc = false;
}
MenuStateSignal::DoOptions => {
self.menu_control_signal = self

View File

@ -5,6 +5,7 @@ use raylib::prelude::*;
use std::time::SystemTime;
use crate::{
asset_manager::{load_music_from_internal_data, load_sound_from_internal_data},
discord::{DiscordChannel, DiscordRpcSignal},
global_resource_package::GlobalResources,
model::player::Player,
@ -14,40 +15,42 @@ use crate::{
#[derive(Debug)]
pub struct PlayableScene {
has_updated_discord_rpc: bool,
pub has_updated_discord_rpc: bool,
player: Player,
world_map: MapRenderer,
camera: raylib::camera::Camera2D,
last_update: SystemTime,
game_soundtrack: Music,
}
impl PlayableScene {
/// Construct a new `PlayableScene`
pub fn new(
raylib_handle: &mut raylib::RaylibHandle,
thread: & raylib::RaylibThread,
thread: &raylib::RaylibThread,
constants: &ProjectConstants,
) -> Self {
let map_renderer = MapRenderer::new("map_gameMap.tmx", raylib_handle, thread).unwrap();
// Load the game music
let game_soundtrack =
load_music_from_internal_data(thread, "assets/audio/gameSoundtrack.mp3").unwrap();
Self {
has_updated_discord_rpc: false,
player: Player::new(na::Vector2::new(10.0, 10.0)),
world_map: map_renderer,
camera: raylib::camera::Camera2D {
target: raylib::math::Vector2 {
x: 0.0,
y: 0.0,
},
offset: raylib::math::Vector2 {
target: raylib::math::Vector2 { x: 0.0, y: 0.0 },
offset: raylib::math::Vector2 {
x: (constants.base_window_size.0 as f32 / 2.0),
y: (constants.base_window_size.1 as f32 / 2.0)
y: (constants.base_window_size.1 as f32 / 2.0),
},
rotation: 0.0,
zoom: 1.0
zoom: 1.0,
},
last_update: SystemTime::UNIX_EPOCH
last_update: SystemTime::UNIX_EPOCH,
game_soundtrack,
}
}
@ -59,6 +62,7 @@ impl PlayableScene {
discord: &DiscordChannel,
global_resources: &GlobalResources,
constants: &ProjectConstants,
audio_subsystem: &mut RaylibAudio,
) {
// Handle updating discord RPC
if !self.has_updated_discord_rpc {
@ -76,96 +80,101 @@ impl PlayableScene {
self.has_updated_discord_rpc = true;
}
// Ensure the game soundtrack is playing
if !audio_subsystem.is_music_playing(&self.game_soundtrack) {
debug!("Playing game soundtrack");
audio_subsystem.play_music_stream(&mut self.game_soundtrack);
} else {
audio_subsystem.update_music_stream(&mut self.game_soundtrack);
}
// Get a drawing handle
let mut draw = raylib.begin_drawing(rl_thread);
// Clear the screen
draw.clear_background(Color::WHITE);
self.draw_world(&mut draw, constants);
self.draw_ui(&mut draw, constants);
}
pub fn draw_world(
&mut self,
draw: &mut RaylibDrawHandle,
constants: &ProjectConstants,
) {
pub fn draw_world(&mut self, draw: &mut RaylibDrawHandle, constants: &ProjectConstants) {
// Begin camera mode
let mut ctx2d = draw.begin_mode2D(self.camera);
// Render the map
self.world_map.render_map(&mut ctx2d, &self.camera, true);
let player_size = (constants.tile_size as f32 * constants.player.start_size * self.player.size) as i32;
let player_size =
(constants.tile_size as f32 * constants.player.start_size * self.player.size) as i32;
ctx2d.draw_rectangle(
self.player.position[0] as i32 - player_size / 2,
self.player.position[0] as i32 - player_size / 2,
self.player.position[1] as i32 * -1 - player_size / 2,
player_size,
player_size,
Color::LIGHTBLUE
Color::LIGHTBLUE,
);
}
pub fn draw_ui(
&mut self,
draw: &mut RaylibDrawHandle,
constants: &ProjectConstants,
) {
draw.draw_rectangle(
draw.get_screen_width() / 2 - 225, 0,
450, 40,
Color::WHITE
);
pub fn draw_ui(&mut self, draw: &mut RaylibDrawHandle, constants: &ProjectConstants) {
draw.draw_rectangle(draw.get_screen_width() / 2 - 225, 0, 450, 40, Color::WHITE);
draw.draw_text(
"Unregistered HyperCam 2",
draw.get_screen_width() / 2 - 215, 0,
32, Color::BLACK
"Unregistered HyperCam 2",
draw.get_screen_width() / 2 - 215,
0,
32,
Color::BLACK,
);
}
// Physics
pub async fn update_physics(
&mut self,
raylib: & raylib::RaylibHandle,
raylib: &raylib::RaylibHandle,
constants: &ProjectConstants,
) {
// Get time since last physics update
let time = SystemTime::now();
let elapsed = time.duration_since(self.last_update).expect("Time Appears to Have Moved Backwards!");
let elapsed = time
.duration_since(self.last_update)
.expect("Time Appears to Have Moved Backwards!");
self.last_update = time;
let delta_time = elapsed.as_millis() as f32 / 1000.0; // Physics will be scaled by this value
let player = &mut self.player;
// Get input direction components
let h_axis = raylib.is_key_down(KeyboardKey::KEY_D) as i8 - raylib.is_key_down(KeyboardKey::KEY_A) as i8;
let v_axis = raylib.is_key_down(KeyboardKey::KEY_W) as i8 - raylib.is_key_down(KeyboardKey::KEY_S) as i8;
let h_axis = raylib.is_key_down(KeyboardKey::KEY_D) as i8
- raylib.is_key_down(KeyboardKey::KEY_A) as i8;
let v_axis = raylib.is_key_down(KeyboardKey::KEY_W) as i8
- raylib.is_key_down(KeyboardKey::KEY_S) as i8;
if h_axis != 0 || v_axis != 0 {
// Normalize input and accelerate in desired direction
let direction = na::Vector2::new(h_axis as f32, v_axis as f32).normalize();
player.velocity += &direction.xy()
player.velocity += &direction.xy()
* constants.player.acceleration as f32
* constants.tile_size as f32
* constants.tile_size as f32
* delta_time;
}
if player.velocity.magnitude() != 0.0 {
player.velocity -= player.velocity.normalize()
* constants.player.deceleration as f32
* constants.tile_size as f32
player.velocity -= player.velocity.normalize()
* constants.player.deceleration as f32
* constants.tile_size as f32
* delta_time;
if player.velocity.magnitude() < 1.0 {
player.velocity.set_magnitude(0.0);
}
}
if ((constants.player.max_velocity * constants.tile_size) as f32)
< player.velocity.magnitude() {
player.velocity.set_magnitude((constants.player.max_velocity * constants.tile_size) as f32);
if ((constants.player.max_velocity * constants.tile_size) as f32)
< player.velocity.magnitude()
{
player
.velocity
.set_magnitude((constants.player.max_velocity * constants.tile_size) as f32);
}
player.position += &player.velocity * delta_time;
@ -173,15 +182,10 @@ impl PlayableScene {
self.update_camera(raylib);
}
pub fn update_camera(
&mut self,
raylib: & raylib::RaylibHandle,
) {
pub fn update_camera(&mut self, raylib: &raylib::RaylibHandle) {
self.camera.target = self.player.position.into();
self.camera.target.y *= -1.0;
self.camera.offset.x = raylib.get_screen_width() as f32 / 2.0;
self.camera.offset.y = raylib.get_screen_height() as f32 / 2.0;
}
}