Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/audio/spatial_audio_3d.rs
6592 views
1
//! This example illustrates how to load and play an audio file, and control where the sounds seems to come from.
2
use bevy::{
3
color::palettes::basic::{BLUE, LIME, RED},
4
prelude::*,
5
time::Stopwatch,
6
};
7
8
fn main() {
9
App::new()
10
.add_plugins(DefaultPlugins)
11
.add_systems(Startup, setup)
12
.add_systems(Update, update_positions)
13
.add_systems(Update, update_listener)
14
.add_systems(Update, mute)
15
.run();
16
}
17
18
fn setup(
19
mut commands: Commands,
20
asset_server: Res<AssetServer>,
21
mut meshes: ResMut<Assets<Mesh>>,
22
mut materials: ResMut<Assets<StandardMaterial>>,
23
) {
24
// Space between the two ears
25
let gap = 4.0;
26
27
// sound emitter
28
commands.spawn((
29
Mesh3d(meshes.add(Sphere::new(0.2).mesh().uv(32, 18))),
30
MeshMaterial3d(materials.add(Color::from(BLUE))),
31
Transform::from_xyz(0.0, 0.0, 0.0),
32
Emitter::default(),
33
AudioPlayer::new(asset_server.load("sounds/Windless Slopes.ogg")),
34
PlaybackSettings::LOOP.with_spatial(true),
35
));
36
37
let listener = SpatialListener::new(gap);
38
commands.spawn((
39
Transform::default(),
40
Visibility::default(),
41
listener.clone(),
42
children![
43
// left ear indicator
44
(
45
Mesh3d(meshes.add(Cuboid::new(0.2, 0.2, 0.2))),
46
MeshMaterial3d(materials.add(Color::from(RED))),
47
Transform::from_translation(listener.left_ear_offset),
48
),
49
// right ear indicator
50
(
51
Mesh3d(meshes.add(Cuboid::new(0.2, 0.2, 0.2))),
52
MeshMaterial3d(materials.add(Color::from(LIME))),
53
Transform::from_translation(listener.right_ear_offset),
54
)
55
],
56
));
57
58
// light
59
commands.spawn((
60
DirectionalLight::default(),
61
Transform::from_xyz(4.0, 8.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
62
));
63
64
// example instructions
65
commands.spawn((
66
Text::new(
67
"Up/Down/Left/Right: Move Listener\nSpace: Toggle Emitter Movement\nM: Toggle Mute",
68
),
69
Node {
70
position_type: PositionType::Absolute,
71
bottom: px(12),
72
left: px(12),
73
..default()
74
},
75
));
76
77
// camera
78
commands.spawn((
79
Camera3d::default(),
80
Transform::from_xyz(0.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
81
));
82
}
83
84
#[derive(Component, Default)]
85
struct Emitter {
86
stopwatch: Stopwatch,
87
}
88
89
fn update_positions(
90
time: Res<Time>,
91
mut emitters: Query<(&mut Transform, &mut Emitter), With<Emitter>>,
92
keyboard: Res<ButtonInput<KeyCode>>,
93
) {
94
for (mut emitter_transform, mut emitter) in emitters.iter_mut() {
95
if keyboard.just_pressed(KeyCode::Space) {
96
if emitter.stopwatch.is_paused() {
97
emitter.stopwatch.unpause();
98
} else {
99
emitter.stopwatch.pause();
100
}
101
}
102
103
emitter.stopwatch.tick(time.delta());
104
105
if !emitter.stopwatch.is_paused() {
106
emitter_transform.translation.x = ops::sin(emitter.stopwatch.elapsed_secs()) * 3.0;
107
emitter_transform.translation.z = ops::cos(emitter.stopwatch.elapsed_secs()) * 3.0;
108
}
109
}
110
}
111
112
fn update_listener(
113
keyboard: Res<ButtonInput<KeyCode>>,
114
time: Res<Time>,
115
mut listeners: Single<&mut Transform, With<SpatialListener>>,
116
) {
117
let speed = 2.;
118
119
if keyboard.pressed(KeyCode::ArrowRight) {
120
listeners.translation.x += speed * time.delta_secs();
121
}
122
if keyboard.pressed(KeyCode::ArrowLeft) {
123
listeners.translation.x -= speed * time.delta_secs();
124
}
125
if keyboard.pressed(KeyCode::ArrowDown) {
126
listeners.translation.z += speed * time.delta_secs();
127
}
128
if keyboard.pressed(KeyCode::ArrowUp) {
129
listeners.translation.z -= speed * time.delta_secs();
130
}
131
}
132
133
fn mute(keyboard_input: Res<ButtonInput<KeyCode>>, mut sinks: Query<&mut SpatialAudioSink>) {
134
if keyboard_input.just_pressed(KeyCode::KeyM) {
135
for mut sink in sinks.iter_mut() {
136
sink.toggle_mute();
137
}
138
}
139
}
140
141