diff --git a/README.md b/README.md index 35919f8..2b2d59a 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ I aim to explore every aspect of building a game with Bevy without having a spec - [x] Implement a satisfactory camera (refer to [bevy-cameraman](https://github.com/fabienjuif/bevy_cameraman)) - [x] Integrate physics/collision mechanics +- [x] Audio - [ ] Develop responsive player control/kinematics - [ ] Incorporate particle systems - [ ] Explore networking capabilities diff --git a/assets/credits.md b/assets/credits.md new file mode 100644 index 0000000..d564e31 --- /dev/null +++ b/assets/credits.md @@ -0,0 +1,5 @@ +# Credits + +## Audio + +- [explosion.ogg](https://freesound.org/people/Werra/sounds/244394/) - edited diff --git a/assets/sounds/explosion.ogg b/assets/sounds/explosion.ogg new file mode 100644 index 0000000..451a43c Binary files /dev/null and b/assets/sounds/explosion.ogg differ diff --git a/src/audio.rs b/src/audio.rs new file mode 100644 index 0000000..2fedbf8 --- /dev/null +++ b/src/audio.rs @@ -0,0 +1,48 @@ +use bevy::prelude::*; +use bevy_cameraman::Cameraman; + +const DEBUG: bool = false; + +pub struct AudioPlugin; + +impl Plugin for AudioPlugin { + fn build(&self, app: &mut App) { + app.add_systems(PostStartup, setup); + } +} + +fn setup(mut commands: Commands, query_camera: Query>) { + let gap = 200.; + let listener = SpatialListener::new(gap); + + for entity in &query_camera { + let mut cmd = commands.entity(entity); + let cmd = cmd.insert((SpatialBundle::default(), listener.clone())); + + if DEBUG { + cmd.with_children(|parent| { + // left ear + parent.spawn(SpriteBundle { + sprite: Sprite { + color: Color::RED, + custom_size: Some(Vec2::splat(20.0)), + ..default() + }, + transform: Transform::from_xyz(-gap / 2.0, 0.0, 0.0), + ..default() + }); + + // right ear + parent.spawn(SpriteBundle { + sprite: Sprite { + color: Color::GREEN, + custom_size: Some(Vec2::splat(20.0)), + ..default() + }, + transform: Transform::from_xyz(gap / 2.0, 0.0, 0.0), + ..default() + }); + }); + } + } +} diff --git a/src/main.rs b/src/main.rs index c008585..7fe2994 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod audio; mod castles; mod common; mod health; @@ -7,6 +8,7 @@ mod player; mod racks; mod teams; +use audio::AudioPlugin; use bevy::{ log::{Level, LogPlugin}, prelude::*, @@ -24,16 +26,29 @@ use racks::RacksPlugin; use teams::TeamsPlugin; use xxhash_rust::xxh3::xxh3_64; +const AUDIO_SCALE: f32 = 1. / 100.0; + fn main() { let mut app = App::new(); let seed = b"13U2x"; app.add_plugins(( - DefaultPlugins.set(LogPlugin { - level: Level::TRACE, - filter: "wgpu=error,bevy_render=warn,bevy_app=warn,bevy_ecs=warn,naga=warn,gilrs=warn,game::health=info,game::racks=info" - .to_string(), - }), + DefaultPlugins + .set(LogPlugin { + level: Level::TRACE, + filter: [ + "wgpu=error", + "bevy_render=warn,bevy_app=warn,bevy_ecs=warn", + "naga=warn", + "gilrs=warn", + "game::health=info,game::racks=info", + ] + .join(","), + }) + .set(bevy::audio::AudioPlugin { + spatial_scale: bevy::audio::SpatialScale::new_2d(AUDIO_SCALE), + ..default() + }), RngPlugin::new().with_rng_seed(xxh3_64(seed)), PhysicsPlugin, TeamsPlugin, @@ -42,6 +57,7 @@ fn main() { CastlesPlugin, HealthPlugin, LocalPlayerPlugin, + AudioPlugin, )) // --- camera --- .add_plugins(( diff --git a/src/minions.rs b/src/minions.rs index 344b2de..31f19a8 100644 --- a/src/minions.rs +++ b/src/minions.rs @@ -12,6 +12,8 @@ const DESTROY_MINIONS_AFTER_SECS: f32 = 120.; const DECAY_VALUE_PER_SEC: f32 = 10.; const REWARDS_GOLD: f32 = 1.; +const EXPLOSION_AUDIO_ID: &str = "sounds/explosion.ogg"; + pub struct MinionsPlugin; // TODO: move this into common @@ -25,21 +27,30 @@ struct Minion { had_exploded: bool, } +#[derive(Resource)] +struct AudioExplosion(Handle); + impl Plugin for MinionsPlugin { fn build(&self, app: &mut App) { - app.add_systems( - Update, - ( - update_move_minions, - check_collisions_minions, - decay_life, - explosion_damage, - ), - ) - .add_systems(PostUpdate, (destroy_minions, destroy_after_timer)); + app.add_systems(Startup, setup_audio) + .add_systems( + Update, + ( + update_move_minions, + check_collisions_minions, + decay_life, + explosion_damage, + ), + ) + .add_systems(PostUpdate, (destroy_minions, destroy_after_timer)); } } +fn setup_audio(mut commands: Commands, server: Res) { + let handle = server.load(EXPLOSION_AUDIO_ID); + commands.insert_resource(AudioExplosion(handle)); +} + #[derive(Bundle)] pub struct MinionBundle { minion: Minion, @@ -111,12 +122,14 @@ struct ExplosionBundle { sensor: Sensor, collider: Collider, timer_destroyable: TimeDestroyable, + audio: AudioBundle, } impl ExplosionBundle { pub fn new( meshes: &mut ResMut>, materials: &mut ResMut>, + audio_asset: &Handle, mut translation: Vec3, team: Team, ) -> Self { @@ -138,6 +151,10 @@ impl ExplosionBundle { timer_destroyable: TimeDestroyable { timer: Timer::from_seconds(0.2, bevy::time::TimerMode::Once), }, + audio: AudioBundle { + source: audio_asset.clone(), + settings: PlaybackSettings::ONCE.with_spatial(true), + }, } } } @@ -239,6 +256,7 @@ fn check_collisions_minions( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, + audio_explosion: Res, mut collision_events: EventReader, // queries mut query_minions: Query<(&Transform, &Team, &mut Minion), With>, @@ -268,6 +286,7 @@ fn check_collisions_minions( commands.spawn(ExplosionBundle::new( &mut meshes, &mut materials, + &audio_explosion.0, transform_a.translation, team_a.clone(), )); @@ -305,6 +324,7 @@ fn check_collisions_minions( commands.spawn(ExplosionBundle::new( &mut meshes, &mut materials, + &audio_explosion.0, minion_transform.translation, minion_team.clone(), )); diff --git a/web/index.html b/web/index.html index 9c6ec15..8f4124b 100644 --- a/web/index.html +++ b/web/index.html @@ -1,50 +1,70 @@ - + + An other bevy 2D game

An other bevy 2D game

+ -