Skip to content
Snippets Groups Projects
Louis Capitanchik's avatar
Louis authored
7f8625fa

Micro Bevy MusicBox

Play some tunes

What?

This library provides a convenience wrapper around bevy_kira_audio, handling all of the setup for the common game audio scenario for you.

There are 4 types of audio channel that will be added to your game, with crossfade tracks for 2 of those; this means a total of 6 AudioChannel resources are created.

  • Music - The main music for your game (includes A/B for crossfading)
  • Ambient - Any long and/or looping background ambience that you might want to layer on top of music (includes A/B for crossfading)
  • Sfx - Any short sound effects
  • UI Sfx - Any sound effects that specifically relate to the UI, allowing for more fine-grained control of audio levels

Track volume is controlled by a resource, and is calculated as the individual track's volume settings multiplied by the master volume setting

How?

Quickstart

  • Include the MusixBocPlugin plugin, or the CombinedAudioPlugins plugin group in your app
  • Implement SuppliesAudio for a resource (or use the built in impl on AssetServer)
  • Use MusicBox<T: SuppliesAudio> as a parameter on a system
  • Call one of the MusicBox::play_* methods to play sounds
fn main() {
    App::new()
        .add_plugins(CombinedAudioPlugins)
        .add_startup_system(|mut music_box: MusicBox<AssetServer>| {
            music_box.play_music("music/bing_bong.mp3");
        });
}

In-Depth

There is a plugin that just provides the layering on top of bevy_kira_audio if you are already integrating with it, and a plugin group that will setup and configure bevy_kira_audio for you if not. Some bevy_ira_audio types are re-exported, so you can depend solely on micro_bevy_musicbox if you don't have any advanced needs.

use micro_bevy_musicbox::{CombinedAudioPlugins, MusicBoxPlugin};

// Either
fn main() {
    App::new()
        .add_plugin(MusicBoxPlugin);
}

// Or
fn main() {
    App::new()
        .add_plugins(CombinedAudioPlugins);
}

In order to use the the MusicBox type, you will need to implement the SuppliesAudio trait on a resource. This resource will need to be able to retrieve the requested audio track by name, although the specifics of what that name represents will depend on how you implement the trait.

Out of the box, there is a SuppliesAudio impl for AssetServer, allowing you to use resource paths. It is, however, recommended that you create your own impl for your own resource type.

/// An example storage resource that allows assets to be retrieved by name,
/// rather than by file path
pub struct AssetHandles {
    // ...Other types...
    pub sounds: HashMap<String, Handle<AudioSource>>,
}

impl SuppliesAudio for AssetHandles {
	fn get_audio_track<T: ToString>(&self, name: T) -> Option<Handle<AudioSource>> {
		self.sounds.get(&name.to_string()).map(Handle::clone_weak)
	}
}

Finally, you need to use the MusicBox type as a SystemParam, alongside your SuppliesAudio impl. There is no binding of between MusicBox and SuppliesAudio, so you can use different impls in different systems as you please, as long as the SuppliesAudio resource has been added to your world

/// A simple event that causes a sound effect to be played.
/// N.B. In a real game, you probably just want to directly play a sound without events
pub struct PlaySoundEvent {
    pub sound: String,
}

// ...Register your event to your app...

/// A system that reads PlaySoundEvent events and plays the sound effect, using the
/// previously created `AssetHandles` resource as a supplier
pub fn play_sounds(
    mut events: EventReader<PlaySoundEvent>,
    music_box: MusicBox<AssetHandles>,
) {
    for PlaySoundEvent { sound } in events.iter() {
        music_box.play_effect_once(sound);
    }
}