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
This commit is contained in:
Charles 2022-04-14 20:20:38 +00:00
parent e1c2ace1f2
commit 6e5955f162
3 changed files with 31 additions and 4 deletions

View file

@ -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

Binary file not shown.

View file

@ -58,12 +58,14 @@ fn main() {
.insert_resource(Scoreboard { score: 0 })
.insert_resource(ClearColor(BACKGROUND_COLOR))
.add_startup_system(setup)
.add_event::<CollisionEvent>()
.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<AudioSource>);
// 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<AssetServer>) {
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<AssetServer>) {
// 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<Scoreboard>,
mut ball_query: Query<(&mut Velocity, &Transform), With<Ball>>,
collider_query: Query<(Entity, &Transform, Option<&Brick>), With<Collider>>,
mut collision_events: EventWriter<CollisionEvent>,
) {
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<CollisionEvent>,
audio: Res<Audio>,
sound: Res<CollisionSound>,
) {
// Play a sound once per frame if a collision occurred. `count` consumes the
// events, preventing them from triggering a sound on the next frame.
if collision_events.iter().count() > 0 {
audio.play(sound.0.clone());
}
}