Updating GlobalVolume now updates the volume of any playing (Spatial)AudioSinks
Fixes #18952
This commit is contained in:
parent
86cc02dca2
commit
405fa13dc6
@ -86,6 +86,37 @@ impl<'w, 's> EarPositions<'w, 's> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates "active" audio and updates their volume based on their [`PlaybackSettings`] and the updated [`GlobalVolume`]
|
||||
///
|
||||
/// "Active" audio is any entity with an [`AudioSink`]/[`SpatialAudioSink`]
|
||||
///
|
||||
/// This system triggers only on [`GlobalVolume`] updates
|
||||
pub(crate) fn update_playing_audio_volume(
|
||||
mut query_playing_audio_sinks: Query<
|
||||
(&mut AudioSink, &PlaybackSettings),
|
||||
(
|
||||
Without<PlaybackDespawnMarker>,
|
||||
Without<PlaybackRemoveMarker>,
|
||||
),
|
||||
>,
|
||||
mut query_playing_spatial_audio_sinks: Query<
|
||||
(&mut SpatialAudioSink, &PlaybackSettings),
|
||||
(
|
||||
Without<PlaybackDespawnMarker>,
|
||||
Without<PlaybackRemoveMarker>,
|
||||
),
|
||||
>,
|
||||
global_volume: Res<GlobalVolume>,
|
||||
) {
|
||||
for (mut sink, settings) in query_playing_audio_sinks.iter_mut() {
|
||||
sink.set_volume(settings.volume * global_volume.volume);
|
||||
}
|
||||
|
||||
for (mut sink, settings) in query_playing_spatial_audio_sinks.iter_mut() {
|
||||
sink.set_volume(settings.volume * global_volume.volume);
|
||||
}
|
||||
}
|
||||
|
||||
/// Plays "queued" audio through the [`AudioOutput`] resource.
|
||||
///
|
||||
/// "Queued" audio is any audio entity (with an [`AudioPlayer`] component) that does not have an
|
||||
|
||||
@ -118,7 +118,11 @@ impl AddAudioSource for App {
|
||||
{
|
||||
self.init_asset::<T>().add_systems(
|
||||
PostUpdate,
|
||||
(play_queued_audio_system::<T>, cleanup_finished_audio::<T>)
|
||||
(
|
||||
update_playing_audio_volume.run_if(resource_changed::<GlobalVolume>),
|
||||
play_queued_audio_system::<T>,
|
||||
cleanup_finished_audio::<T>,
|
||||
)
|
||||
.in_set(AudioPlaybackSystems),
|
||||
);
|
||||
self
|
||||
|
||||
@ -3,8 +3,6 @@ use bevy_math::ops;
|
||||
use bevy_reflect::prelude::*;
|
||||
|
||||
/// Use this [`Resource`] to control the global volume of all audio.
|
||||
///
|
||||
/// Note: Changing [`GlobalVolume`] does not affect already playing audio.
|
||||
#[derive(Resource, Debug, Default, Clone, Copy, Reflect)]
|
||||
#[reflect(Resource, Debug, Default, Clone)]
|
||||
pub struct GlobalVolume {
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
const TEXT_COLOR: Color = Color::srgb(0.9, 0.9, 0.9);
|
||||
const VOLUME_MAX: u32 = 9;
|
||||
|
||||
// Enum that will be used as a global state for the game
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)]
|
||||
@ -27,6 +28,9 @@ enum DisplayQuality {
|
||||
#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)]
|
||||
struct Volume(u32);
|
||||
|
||||
#[derive(Resource, Deref)]
|
||||
struct MenuMusic(Handle<AudioSource>);
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
@ -36,13 +40,20 @@ fn main() {
|
||||
// Declare the game state, whose starting value is determined by the `Default` trait
|
||||
.init_state::<GameState>()
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(
|
||||
Update,
|
||||
translate_discreet_volume_to_global_volume.run_if(resource_changed::<Volume>),
|
||||
)
|
||||
// Adds the plugins for each state
|
||||
.add_plugins((splash::splash_plugin, menu::menu_plugin, game::game_plugin))
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn(Camera2d);
|
||||
// Sound
|
||||
let music = asset_server.load("sounds/Windless Slopes.ogg");
|
||||
commands.insert_resource(MenuMusic(music));
|
||||
}
|
||||
|
||||
mod splash {
|
||||
@ -113,14 +124,19 @@ mod game {
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::{play_music, stop_music};
|
||||
|
||||
use super::{despawn_screen, DisplayQuality, GameState, Volume, TEXT_COLOR};
|
||||
|
||||
// This plugin will contain the game. In this case, it's just be a screen that will
|
||||
// display the current settings for 5 seconds before returning to the menu
|
||||
pub fn game_plugin(app: &mut App) {
|
||||
app.add_systems(OnEnter(GameState::Game), game_setup)
|
||||
app.add_systems(OnEnter(GameState::Game), (game_setup, play_music))
|
||||
.add_systems(Update, game.run_if(in_state(GameState::Game)))
|
||||
.add_systems(OnExit(GameState::Game), despawn_screen::<OnGameScreen>);
|
||||
.add_systems(
|
||||
OnExit(GameState::Game),
|
||||
(despawn_screen::<OnGameScreen>, stop_music),
|
||||
);
|
||||
}
|
||||
|
||||
// Tag component used to tag entities added on the game screen
|
||||
@ -229,6 +245,8 @@ mod menu {
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::{play_music, stop_music, VOLUME_MAX};
|
||||
|
||||
use super::{despawn_screen, DisplayQuality, GameState, Volume, TEXT_COLOR};
|
||||
|
||||
// This plugin manages the menu, with 5 different screens:
|
||||
@ -265,14 +283,17 @@ mod menu {
|
||||
despawn_screen::<OnDisplaySettingsMenuScreen>,
|
||||
)
|
||||
// Systems to handle the sound settings screen
|
||||
.add_systems(OnEnter(MenuState::SettingsSound), sound_settings_menu_setup)
|
||||
.add_systems(
|
||||
OnEnter(MenuState::SettingsSound),
|
||||
(sound_settings_menu_setup, play_music),
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
setting_button::<Volume>.run_if(in_state(MenuState::SettingsSound)),
|
||||
)
|
||||
.add_systems(
|
||||
OnExit(MenuState::SettingsSound),
|
||||
despawn_screen::<OnSoundSettingsMenuScreen>,
|
||||
(despawn_screen::<OnSoundSettingsMenuScreen>, stop_music),
|
||||
)
|
||||
// Common systems to all screens that handles buttons behavior
|
||||
.add_systems(
|
||||
@ -663,7 +684,7 @@ mod menu {
|
||||
Children::spawn((
|
||||
Spawn((Text::new("Volume"), button_text_style.clone())),
|
||||
SpawnWith(move |parent: &mut ChildSpawner| {
|
||||
for volume_setting in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] {
|
||||
for volume_setting in 0..VOLUME_MAX {
|
||||
let mut entity = parent.spawn((
|
||||
Button,
|
||||
Node {
|
||||
@ -735,3 +756,25 @@ fn despawn_screen<T: Component>(to_despawn: Query<Entity, With<T>>, mut commands
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_music(playing_sounds_query: Query<AnyOf<(&AudioSink, &SpatialAudioSink)>>) {
|
||||
for (audio_sink, spatial_audio_sink) in &playing_sounds_query {
|
||||
if let Some(audio_sink) = audio_sink {
|
||||
audio_sink.stop();
|
||||
}
|
||||
if let Some(spatial_audio_sink) = spatial_audio_sink {
|
||||
spatial_audio_sink.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn play_music(mut commands: Commands, music: Res<MenuMusic>) {
|
||||
commands.spawn((AudioPlayer(music.clone()), PlaybackSettings::DESPAWN));
|
||||
}
|
||||
|
||||
fn translate_discreet_volume_to_global_volume(
|
||||
volume: Res<Volume>,
|
||||
mut global_volume: ResMut<GlobalVolume>,
|
||||
) {
|
||||
global_volume.volume = bevy::audio::Volume::Linear(volume.0 as f32 / VOLUME_MAX as f32);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user