Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/audio/soundtrack.rs
9359 views
1
//! This example illustrates how to load and play different soundtracks,
2
//! transitioning between them as the game state changes.
3
4
use bevy::{audio::Volume, prelude::*};
5
6
fn main() {
7
App::new()
8
.add_plugins(DefaultPlugins)
9
.add_systems(Startup, setup)
10
.add_systems(Update, (cycle_game_state, fade_in, fade_out))
11
.add_systems(Update, change_track)
12
.run();
13
}
14
15
// This resource simulates game states
16
#[derive(Resource, Default)]
17
enum GameState {
18
#[default]
19
Peaceful,
20
Battle,
21
}
22
23
// This timer simulates game state changes
24
#[derive(Resource)]
25
struct GameStateTimer(Timer);
26
27
// This resource will hold the track list for your soundtrack
28
#[derive(Resource)]
29
struct SoundtrackPlayer {
30
track_list: Vec<Handle<AudioSource>>,
31
}
32
33
impl SoundtrackPlayer {
34
fn new(track_list: Vec<Handle<AudioSource>>) -> Self {
35
Self { track_list }
36
}
37
}
38
39
// This component will be attached to an entity to fade the audio in
40
#[derive(Component)]
41
struct FadeIn;
42
43
// This component will be attached to an entity to fade the audio out
44
#[derive(Component)]
45
struct FadeOut;
46
47
fn setup(asset_server: Res<AssetServer>, mut commands: Commands) {
48
// Instantiate the game state resources
49
commands.insert_resource(GameState::default());
50
commands.insert_resource(GameStateTimer(Timer::from_seconds(
51
10.0,
52
TimerMode::Repeating,
53
)));
54
55
// Create the track list
56
let track_1 = asset_server.load::<AudioSource>("sounds/Mysterious acoustic guitar.ogg");
57
let track_2 = asset_server.load::<AudioSource>("sounds/Epic orchestra music.ogg");
58
let track_list = vec![track_1, track_2];
59
commands.insert_resource(SoundtrackPlayer::new(track_list));
60
}
61
62
// Every time the GameState resource changes, this system is run to trigger the song change.
63
fn change_track(
64
mut commands: Commands,
65
soundtrack_player: Res<SoundtrackPlayer>,
66
soundtrack: Query<Entity, With<AudioSink>>,
67
game_state: Res<GameState>,
68
) {
69
if game_state.is_changed() {
70
// Fade out all currently running tracks
71
for track in soundtrack.iter() {
72
commands.entity(track).insert(FadeOut);
73
}
74
75
// Spawn a new `AudioPlayer` with the appropriate soundtrack based on
76
// the game state.
77
//
78
// Volume is set to start at zero and is then increased by the fade_in system.
79
let track = match game_state.as_ref() {
80
GameState::Peaceful => soundtrack_player.track_list.first().unwrap().clone(),
81
GameState::Battle => soundtrack_player.track_list.get(1).unwrap().clone(),
82
};
83
84
commands.spawn((
85
AudioPlayer(track),
86
PlaybackSettings {
87
mode: bevy::audio::PlaybackMode::Loop,
88
volume: Volume::SILENT,
89
..default()
90
},
91
FadeIn,
92
));
93
}
94
}
95
96
// Fade effect duration
97
const FADE_TIME: f32 = 2.0;
98
99
// Fades in the audio of entities that has the FadeIn component. Removes the FadeIn component once
100
// full volume is reached.
101
fn fade_in(
102
mut commands: Commands,
103
mut audio_sink: Query<(&mut AudioSink, Entity), With<FadeIn>>,
104
timer: Res<GameStateTimer>,
105
) {
106
for (mut audio, entity) in audio_sink.iter_mut() {
107
audio.set_volume(
108
Volume::SILENT.fade_towards(Volume::Linear(1.0), timer.0.elapsed_secs() / FADE_TIME),
109
);
110
if timer.0.elapsed_secs() >= FADE_TIME {
111
audio.set_volume(Volume::Linear(1.0));
112
commands.entity(entity).remove::<FadeIn>();
113
}
114
}
115
}
116
117
// Fades out the audio of entities that has the FadeOut component. Despawns the entities once audio
118
// volume reaches zero.
119
fn fade_out(
120
mut commands: Commands,
121
mut audio_sink: Query<(&mut AudioSink, Entity), With<FadeOut>>,
122
timer: Res<GameStateTimer>,
123
) {
124
for (mut audio, entity) in audio_sink.iter_mut() {
125
audio.set_volume(
126
Volume::Linear(1.0).fade_towards(Volume::SILENT, timer.0.elapsed_secs() / FADE_TIME),
127
);
128
if timer.0.elapsed_secs() >= FADE_TIME {
129
commands.entity(entity).despawn();
130
}
131
}
132
}
133
134
// Every time the timer ends, switches between the "Peaceful" and "Battle" state.
135
fn cycle_game_state(
136
mut timer: ResMut<GameStateTimer>,
137
mut game_state: ResMut<GameState>,
138
time: Res<Time>,
139
) {
140
timer.0.tick(time.delta());
141
if timer.0.just_finished() {
142
match game_state.as_ref() {
143
GameState::Battle => *game_state = GameState::Peaceful,
144
GameState::Peaceful => *game_state = GameState::Battle,
145
}
146
}
147
}
148
149