add background music

This commit is contained in:
Jim 2025-09-02 19:59:48 -04:00
parent c6d9a002b6
commit 86f2345667
Signed by: jim
GPG key ID: 3D7D94BA53088BF4
13 changed files with 210 additions and 8 deletions

11
Cargo.lock generated
View file

@ -2046,6 +2046,7 @@ dependencies = [
"inhabitant_simbert",
"input",
"level",
"music",
"physics",
"resource_graveyard",
"resource_soul_spot",
@ -3458,6 +3459,15 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "music"
version = "0.1.0"
dependencies = [
"bevy",
"rand",
"state",
]
[[package]]
name = "naga"
version = "24.0.0"
@ -5411,6 +5421,7 @@ dependencies = [
"bevy",
"bevy_egui",
"combat",
"music",
"state",
]

View file

@ -64,4 +64,5 @@ resource_soul_spot = { path = "crates/resource_soul_spot" }
building_ramtower = { path = "crates/building_ramtower" }
building_suncannon = { path = "crates/building_suncannon" }
ui_menu = { path = "crates/ui_menu" }
music = { path = "crates/music" }
#new internal crates go here

Binary file not shown.

BIN
assets/music/Aquarium.ogg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,22 +1,22 @@
(
map: {
KeyCode(ShiftLeft): [
MultiPlaceTower,
],
MouseButton(Middle): [
CameraOrbitEnable,
],
KeyCode(F1): [
ToggleDebugUI,
],
MouseButton(Right): [
PlaceTower,
],
MouseButton(Left): [
CameraPanEnable,
],
KeyCode(ShiftLeft): [
MultiPlaceTower,
],
MouseScroll: [
CameraZoom,
],
MouseButton(Left): [
CameraPanEnable,
KeyCode(F1): [
ToggleDebugUI,
],
MouseMovementX: [
CameraYaw,

12
crates/music/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "music"
version = "0.1.0"
edition = "2024"
[dependencies]
bevy = { workspace = true }
rand = { workspace = true }
state = { path = "../state" }
[lints]
workspace = true

93
crates/music/src/lib.rs Normal file
View file

@ -0,0 +1,93 @@
//bevy system signatures often violate these rules
#![allow(clippy::type_complexity)]
#![allow(clippy::too_many_arguments)]
use bevy::prelude::*;
use rand::{Rng, thread_rng};
use state::AppState;
pub struct MusicPlugin;
impl Plugin for MusicPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, init)
.add_systems(Update, update_track)
.add_systems(OnEnter(AppState::Menu), play_menu_music)
.add_systems(OnEnter(AppState::Game), play_game_music);
}
}
#[derive(Component)]
pub struct BackgroundMusicPlayer {
menu_music: Vec<Handle<AudioSource>>,
game_music: Vec<Handle<AudioSource>>,
track_index: usize,
}
fn init(mut commands: Commands, assets: Res<AssetServer>) {
commands.spawn(BackgroundMusicPlayer {
menu_music: vec![
assets.load("music/Gymnopedie No 3.ogg"),
assets.load("music/A Very Brady Special.ogg"),
],
game_music: vec![
assets.load("music/Aquarium.ogg"),
assets.load("music/Egmont Overture.ogg"),
assets.load("music/Ghost Dance.ogg"),
],
track_index: 0,
});
}
fn play_menu_music(
player_query: Single<(Entity, &mut BackgroundMusicPlayer)>,
mut commands: Commands,
) {
let (player_entity, mut player) = player_query.into_inner();
player.track_index = 0;
commands
.entity(player_entity)
.remove::<AudioSink>()
.insert(AudioPlayer(player.menu_music[player.track_index].clone()));
}
fn play_game_music(
player_query: Single<(Entity, &mut BackgroundMusicPlayer)>,
mut commands: Commands,
) {
let (player_entity, mut player) = player_query.into_inner();
player.track_index = thread_rng().gen_range(0..player.game_music.len());
commands
.entity(player_entity)
.remove::<AudioSink>()
.insert(AudioPlayer(player.game_music[player.track_index].clone()));
}
fn update_track(
state: Res<State<AppState>>,
player_query: Single<(Entity, &AudioSink, &mut BackgroundMusicPlayer)>,
mut commands: Commands,
) {
let (entity, sink, mut background_player) = player_query.into_inner();
if sink.empty() {
let next_track = match state.get() {
AppState::Menu => {
background_player.track_index =
(background_player.track_index + 1) % background_player.menu_music.len();
background_player.menu_music[background_player.track_index].clone()
}
AppState::Game => {
background_player.track_index =
(background_player.track_index + 1) % background_player.game_music.len();
background_player.game_music[background_player.track_index].clone()
}
_ => {
return;
}
};
commands
.entity(entity)
.remove::<AudioSink>()
.insert(AudioPlayer(next_track));
}
}

View file

@ -8,6 +8,7 @@ bevy = { workspace = true }
bevy_egui = { workspace = true }
state = { path = "../state" }
combat = { path = "../combat" }
music = { path = "../music" }
[lints]
workspace = true

View file

@ -8,6 +8,7 @@ use bevy_egui::{
egui::{self, Align2, RichText},
};
use combat::Difficulty;
use music::BackgroundMusicPlayer;
use state::AppState;
pub struct UiMenuPlugin;
@ -22,6 +23,7 @@ impl Plugin for UiMenuPlugin {
difficulty_screen.run_if(in_state(MenuScreen::Difficulty)),
info_screen.run_if(in_state(MenuScreen::Info)),
settings_screen.run_if(in_state(MenuScreen::Settings)),
credits_screen.run_if(in_state(MenuScreen::Credits)),
),
)
.add_sub_state::<MenuScreen>();
@ -39,6 +41,7 @@ enum MenuScreen {
Info,
Difficulty,
Settings,
Credits,
}
fn setup_menu(mut commands: Commands) {
@ -96,6 +99,14 @@ fn main_screen(mut contexts: EguiContexts, mut next_screen: ResMut<NextState<Men
next_screen.set(MenuScreen::Settings);
}
});
ui.centered_and_justified(|ui| {
if ui
.button(RichText::new("CREDITS").size(VERY_LARGE_FONT_SIZE))
.clicked()
{
next_screen.set(MenuScreen::Credits);
}
});
});
}
@ -212,6 +223,7 @@ fn settings_screen(
mut contexts: EguiContexts,
mut next_screen: ResMut<NextState<MenuScreen>>,
mut global_volume: ResMut<GlobalVolume>,
mut music_query: Query<&mut AudioSink, With<BackgroundMusicPlayer>>,
mut volume: Local<f32>,
mut init: Local<bool>,
) {
@ -230,6 +242,77 @@ fn settings_screen(
ui.label(RichText::new("Volume").size(FONT_SIZE));
ui.add(egui::Slider::new(&mut *volume, 0.0..=100.));
global_volume.volume = Volume::Linear(*volume / 100.);
if let Ok(mut sink) = music_query.single_mut() {
sink.set_volume(Volume::Linear(*volume / 100.));
}
});
egui::Window::new("SettingsBack")
.resizable(false)
.collapsible(false)
.title_bar(false)
.anchor(Align2::LEFT_TOP, [0., 0.])
.show(ctx, |ui| {
if ui
.button(RichText::new("BACK").size(VERY_LARGE_FONT_SIZE))
.clicked()
{
next_screen.set(MenuScreen::Main);
}
});
}
fn credits_screen(mut contexts: EguiContexts, mut next_screen: ResMut<NextState<MenuScreen>>) {
let ctx = contexts.ctx_mut().unwrap();
egui::Window::new("SettingsScreen")
.resizable(false)
.collapsible(false)
.title_bar(false)
.anchor(Align2::CENTER_CENTER, [0., 0.])
.fixed_size([800., 100.])
.show(ctx, |ui| {
ui.heading("Created by Jim Moore");
ui.heading("Music (in game)");
ui.label(
RichText::new(
"\"Aquarium\" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
http://creativecommons.org/licenses/by/4.0/",
)
.size(FONT_SIZE),
);
ui.label(
RichText::new(
"\"Egmont Overture\" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
http://creativecommons.org/licenses/by/4.0/",
)
.size(FONT_SIZE),
);
ui.label(
RichText::new(
"\"Ghost Dance\" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
http://creativecommons.org/licenses/by/4.0/",
)
.size(FONT_SIZE),
);
ui.heading("Music (menu)");
ui.label(
RichText::new(
"\"Gymnopedie No. 3\" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
http://creativecommons.org/licenses/by/4.0/",
)
.size(FONT_SIZE),
);
ui.label(
RichText::new(
"\"A Very Brady Special\" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
http://creativecommons.org/licenses/by/4.0/",
)
.size(FONT_SIZE),
);
});
egui::Window::new("SettingsBack")
.resizable(false)

View file

@ -47,6 +47,7 @@ fn main() {
building_ramtower::BuildingRamtowerPlugin,
building_suncannon::BuildingSuncannonPlugin,
ui_menu::UiMenuPlugin,
music::MusicPlugin,
// new internal crates go here
));