-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Programmed soundtrack example (#12774)
Created soundtrack example, fade-in and fade-out features, added new assets, and updated credits. # Objective - Fixes #12651 ## Solution - Created a resource to hold the track list. - The audio assets are then loaded by the asset server and added to the track list. - Once the game is in a specific state, an `AudioBundle` is spawned and plays the appropriate track. - The audio volume starts at zero and is then incremented gradually until it reaches full volume. - Once the game state changes, the current track fades out, and a new one fades in at the same time, offering a relatively seamless transition. - Once a track is completely faded out, it is despawned from the app. - Game state changes are simulated through a `Timer` for simplicity. - Track change system is only run if there is a change in the `GameState` resource. - All tracks are used according to their respective licenses. --------- Co-authored-by: Alice Cecile <[email protected]>
- Loading branch information
1 parent
622f9a3
commit df76fd4
Showing
6 changed files
with
171 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
//! This example illustrates how to load and play different soundtracks, | ||
//! transitioning between them as the game state changes. | ||
|
||
use bevy::prelude::*; | ||
|
||
fn main() { | ||
App::new() | ||
.add_plugins(DefaultPlugins) | ||
.add_systems(Startup, setup) | ||
.add_systems(Update, (cycle_game_state, fade_in, fade_out)) | ||
.add_systems(Update, change_track) | ||
.run(); | ||
} | ||
|
||
// This resource simulates game states | ||
#[derive(Resource, Default)] | ||
enum GameState { | ||
#[default] | ||
Peaceful, | ||
Battle, | ||
} | ||
|
||
// This timer simulates game state changes | ||
#[derive(Resource)] | ||
struct GameStateTimer(Timer); | ||
|
||
// This resource will hold the track list for your soundtrack | ||
#[derive(Resource)] | ||
struct SoundtrackPlayer { | ||
track_list: Vec<Handle<AudioSource>>, | ||
} | ||
|
||
impl SoundtrackPlayer { | ||
fn new(track_list: Vec<Handle<AudioSource>>) -> Self { | ||
Self { track_list } | ||
} | ||
} | ||
|
||
// This component will be attached to an entity to fade the audio in | ||
#[derive(Component)] | ||
struct FadeIn; | ||
|
||
// This component will be attached to an entity to fade the audio out | ||
#[derive(Component)] | ||
struct FadeOut; | ||
|
||
fn setup(asset_server: Res<AssetServer>, mut commands: Commands) { | ||
// Instantiate the game state resources | ||
commands.insert_resource(GameState::default()); | ||
commands.insert_resource(GameStateTimer(Timer::from_seconds( | ||
10.0, | ||
TimerMode::Repeating, | ||
))); | ||
|
||
// Create the track list | ||
let track_1 = asset_server.load::<AudioSource>("sounds/Mysterious acoustic guitar.ogg"); | ||
let track_2 = asset_server.load::<AudioSource>("sounds/Epic orchestra music.ogg"); | ||
let track_list = vec![track_1, track_2]; | ||
commands.insert_resource(SoundtrackPlayer::new(track_list)); | ||
} | ||
|
||
// Every time the GameState resource changes, this system is run to trigger the song change. | ||
fn change_track( | ||
mut commands: Commands, | ||
soundtrack_player: Res<SoundtrackPlayer>, | ||
soundtrack: Query<Entity, With<AudioSink>>, | ||
game_state: Res<GameState>, | ||
) { | ||
if game_state.is_changed() { | ||
// Fade out all currently running tracks | ||
for track in soundtrack.iter() { | ||
commands.entity(track).insert(FadeOut); | ||
} | ||
|
||
// Spawn a new `AudioBundle` with the appropriate soundtrack based on | ||
// the game state. | ||
// | ||
// Volume is set to start at zero and is then increased by the fade_in system. | ||
match game_state.as_ref() { | ||
GameState::Peaceful => { | ||
commands.spawn(( | ||
AudioBundle { | ||
source: soundtrack_player.track_list.first().unwrap().clone(), | ||
settings: PlaybackSettings { | ||
mode: bevy::audio::PlaybackMode::Loop, | ||
volume: bevy::audio::Volume::ZERO, | ||
..default() | ||
}, | ||
}, | ||
FadeIn, | ||
)); | ||
} | ||
GameState::Battle => { | ||
commands.spawn(( | ||
AudioBundle { | ||
source: soundtrack_player.track_list.get(1).unwrap().clone(), | ||
settings: PlaybackSettings { | ||
mode: bevy::audio::PlaybackMode::Loop, | ||
volume: bevy::audio::Volume::ZERO, | ||
..default() | ||
}, | ||
}, | ||
FadeIn, | ||
)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Fade effect duration | ||
const FADE_TIME: f32 = 2.0; | ||
|
||
// Fades in the audio of entities that has the FadeIn component. Removes the FadeIn component once | ||
// full volume is reached. | ||
fn fade_in( | ||
mut commands: Commands, | ||
mut audio_sink: Query<(&mut AudioSink, Entity), With<FadeIn>>, | ||
time: Res<Time>, | ||
) { | ||
for (audio, entity) in audio_sink.iter_mut() { | ||
audio.set_volume(audio.volume() + time.delta_seconds() / FADE_TIME); | ||
if audio.volume() >= 1.0 { | ||
audio.set_volume(1.0); | ||
commands.entity(entity).remove::<FadeIn>(); | ||
} | ||
} | ||
} | ||
|
||
// Fades out the audio of entities that has the FadeOut component. Despawns the entities once audio | ||
// volume reaches zero. | ||
fn fade_out( | ||
mut commands: Commands, | ||
mut audio_sink: Query<(&mut AudioSink, Entity), With<FadeOut>>, | ||
time: Res<Time>, | ||
) { | ||
for (audio, entity) in audio_sink.iter_mut() { | ||
audio.set_volume(audio.volume() - time.delta_seconds() / FADE_TIME); | ||
if audio.volume() <= 0.0 { | ||
commands.entity(entity).despawn_recursive(); | ||
} | ||
} | ||
} | ||
|
||
// Every time the timer ends, switches between the "Peaceful" and "Battle" state. | ||
fn cycle_game_state( | ||
mut timer: ResMut<GameStateTimer>, | ||
mut game_state: ResMut<GameState>, | ||
time: Res<Time>, | ||
) { | ||
timer.0.tick(time.delta()); | ||
if timer.0.just_finished() { | ||
match game_state.as_ref() { | ||
GameState::Battle => *game_state = GameState::Peaceful, | ||
GameState::Peaceful => *game_state = GameState::Battle, | ||
} | ||
} | ||
} |