From 6e5955f162ebdd324eae43f8c19c219dcb8ab407 Mon Sep 17 00:00:00 2001 From: Charles Date: Thu, 14 Apr 2022 20:20:38 +0000 Subject: [PATCH] Add simple collision sound to breakout (#4331) # Objective - Add sound effect to the breakout example ## Solution - Add a collision event and a system that listens to the event and plays a sound I only added a single sound for all collisions for the sake of simplicity, but this could easily be extended to play a different sound depending on the type of entity hit. The sound was generated randomly by using https://sfxr.me https://sfxr.me/#11111GA9soYREjtsWhzjPrpMDEYSjX8Fo1E6PnKhxdw6tu869XW4EAc3nzpKVAYLMzToNcHQtQjeBqjZukqPmMDToGdYQQCWBnC3nEYfp53se5ep9btxRdLK Closes #4326 https://user-images.githubusercontent.com/8348954/160154727-00e30743-3385-4c2f-97f0-1aaaf9a4dcc5.mp4 For some reason the video has a lot of delay in the sound, but when playing it locally there's no delay --- ## Changelog - Added sound to breakout example - Added bevy_audio and vorbis to the feature list ran for examples in CI ## Migration Guide N/A --- .github/workflows/ci.yml | 4 ++-- assets/sounds/breakout_collision.ogg | Bin 0 -> 4930 bytes examples/games/breakout.rs | 31 +++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 assets/sounds/breakout_collision.ogg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 878df8f026..428b0e359f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -274,13 +274,13 @@ jobs: toolchain: stable - name: Build bevy run: | - cargo build --no-default-features --features "bevy_dynamic_plugin,bevy_gilrs,bevy_gltf,bevy_winit,render,png,hdr,x11,bevy_ci_testing,trace,trace_chrome" + cargo build --no-default-features --features "bevy_dynamic_plugin,bevy_gilrs,bevy_gltf,bevy_winit,render,png,hdr,x11,bevy_ci_testing,trace,trace_chrome,bevy_audio,vorbis" - name: Run examples run: | for example in .github/example-run/*.ron; do example_name=`basename $example .ron` echo "running $example_name - "`date` - time CI_TESTING_CONFIG=$example xvfb-run cargo run --example $example_name --no-default-features --features "bevy_dynamic_plugin,bevy_gilrs,bevy_gltf,bevy_winit,render,png,hdr,x11,bevy_ci_testing,trace,trace_chrome" + time CI_TESTING_CONFIG=$example xvfb-run cargo run --example $example_name --no-default-features --features "bevy_dynamic_plugin,bevy_gilrs,bevy_gltf,bevy_winit,render,png,hdr,x11,bevy_ci_testing,trace,trace_chrome,bevy_audio,vorbis" sleep 10 done zip traces.zip trace*.json diff --git a/assets/sounds/breakout_collision.ogg b/assets/sounds/breakout_collision.ogg new file mode 100644 index 0000000000000000000000000000000000000000..0211d70cfb845fc9a88e32b0caf038f435a98d29 GIT binary patch literal 4930 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 EKT_>GLI3~& literal 0 HcmV?d00001 diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index d00e5200df..d2ee2e2f71 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -58,12 +58,14 @@ fn main() { .insert_resource(Scoreboard { score: 0 }) .insert_resource(ClearColor(BACKGROUND_COLOR)) .add_startup_system(setup) + .add_event::() .add_system_set( SystemSet::new() .with_run_criteria(FixedTimestep::step(TIME_STEP as f64)) .with_system(check_for_collisions) .with_system(move_paddle.before(check_for_collisions)) - .with_system(apply_velocity.before(check_for_collisions)), + .with_system(apply_velocity.before(check_for_collisions)) + .with_system(play_collision_sound.after(check_for_collisions)), ) .add_system(update_scoreboard) .add_system(bevy::input::system::exit_on_esc_system) @@ -82,9 +84,14 @@ struct Velocity(Vec2); #[derive(Component)] struct Collider; +#[derive(Default)] +struct CollisionEvent; + #[derive(Component)] struct Brick; +struct CollisionSound(Handle); + // This bundle is a collection of the components that define a "wall" in our game #[derive(Bundle)] struct WallBundle { @@ -167,6 +174,10 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn_bundle(OrthographicCameraBundle::new_2d()); commands.spawn_bundle(UiCameraBundle::default()); + // Sound + let ball_collision_sound = asset_server.load("sounds/breakout_collision.ogg"); + commands.insert_resource(CollisionSound(ball_collision_sound)); + // Paddle let paddle_y = BOTTOM_WALL + GAP_BETWEEN_PADDLE_AND_FLOOR; @@ -268,7 +279,7 @@ fn setup(mut commands: Commands, asset_server: Res) { // the space on the top and sides of the bricks only captures a lower bound, not an exact value let center_of_bricks = (LEFT_WALL + RIGHT_WALL) / 2.0; let left_edge_of_bricks = center_of_bricks - // Space taken up by the bricks + // Space taken up by the bricks - (n_columns as f32 / 2.0 * BRICK_SIZE.x) // Space taken up by the gaps - n_vertical_gaps as f32 / 2.0 * GAP_BETWEEN_BRICKS; @@ -349,6 +360,7 @@ fn check_for_collisions( mut scoreboard: ResMut, mut ball_query: Query<(&mut Velocity, &Transform), With>, collider_query: Query<(Entity, &Transform, Option<&Brick>), With>, + mut collision_events: EventWriter, ) { let (mut ball_velocity, ball_transform) = ball_query.single_mut(); let ball_size = ball_transform.scale.truncate(); @@ -362,6 +374,9 @@ fn check_for_collisions( transform.scale.truncate(), ); if let Some(collision) = collision { + // Sends a collision event so that other systems can react to the collision + collision_events.send_default(); + // Bricks should be despawned and increment the scoreboard on collision if maybe_brick.is_some() { scoreboard.score += 1; @@ -394,3 +409,15 @@ fn check_for_collisions( } } } + +fn play_collision_sound( + mut collision_events: EventReader, + audio: Res