Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/audio/soundtrack.rs
6592 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
match game_state.as_ref() {
80
GameState::Peaceful => {
81
commands.spawn((
82
AudioPlayer(soundtrack_player.track_list.first().unwrap().clone()),
83
PlaybackSettings {
84
mode: bevy::audio::PlaybackMode::Loop,
85
volume: Volume::SILENT,
86
..default()
87
},
88
FadeIn,
89
));
90
}
91
GameState::Battle => {
92
commands.spawn((
93
AudioPlayer(soundtrack_player.track_list.get(1).unwrap().clone()),
94
PlaybackSettings {
95
mode: bevy::audio::PlaybackMode::Loop,
96
volume: Volume::SILENT,
97
..default()
98
},
99
FadeIn,
100
));
101
}
102
}
103
}
104
}
105
106
// Fade effect duration
107
const FADE_TIME: f32 = 2.0;
108
109
// Fades in the audio of entities that has the FadeIn component. Removes the FadeIn component once
110
// full volume is reached.
111
fn fade_in(
112
mut commands: Commands,
113
mut audio_sink: Query<(&mut AudioSink, Entity), With<FadeIn>>,
114
time: Res<Time>,
115
) {
116
for (mut audio, entity) in audio_sink.iter_mut() {
117
let current_volume = audio.volume();
118
audio.set_volume(
119
current_volume.fade_towards(Volume::Linear(1.0), time.delta_secs() / FADE_TIME),
120
);
121
if audio.volume().to_linear() >= 1.0 {
122
audio.set_volume(Volume::Linear(1.0));
123
commands.entity(entity).remove::<FadeIn>();
124
}
125
}
126
}
127
128
// Fades out the audio of entities that has the FadeOut component. Despawns the entities once audio
129
// volume reaches zero.
130
fn fade_out(
131
mut commands: Commands,
132
mut audio_sink: Query<(&mut AudioSink, Entity), With<FadeOut>>,
133
time: Res<Time>,
134
) {
135
for (mut audio, entity) in audio_sink.iter_mut() {
136
let current_volume = audio.volume();
137
audio.set_volume(
138
current_volume.fade_towards(Volume::Linear(0.0), time.delta_secs() / FADE_TIME),
139
);
140
if audio.volume().to_linear() <= 0.0 {
141
commands.entity(entity).despawn();
142
}
143
}
144
}
145
146
// Every time the timer ends, switches between the "Peaceful" and "Battle" state.
147
fn cycle_game_state(
148
mut timer: ResMut<GameStateTimer>,
149
mut game_state: ResMut<GameState>,
150
time: Res<Time>,
151
) {
152
timer.0.tick(time.delta());
153
if timer.0.just_finished() {
154
match game_state.as_ref() {
155
GameState::Battle => *game_state = GameState::Peaceful,
156
GameState::Peaceful => *game_state = GameState::Battle,
157
}
158
}
159
}
160
161