allow controlling volume for sound/music
This commit is contained in:
27
crates/hedz_reloaded/src/client/audio/mod.rs
Normal file
27
crates/hedz_reloaded/src/client/audio/mod.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
mod music;
|
||||
mod sounds;
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Resource, Reflect, Clone, Copy, Debug)]
|
||||
#[reflect(Resource)]
|
||||
pub struct SoundSettings {
|
||||
pub sound_volume: f32,
|
||||
pub music_volume: f32,
|
||||
}
|
||||
|
||||
impl Default for SoundSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sound_volume: 0.8,
|
||||
music_volume: 0.5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.init_resource::<SoundSettings>();
|
||||
|
||||
app.add_plugins(music::plugin);
|
||||
app.add_plugins(sounds::plugin);
|
||||
}
|
||||
45
crates/hedz_reloaded/src/client/audio/music.rs
Normal file
45
crates/hedz_reloaded/src/client/audio/music.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::{GameState, client::audio::SoundSettings, loading_assets::AudioAssets};
|
||||
use bevy::{
|
||||
audio::{PlaybackMode, Volume},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
#[derive(Component)]
|
||||
struct Music;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(OnEnter(GameState::Playing), music);
|
||||
app.add_systems(Update, settings_changed);
|
||||
}
|
||||
|
||||
fn settings_changed(settings: Res<SoundSettings>, mut query: Query<&mut AudioSink, With<Music>>) {
|
||||
if settings.is_changed() {
|
||||
for mut sink in query.iter_mut() {
|
||||
sink.set_volume(Volume::Linear(settings.music_volume));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn music(mut commands: Commands, assets: Res<AudioAssets>, settings: Res<SoundSettings>) {
|
||||
commands.spawn((
|
||||
Name::new("sfx-music"),
|
||||
Music,
|
||||
AudioPlayer::new(assets.music.clone()),
|
||||
PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::Linear(settings.music_volume),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
Name::new("sfx-ambient"),
|
||||
Music,
|
||||
AudioPlayer::new(assets.ambient.clone()),
|
||||
PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::Linear(settings.music_volume),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::{global_observer, loading_assets::AudioAssets, protocol::PlaySound};
|
||||
use bevy::prelude::*;
|
||||
use crate::{
|
||||
client::audio::SoundSettings, global_observer, loading_assets::AudioAssets, protocol::PlaySound,
|
||||
};
|
||||
use bevy::{audio::Volume, prelude::*};
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
global_observer!(app, on_spawn_sounds);
|
||||
@@ -8,14 +10,14 @@ pub fn plugin(app: &mut App) {
|
||||
fn on_spawn_sounds(
|
||||
trigger: On<PlaySound>,
|
||||
mut commands: Commands,
|
||||
// settings: SettingsRead,
|
||||
assets: Res<AudioAssets>,
|
||||
settings: Res<SoundSettings>,
|
||||
) {
|
||||
let event = trigger.event();
|
||||
if settings.sound_volume <= f32::EPSILON {
|
||||
return;
|
||||
}
|
||||
|
||||
// if !settings.is_sound_on() {
|
||||
// continue;
|
||||
// }
|
||||
let event = trigger.event();
|
||||
|
||||
let source = match event {
|
||||
PlaySound::Hit => {
|
||||
@@ -60,6 +62,7 @@ fn on_spawn_sounds(
|
||||
AudioPlayer::new(source),
|
||||
PlaybackSettings {
|
||||
mode: bevy::audio::PlaybackMode::Despawn,
|
||||
volume: Volume::Linear(settings.sound_volume),
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
209
crates/hedz_reloaded/src/client/mod.rs
Normal file
209
crates/hedz_reloaded/src/client/mod.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use crate::{
|
||||
GameState,
|
||||
config::NetworkingConfig,
|
||||
protocol::{
|
||||
ClientEnteredPlaying, TbMapEntityId, TbMapEntityMapping, messages::DespawnTbMapEntity,
|
||||
},
|
||||
tb_entities::{Movable, Platform, PlatformTarget},
|
||||
};
|
||||
use avian3d::prelude::{
|
||||
Collider, ColliderAabb, ColliderDensity, ColliderMarker, ColliderOf, ColliderTransform,
|
||||
CollisionEventsEnabled, CollisionLayers, Sensor,
|
||||
};
|
||||
use bevy::{ecs::bundle::BundleFromComponents, prelude::*, scene::SceneInstance};
|
||||
use bevy_replicon::{
|
||||
client::{ClientSystems, confirm_history::ConfirmHistory},
|
||||
prelude::{ClientState, ClientTriggerExt, RepliconChannels},
|
||||
};
|
||||
use bevy_replicon_renet::{
|
||||
RenetChannelsExt,
|
||||
netcode::{ClientAuthentication, NetcodeClientTransport, NetcodeError},
|
||||
renet::{ConnectionConfig, RenetClient},
|
||||
};
|
||||
use bevy_trenchbroom::geometry::Brushes;
|
||||
use std::{
|
||||
net::{Ipv4Addr, UdpSocket},
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
pub mod audio;
|
||||
pub mod backpack;
|
||||
pub mod control;
|
||||
pub mod debug;
|
||||
pub mod enemy;
|
||||
pub mod heal_effect;
|
||||
pub mod player;
|
||||
pub mod setup;
|
||||
pub mod steam;
|
||||
pub mod ui;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_plugins((
|
||||
backpack::plugin,
|
||||
control::plugin,
|
||||
debug::plugin,
|
||||
enemy::plugin,
|
||||
heal_effect::plugin,
|
||||
player::plugin,
|
||||
setup::plugin,
|
||||
audio::plugin,
|
||||
steam::plugin,
|
||||
ui::plugin,
|
||||
));
|
||||
|
||||
app.add_systems(
|
||||
OnEnter(GameState::Connecting),
|
||||
connect_to_server.run_if(|config: Res<NetworkingConfig>| config.server.is_some()),
|
||||
);
|
||||
app.add_systems(Update, despawn_absent_map_entities);
|
||||
app.add_systems(
|
||||
PreUpdate,
|
||||
(migrate_remote_entities, ApplyDeferred)
|
||||
.chain()
|
||||
.after(ClientSystems::Receive),
|
||||
);
|
||||
|
||||
app.add_systems(OnEnter(ClientState::Connected), on_connected_state);
|
||||
app.add_systems(OnExit(ClientState::Connected), on_disconnect);
|
||||
}
|
||||
|
||||
//
|
||||
// Client logic
|
||||
//
|
||||
|
||||
fn on_connected_state(mut commands: Commands, mut game_state: ResMut<NextState<GameState>>) {
|
||||
info!("sent entered playing signal");
|
||||
commands.client_trigger(ClientEnteredPlaying);
|
||||
game_state.set(GameState::Playing);
|
||||
}
|
||||
|
||||
fn on_disconnect() {
|
||||
info!("disconnected from the server");
|
||||
}
|
||||
|
||||
//
|
||||
// Renet
|
||||
//
|
||||
|
||||
fn connect_to_server(
|
||||
mut commands: Commands,
|
||||
config: Res<NetworkingConfig>,
|
||||
channels: Res<RepliconChannels>,
|
||||
) -> Result {
|
||||
let server_channels_config = channels.server_configs();
|
||||
let client_channels_config = channels.client_configs();
|
||||
|
||||
let client = RenetClient::new(ConnectionConfig {
|
||||
server_channels_config,
|
||||
client_channels_config,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
commands.insert_resource(client);
|
||||
commands.insert_resource(client_transport(&config)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn client_transport(config: &NetworkingConfig) -> Result<NetcodeClientTransport, NetcodeError> {
|
||||
let current_time = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap();
|
||||
let client_id = current_time.as_millis() as u64;
|
||||
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?;
|
||||
let server_addr = config
|
||||
.server
|
||||
.flatten()
|
||||
.unwrap_or_else(|| "127.0.0.1:31111".parse().unwrap());
|
||||
let authentication = ClientAuthentication::Unsecure {
|
||||
client_id,
|
||||
protocol_id: 0,
|
||||
server_addr,
|
||||
user_data: None,
|
||||
};
|
||||
|
||||
info!("attempting connection to {server_addr}");
|
||||
NetcodeClientTransport::new(current_time, authentication, socket)
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn migrate_remote_entities(
|
||||
query: Query<(Entity, &TbMapEntityId), (Added<TbMapEntityId>, With<ConfirmHistory>)>,
|
||||
children: Query<&Children>,
|
||||
mut commands: Commands,
|
||||
mut mapping: ResMut<TbMapEntityMapping>,
|
||||
) {
|
||||
for (serverside, tb_id) in query.iter() {
|
||||
received_remote_map_entity(serverside, tb_id.id, &children, &mut mapping, &mut commands);
|
||||
}
|
||||
}
|
||||
|
||||
fn received_remote_map_entity(
|
||||
serverside: Entity,
|
||||
tb_id: u64,
|
||||
children: &Query<&Children>,
|
||||
mapping: &mut TbMapEntityMapping,
|
||||
commands: &mut Commands,
|
||||
) {
|
||||
let Some(clientside) = mapping.0.remove(&tb_id) else {
|
||||
warn!("received unknown MapEntity ID `{tb_id:?}`");
|
||||
return;
|
||||
};
|
||||
|
||||
// cannot just use `take` directly with a bundle because then any missing component would cause
|
||||
// the entire bundle to fail
|
||||
move_component::<Brushes>(commands, clientside, serverside);
|
||||
move_component::<(
|
||||
Collider,
|
||||
ColliderAabb,
|
||||
ColliderDensity,
|
||||
ColliderMarker,
|
||||
CollisionLayers,
|
||||
)>(commands, clientside, serverside);
|
||||
move_component::<ColliderOf>(commands, clientside, serverside);
|
||||
move_component::<ColliderTransform>(commands, clientside, serverside);
|
||||
move_component::<CollisionEventsEnabled>(commands, clientside, serverside);
|
||||
move_component::<Movable>(commands, clientside, serverside);
|
||||
move_component::<Platform>(commands, clientside, serverside);
|
||||
move_component::<PlatformTarget>(commands, clientside, serverside);
|
||||
move_component::<SceneInstance>(commands, clientside, serverside);
|
||||
move_component::<SceneRoot>(commands, clientside, serverside);
|
||||
move_component::<Sensor>(commands, clientside, serverside);
|
||||
|
||||
if let Ok(children) = children.get(clientside) {
|
||||
for child in children.iter() {
|
||||
commands.entity(child).insert(ChildOf(serverside));
|
||||
}
|
||||
}
|
||||
|
||||
commands.entity(clientside).despawn();
|
||||
}
|
||||
|
||||
fn move_component<B: Bundle + BundleFromComponents>(
|
||||
commands: &mut Commands,
|
||||
from: Entity,
|
||||
to: Entity,
|
||||
) {
|
||||
commands.queue(move |world: &mut World| {
|
||||
let comp = world.entity_mut(from).take::<B>();
|
||||
if let Some(comp) = comp {
|
||||
world.entity_mut(to).insert(comp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn despawn_absent_map_entities(
|
||||
mut commands: Commands,
|
||||
mut messages: MessageReader<DespawnTbMapEntity>,
|
||||
mut map: ResMut<TbMapEntityMapping>,
|
||||
) {
|
||||
for msg in messages.read() {
|
||||
// the server may double-send DespawnTbMapEntity for a given ID, so ignore it if the entity
|
||||
// was already despawned.
|
||||
let Some(entity) = map.0.remove(&msg.0) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
use crate::{DebugVisuals, GameState, camera::MainCamera, loading_assets::AudioAssets};
|
||||
use bevy::{
|
||||
audio::{PlaybackMode, Volume},
|
||||
core_pipeline::tonemapping::Tonemapping,
|
||||
prelude::*,
|
||||
render::view::ColorGrading,
|
||||
};
|
||||
use crate::{DebugVisuals, camera::MainCamera};
|
||||
use bevy::{core_pipeline::tonemapping::Tonemapping, prelude::*, render::view::ColorGrading};
|
||||
use bevy_trenchbroom::TrenchBroomServer;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
@@ -21,36 +16,11 @@ pub fn plugin(app: &mut App) {
|
||||
..Default::default()
|
||||
});
|
||||
app.insert_resource(ClearColor(Color::BLACK));
|
||||
//TODO: let user control this
|
||||
app.insert_resource(GlobalVolume::new(Volume::Linear(0.4)));
|
||||
|
||||
app.add_systems(Startup, write_trenchbroom_config);
|
||||
app.add_systems(OnEnter(GameState::Playing), music);
|
||||
app.add_systems(Update, (set_materials_unlit, set_tonemapping, set_shadows));
|
||||
}
|
||||
|
||||
fn music(assets: Res<AudioAssets>, mut commands: Commands) {
|
||||
commands.spawn((
|
||||
Name::new("sfx-music"),
|
||||
AudioPlayer::new(assets.music.clone()),
|
||||
PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::Linear(0.6),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
Name::new("sfx-ambient"),
|
||||
AudioPlayer::new(assets.ambient.clone()),
|
||||
PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::Linear(0.8),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
fn write_trenchbroom_config(server: Res<TrenchBroomServer>, type_registry: Res<AppTypeRegistry>) {
|
||||
if let Err(e) = server
|
||||
.config
|
||||
|
||||
Reference in New Issue
Block a user