We have music!
This commit is contained in:
parent
928dea652e
commit
ae4085c6ca
BIN
game/dist/assets/audio/gameSoundtrack.mp3
vendored
Normal file
BIN
game/dist/assets/audio/gameSoundtrack.mp3
vendored
Normal file
Binary file not shown.
@ -24,4 +24,4 @@ pub use json::{InternalJsonLoadError, load_json_structure};
|
|||||||
mod sprite_types;
|
mod sprite_types;
|
||||||
pub use sprite_types::{KnownSpriteType, load_known_sprite_types};
|
pub use sprite_types::{KnownSpriteType, load_known_sprite_types};
|
||||||
mod texture;
|
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};
|
@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use raylib::{texture::Texture2D, RaylibHandle, RaylibThread};
|
use raylib::{
|
||||||
|
audio::{Music, RaylibAudio, Sound},
|
||||||
|
texture::Texture2D,
|
||||||
|
RaylibHandle, RaylibThread,
|
||||||
|
};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
use crate::asset_manager::InternalData;
|
use crate::asset_manager::InternalData;
|
||||||
@ -62,3 +66,70 @@ pub fn load_texture_from_internal_data(
|
|||||||
|
|
||||||
Ok(texture)
|
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)
|
||||||
|
}
|
@ -111,6 +111,11 @@ pub async fn handle_graphics_blocking<ConfigBuilder>(
|
|||||||
constants,
|
constants,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
// Handle exiting the game
|
||||||
|
if render_delegate.needs_exit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => backend_sm = RenderBackendStates::sm_failed(),
|
_ => backend_sm = RenderBackendStates::sm_failed(),
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,7 @@ pub enum MenuStateSignal {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MainMenu {
|
pub struct MainMenu {
|
||||||
has_updated_discord_rpc: bool,
|
pub has_updated_discord_rpc: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MainMenu {
|
impl MainMenu {
|
||||||
|
@ -23,6 +23,8 @@ mod test_fox;
|
|||||||
/// This is a struct to allow for stateful data (like sub-screens) to be set up
|
/// This is a struct to allow for stateful data (like sub-screens) to be set up
|
||||||
pub struct SceneRenderDelegate {
|
pub struct SceneRenderDelegate {
|
||||||
menu_control_signal: MenuStateSignal,
|
menu_control_signal: MenuStateSignal,
|
||||||
|
pub needs_exit: bool,
|
||||||
|
audio_subsystem: RaylibAudio,
|
||||||
/* Scenes */
|
/* Scenes */
|
||||||
scene_test_fox: TestFoxScene,
|
scene_test_fox: TestFoxScene,
|
||||||
scene_playable: PlayableScene,
|
scene_playable: PlayableScene,
|
||||||
@ -36,7 +38,9 @@ impl SceneRenderDelegate {
|
|||||||
rl_thread: &RaylibThread,
|
rl_thread: &RaylibThread,
|
||||||
constants: &ProjectConstants,
|
constants: &ProjectConstants,
|
||||||
) -> Self {
|
) -> 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
|
// Init some scenes
|
||||||
let scene_test_fox = TestFoxScene::new(raylib, rl_thread);
|
let scene_test_fox = TestFoxScene::new(raylib, rl_thread);
|
||||||
@ -45,6 +49,8 @@ impl SceneRenderDelegate {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
menu_control_signal: MenuStateSignal::DoMainMenu,
|
menu_control_signal: MenuStateSignal::DoMainMenu,
|
||||||
|
needs_exit: false,
|
||||||
|
audio_subsystem,
|
||||||
scene_test_fox,
|
scene_test_fox,
|
||||||
scene_playable,
|
scene_playable,
|
||||||
scene_main_menu,
|
scene_main_menu,
|
||||||
@ -62,22 +68,29 @@ impl SceneRenderDelegate {
|
|||||||
global_resources: &GlobalResources,
|
global_resources: &GlobalResources,
|
||||||
constants: &ProjectConstants,
|
constants: &ProjectConstants,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Render the main menu if in it, otherwise, render the game
|
// Render the main menu if in it, otherwise, render the game
|
||||||
match self.menu_control_signal {
|
match self.menu_control_signal {
|
||||||
MenuStateSignal::StartGame => {
|
MenuStateSignal::StartGame => {
|
||||||
self.scene_playable
|
self.scene_playable
|
||||||
.render_frame(raylib, rl_thread, &discord, global_resources, constants)
|
.render_frame(raylib, rl_thread, &discord, global_resources, constants, &mut self.audio_subsystem)
|
||||||
.await;
|
|
||||||
self.scene_playable
|
|
||||||
.update_physics(raylib, constants)
|
|
||||||
.await;
|
.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 => {
|
MenuStateSignal::DoMainMenu => {
|
||||||
self.menu_control_signal = self
|
self.menu_control_signal = self
|
||||||
.scene_main_menu
|
.scene_main_menu
|
||||||
.render_main_menu_frame(raylib, rl_thread, discord, global_resources, constants)
|
.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 => {
|
MenuStateSignal::DoOptions => {
|
||||||
self.menu_control_signal = self
|
self.menu_control_signal = self
|
||||||
|
@ -5,6 +5,7 @@ use raylib::prelude::*;
|
|||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
asset_manager::{load_music_from_internal_data, load_sound_from_internal_data},
|
||||||
discord::{DiscordChannel, DiscordRpcSignal},
|
discord::{DiscordChannel, DiscordRpcSignal},
|
||||||
global_resource_package::GlobalResources,
|
global_resource_package::GlobalResources,
|
||||||
model::player::Player,
|
model::player::Player,
|
||||||
@ -14,40 +15,42 @@ use crate::{
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PlayableScene {
|
pub struct PlayableScene {
|
||||||
has_updated_discord_rpc: bool,
|
pub has_updated_discord_rpc: bool,
|
||||||
player: Player,
|
player: Player,
|
||||||
world_map: MapRenderer,
|
world_map: MapRenderer,
|
||||||
camera: raylib::camera::Camera2D,
|
camera: raylib::camera::Camera2D,
|
||||||
last_update: SystemTime,
|
last_update: SystemTime,
|
||||||
|
game_soundtrack: Music,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayableScene {
|
impl PlayableScene {
|
||||||
/// Construct a new `PlayableScene`
|
/// Construct a new `PlayableScene`
|
||||||
pub fn new(
|
pub fn new(
|
||||||
raylib_handle: &mut raylib::RaylibHandle,
|
raylib_handle: &mut raylib::RaylibHandle,
|
||||||
thread: & raylib::RaylibThread,
|
thread: &raylib::RaylibThread,
|
||||||
constants: &ProjectConstants,
|
constants: &ProjectConstants,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
||||||
let map_renderer = MapRenderer::new("map_gameMap.tmx", raylib_handle, thread).unwrap();
|
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 {
|
Self {
|
||||||
has_updated_discord_rpc: false,
|
has_updated_discord_rpc: false,
|
||||||
player: Player::new(na::Vector2::new(10.0, 10.0)),
|
player: Player::new(na::Vector2::new(10.0, 10.0)),
|
||||||
world_map: map_renderer,
|
world_map: map_renderer,
|
||||||
camera: raylib::camera::Camera2D {
|
camera: raylib::camera::Camera2D {
|
||||||
target: raylib::math::Vector2 {
|
target: raylib::math::Vector2 { x: 0.0, y: 0.0 },
|
||||||
x: 0.0,
|
offset: raylib::math::Vector2 {
|
||||||
y: 0.0,
|
|
||||||
},
|
|
||||||
offset: raylib::math::Vector2 {
|
|
||||||
x: (constants.base_window_size.0 as f32 / 2.0),
|
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,
|
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,
|
discord: &DiscordChannel,
|
||||||
global_resources: &GlobalResources,
|
global_resources: &GlobalResources,
|
||||||
constants: &ProjectConstants,
|
constants: &ProjectConstants,
|
||||||
|
audio_subsystem: &mut RaylibAudio,
|
||||||
) {
|
) {
|
||||||
// Handle updating discord RPC
|
// Handle updating discord RPC
|
||||||
if !self.has_updated_discord_rpc {
|
if !self.has_updated_discord_rpc {
|
||||||
@ -76,96 +80,101 @@ impl PlayableScene {
|
|||||||
self.has_updated_discord_rpc = true;
|
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
|
// Get a drawing handle
|
||||||
let mut draw = raylib.begin_drawing(rl_thread);
|
let mut draw = raylib.begin_drawing(rl_thread);
|
||||||
|
|
||||||
// Clear the screen
|
// Clear the screen
|
||||||
draw.clear_background(Color::WHITE);
|
draw.clear_background(Color::WHITE);
|
||||||
|
|
||||||
self.draw_world(&mut draw, constants);
|
self.draw_world(&mut draw, constants);
|
||||||
|
|
||||||
self.draw_ui(&mut draw, constants);
|
self.draw_ui(&mut draw, constants);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_world(
|
pub fn draw_world(&mut self, draw: &mut RaylibDrawHandle, constants: &ProjectConstants) {
|
||||||
&mut self,
|
|
||||||
draw: &mut RaylibDrawHandle,
|
|
||||||
constants: &ProjectConstants,
|
|
||||||
) {
|
|
||||||
// Begin camera mode
|
// Begin camera mode
|
||||||
let mut ctx2d = draw.begin_mode2D(self.camera);
|
let mut ctx2d = draw.begin_mode2D(self.camera);
|
||||||
|
|
||||||
// Render the map
|
// Render the map
|
||||||
self.world_map.render_map(&mut ctx2d, &self.camera, true);
|
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(
|
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,
|
self.player.position[1] as i32 * -1 - player_size / 2,
|
||||||
player_size,
|
player_size,
|
||||||
player_size,
|
player_size,
|
||||||
Color::LIGHTBLUE
|
Color::LIGHTBLUE,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_ui(
|
pub fn draw_ui(&mut self, draw: &mut RaylibDrawHandle, constants: &ProjectConstants) {
|
||||||
&mut self,
|
draw.draw_rectangle(draw.get_screen_width() / 2 - 225, 0, 450, 40, Color::WHITE);
|
||||||
draw: &mut RaylibDrawHandle,
|
|
||||||
constants: &ProjectConstants,
|
|
||||||
) {
|
|
||||||
draw.draw_rectangle(
|
|
||||||
draw.get_screen_width() / 2 - 225, 0,
|
|
||||||
450, 40,
|
|
||||||
Color::WHITE
|
|
||||||
);
|
|
||||||
draw.draw_text(
|
draw.draw_text(
|
||||||
"Unregistered HyperCam 2",
|
"Unregistered HyperCam 2",
|
||||||
draw.get_screen_width() / 2 - 215, 0,
|
draw.get_screen_width() / 2 - 215,
|
||||||
32, Color::BLACK
|
0,
|
||||||
|
32,
|
||||||
|
Color::BLACK,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Physics
|
// Physics
|
||||||
pub async fn update_physics(
|
pub async fn update_physics(
|
||||||
&mut self,
|
&mut self,
|
||||||
raylib: & raylib::RaylibHandle,
|
raylib: &raylib::RaylibHandle,
|
||||||
constants: &ProjectConstants,
|
constants: &ProjectConstants,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Get time since last physics update
|
// Get time since last physics update
|
||||||
let time = SystemTime::now();
|
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;
|
self.last_update = time;
|
||||||
let delta_time = elapsed.as_millis() as f32 / 1000.0; // Physics will be scaled by this value
|
let delta_time = elapsed.as_millis() as f32 / 1000.0; // Physics will be scaled by this value
|
||||||
|
|
||||||
let player = &mut self.player;
|
let player = &mut self.player;
|
||||||
|
|
||||||
// Get input direction components
|
// 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 h_axis = raylib.is_key_down(KeyboardKey::KEY_D) as i8
|
||||||
let v_axis = raylib.is_key_down(KeyboardKey::KEY_W) as i8 - raylib.is_key_down(KeyboardKey::KEY_S) 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 {
|
if h_axis != 0 || v_axis != 0 {
|
||||||
// Normalize input and accelerate in desired direction
|
// Normalize input and accelerate in desired direction
|
||||||
let direction = na::Vector2::new(h_axis as f32, v_axis as f32).normalize();
|
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.player.acceleration as f32
|
||||||
* constants.tile_size as f32
|
* constants.tile_size as f32
|
||||||
* delta_time;
|
* delta_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
if player.velocity.magnitude() != 0.0 {
|
if player.velocity.magnitude() != 0.0 {
|
||||||
player.velocity -= player.velocity.normalize()
|
player.velocity -= player.velocity.normalize()
|
||||||
* constants.player.deceleration as f32
|
* constants.player.deceleration as f32
|
||||||
* constants.tile_size as f32
|
* constants.tile_size as f32
|
||||||
* delta_time;
|
* delta_time;
|
||||||
if player.velocity.magnitude() < 1.0 {
|
if player.velocity.magnitude() < 1.0 {
|
||||||
player.velocity.set_magnitude(0.0);
|
player.velocity.set_magnitude(0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((constants.player.max_velocity * constants.tile_size) as f32)
|
if ((constants.player.max_velocity * constants.tile_size) as f32)
|
||||||
< player.velocity.magnitude() {
|
< player.velocity.magnitude()
|
||||||
player.velocity.set_magnitude((constants.player.max_velocity * constants.tile_size) as f32);
|
{
|
||||||
|
player
|
||||||
|
.velocity
|
||||||
|
.set_magnitude((constants.player.max_velocity * constants.tile_size) as f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
player.position += &player.velocity * delta_time;
|
player.position += &player.velocity * delta_time;
|
||||||
@ -173,15 +182,10 @@ impl PlayableScene {
|
|||||||
self.update_camera(raylib);
|
self.update_camera(raylib);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_camera(
|
pub fn update_camera(&mut self, raylib: &raylib::RaylibHandle) {
|
||||||
&mut self,
|
|
||||||
raylib: & raylib::RaylibHandle,
|
|
||||||
) {
|
|
||||||
self.camera.target = self.player.position.into();
|
self.camera.target = self.player.position.into();
|
||||||
self.camera.target.y *= -1.0;
|
self.camera.target.y *= -1.0;
|
||||||
self.camera.offset.x = raylib.get_screen_width() as f32 / 2.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;
|
self.camera.offset.y = raylib.get_screen_height() as f32 / 2.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user