Skip to main content

SoundBuilder

Struct SoundBuilder 

Source
pub struct SoundBuilder<'a> { /* private fields */ }
Expand description

Configures a sound before playing it. Created by Audio::sound.

Implementations§

Source§

impl<'a> SoundBuilder<'a>

Source

pub fn volume(self, volume: f32) -> Self

Linear volume factor: 1.0 = as recorded (default), 0.5 = half (about -6 dB), 0.0 = silent. Values above 1.0 amplify and may distort.

Examples found in repository?
examples/platformer.rs (line 48)
19    fn new(ctx: &mut Context) -> Self {
20        let audio = &mut ctx.audio;
21        audio.register("jump", Synth::square(300.0).sweep_to(700.0).length(0.15).decay(10.0).volume(0.2));
22        audio.register("land", Synth::noise().length(0.1).decay(40.0).with(Synth::sine(90.0).length(0.1).decay(30.0)));
23        audio.register("coin", Synth::triangle(988.0).length(0.08).then(Synth::triangle(1319.0).length(0.17).decay(15.0)));
24        audio.register("run", Synth::noise().length(0.06).decay(60.0).volume(0.3).then(Synth::silence(0.09)));
25        audio.register("lift", Synth::saw(55.0).length(1.0).volume(0.1));
26        audio.register("push", Synth::noise().length(0.3).volume(0.15));
27
28        let physics = &mut ctx.physics;
29        physics.add(Body::fixed(vec3(0.0, 0.0, -0.5)).with_box(200.0, 4.0, 1.0).with_tag("ground"));
30        for (x, z) in [(8.0, 2.0), (14.0, 4.0), (20.0, 2.5)] {
31            physics.add(Body::fixed(vec3(x, 0.0, z)).with_box(4.0, 4.0, 0.5).with_tag("platform"));
32        }
33        // A lift going up and down at x = 40, and a heavy crate at x = 30.
34        let lift = physics.add(Body::kinematic(vec3(40.0, 0.0, 0.0)).with_box(3.0, 4.0, 0.5).with_tag("lift"));
35        physics.add(Body::dynamic(vec3(30.0, 0.0, 0.5)).with_box(1.0, 1.0, 1.0).with_mass(5.0).with_tag("crate"));
36        // Facing north, so x maps to left and right in the ears.
37        let player = physics
38            .add(Body::dynamic(vec3(0.0, 0.0, 1.0)).with_box(0.8, 0.8, 1.6).with_max_speed(25.0).with_tag("player"));
39
40        ctx.follow(player);
41        ctx.loop_on(lift, "lift");
42        // Coins are sensors with a looping sound, so you can hear where they are.
43        for (x, z) in [(5.0, 1.0), (14.0, 5.5), (25.0, 1.0), (40.0, 8.0)] {
44            let coin = ctx.physics.add(Body::sensor(vec3(x, 0.0, z)).with_sphere(0.6).with_tag("coin"));
45            ctx.loop_on(coin, "coin").set_volume(0.3);
46        }
47
48        let run_sound = ctx.audio.sound("run").looped().volume(0.0).play();
49        Self { player, lift, run_sound, score: 0 }
50    }
51}
52
53impl Scene for Platformer {
54    fn start(&mut self, ctx: &mut Context) {
55        ctx.speak("Collect the coins. Left and right to run, space to jump, S for score, escape to quit.");
56    }
57
58    fn update(&mut self, ctx: &mut Context) {
59        if ctx.pressed(Key::Escape) {
60            ctx.quit();
61        }
62
63        // The lift goes up for 4 seconds and down for 4 seconds.
64        let lift_up = (ctx.time.elapsed() % 8.0) < 4.0;
65        ctx.physics.set_velocity(self.lift, vec3(0.0, 0.0, if lift_up { 2.0 } else { -2.0 }));
66
67        let run = ctx.input.axis(Key::Left, Key::Right);
68        let mut velocity = ctx.physics.velocity(self.player);
69        velocity.x = run * 6.0;
70        ctx.physics.set_velocity(self.player, velocity);
71        if ctx.pressed(Key::Space) && ctx.physics.jump(self.player, 2.5) {
72            ctx.play("jump");
73        }
74        let running = ctx.physics.on_ground(self.player) && run != 0.0;
75        self.run_sound.set_volume(if running { 0.6 } else { 0.0 });
76
77        if let Some(speed) = ctx.physics.landed(self.player) {
78            ctx.audio.sound("land").volume((speed / 12.0).clamp(0.2, 1.0)).play();
79            if speed > 15.0 {
80                ctx.speak("Ouch!");
81            }
82        }
83        if let Some((other, _)) = ctx.physics.collided(self.player)
84            && ctx.physics.tag(other) == "crate"
85        {
86            ctx.play_on(other, "push");
87        }
88        if let Some(coin) = ctx.physics.entered_tag(self.player, "coin") {
89            ctx.physics.remove(coin); // its looping sound stops by itself
90            self.score += 1;
91            ctx.audio.sound("coin").pitch(1.5).play();
92            ctx.speak(format!("{} of {COINS} coins", self.score));
93            if self.score == COINS {
94                ctx.ui.message("won", "You collected every coin! Press enter to quit.");
95            }
96        }
97        if ctx.ui.closed("won") {
98            ctx.quit();
99        }
100
101        if ctx.pressed(Key::S) {
102            let x = ctx.physics.position(self.player).x;
103            ctx.speak(format!("Score {}, position {x:.0}", self.score));
104        }
105    }
Source

pub fn volume_db(self, db: f32) -> Self

Volume in decibels: 0 = original, -6 = about half, -20 = a tenth, +6 = about double.

Source

pub fn pitch(self, pitch: f32) -> Self

Playback speed, which changes pitch and length together: 1.0 = original (default), 2.0 = one octave up and twice as fast, 0.5 = one octave down and twice as long.

Examples found in repository?
examples/bell_catcher.rs (line 183)
179    fn ring(&mut self, ctx: &mut Context) {
180        // 0.0 when the bell starts falling, 1.0 when it lands.
181        let progress = 1.0 - self.left / self.fall;
182        let position = self.bell_position();
183        ctx.audio.sound("bell").at(position).pitch(1.0 + progress * 0.6).play();
184        self.next_ring = 0.12 + 0.45 * (1.0 - progress);
185    }
More examples
Hide additional examples
examples/platformer.rs (line 91)
58    fn update(&mut self, ctx: &mut Context) {
59        if ctx.pressed(Key::Escape) {
60            ctx.quit();
61        }
62
63        // The lift goes up for 4 seconds and down for 4 seconds.
64        let lift_up = (ctx.time.elapsed() % 8.0) < 4.0;
65        ctx.physics.set_velocity(self.lift, vec3(0.0, 0.0, if lift_up { 2.0 } else { -2.0 }));
66
67        let run = ctx.input.axis(Key::Left, Key::Right);
68        let mut velocity = ctx.physics.velocity(self.player);
69        velocity.x = run * 6.0;
70        ctx.physics.set_velocity(self.player, velocity);
71        if ctx.pressed(Key::Space) && ctx.physics.jump(self.player, 2.5) {
72            ctx.play("jump");
73        }
74        let running = ctx.physics.on_ground(self.player) && run != 0.0;
75        self.run_sound.set_volume(if running { 0.6 } else { 0.0 });
76
77        if let Some(speed) = ctx.physics.landed(self.player) {
78            ctx.audio.sound("land").volume((speed / 12.0).clamp(0.2, 1.0)).play();
79            if speed > 15.0 {
80                ctx.speak("Ouch!");
81            }
82        }
83        if let Some((other, _)) = ctx.physics.collided(self.player)
84            && ctx.physics.tag(other) == "crate"
85        {
86            ctx.play_on(other, "push");
87        }
88        if let Some(coin) = ctx.physics.entered_tag(self.player, "coin") {
89            ctx.physics.remove(coin); // its looping sound stops by itself
90            self.score += 1;
91            ctx.audio.sound("coin").pitch(1.5).play();
92            ctx.speak(format!("{} of {COINS} coins", self.score));
93            if self.score == COINS {
94                ctx.ui.message("won", "You collected every coin! Press enter to quit.");
95            }
96        }
97        if ctx.ui.closed("won") {
98            ctx.quit();
99        }
100
101        if ctx.pressed(Key::S) {
102            let x = ctx.physics.position(self.player).x;
103            ctx.speak(format!("Score {}, position {x:.0}", self.score));
104        }
105    }
Source

pub fn random_pitch(self, min: f32, max: f32) -> Self

Random pitch between min and max – makes repeated sounds less monotonous. 0.9, 1.1 is a good range for footsteps and hits.

Examples found in repository?
examples/bell_catcher.rs (line 249)
228    fn update(&mut self, ctx: &mut Context) {
229        if self.over {
230            if ctx.ui.closed("over") {
231                ctx.switch_scene(Title);
232            }
233            return;
234        }
235        if ctx.pressed(Key::Escape) {
236            ctx.switch_scene(Title);
237            return;
238        }
239        if ctx.pressed(Key::S) {
240            ctx.speak(format!("{} bells, {} lives", self.score, self.lives));
241        }
242        for (key, step) in [(Key::Left, -1), (Key::Right, 1)] {
243            if ctx.input.repeating(key, 0.3, 0.15) {
244                let lane = (self.lane + step).clamp(-LANES, LANES);
245                if lane == self.lane {
246                    ctx.play("edge");
247                } else {
248                    self.lane = lane;
249                    ctx.audio.sound("step").at(vec3(step as f32 * 0.5, 0.0, 0.0)).random_pitch(0.9, 1.1).play();
250                }
251            }
252        }
253
254        let dt = ctx.time.delta();
255        if self.left < 0.0 {
256            self.left += dt;
257            if self.left >= 0.0 {
258                self.drop_bell();
259            }
260            return;
261        }
262        self.left -= dt;
263        self.next_ring -= dt;
264        if self.left <= 0.0 {
265            self.land(ctx);
266        } else if self.next_ring <= 0.0 {
267            self.ring(ctx);
268        }
269    }
More examples
Hide additional examples
examples/beacon.rs (line 75)
58    fn update(&mut self, ctx: &mut Context) {
59        if ctx.pressed(Key::Escape) {
60            ctx.push_scene(Pause);
61        }
62        if ctx.pressed(Key::Left) || ctx.pressed(Key::Right) {
63            let degrees = if ctx.pressed(Key::Left) { -45.0 } else { 45.0 };
64            let facing = ctx.physics.turn(self.player, degrees);
65            ctx.speak(math::direction_name(facing));
66        }
67
68        let forward = ctx.input.repeating(Key::Up, 0.35, 0.35);
69        let backward = ctx.input.repeating(Key::Down, 0.35, 0.35);
70        if forward || backward {
71            let step = if forward { 1.0 } else { -1.0 };
72            if ctx.physics.move_forward(self.player, step).blocked {
73                ctx.play("wall");
74            } else {
75                ctx.audio.sound("step").random_pitch(0.85, 1.15).play();
76            }
77        }
78
79        if ctx.pressed(Key::C) {
80            let position = ctx.physics.position(self.player);
81            match ctx.physics.nearest_with_tag(position, "beacon") {
82                Some(beacon) => {
83                    let text = ctx.physics.describe(self.player, beacon);
84                    ctx.speak(text);
85                }
86                None => ctx.speak("No beacons left"),
87            }
88        }
89        if ctx.pressed(Key::F) {
90            let facing = ctx.physics.facing(self.player);
91            ctx.speak(format!("Facing {}", math::direction_name(facing)));
92        }
93
94        if let Some(beacon) = ctx.physics.entered_tag(self.player, "beacon") {
95            ctx.physics.remove(beacon);
96            self.beacons.retain(|b| *b != beacon);
97            ctx.play("found");
98            if self.beacons.is_empty() {
99                self.finish(ctx);
100            } else {
101                ctx.speak(format!("Beacon found! {} left.", self.beacons.len()));
102            }
103        }
104        if ctx.ui.closed("done") {
105            ctx.quit();
106        }
107    }
Source

pub fn pan(self, pan: f32) -> Self

Stereo position for non-positioned sounds: -1.0 left, 0.0 center (default), 1.0 right. Ignored once the sound has a position (SoundBuilder::at).

Source

pub fn looped(self) -> Self

Loop endlessly.

Examples found in repository?
examples/platformer.rs (line 48)
19    fn new(ctx: &mut Context) -> Self {
20        let audio = &mut ctx.audio;
21        audio.register("jump", Synth::square(300.0).sweep_to(700.0).length(0.15).decay(10.0).volume(0.2));
22        audio.register("land", Synth::noise().length(0.1).decay(40.0).with(Synth::sine(90.0).length(0.1).decay(30.0)));
23        audio.register("coin", Synth::triangle(988.0).length(0.08).then(Synth::triangle(1319.0).length(0.17).decay(15.0)));
24        audio.register("run", Synth::noise().length(0.06).decay(60.0).volume(0.3).then(Synth::silence(0.09)));
25        audio.register("lift", Synth::saw(55.0).length(1.0).volume(0.1));
26        audio.register("push", Synth::noise().length(0.3).volume(0.15));
27
28        let physics = &mut ctx.physics;
29        physics.add(Body::fixed(vec3(0.0, 0.0, -0.5)).with_box(200.0, 4.0, 1.0).with_tag("ground"));
30        for (x, z) in [(8.0, 2.0), (14.0, 4.0), (20.0, 2.5)] {
31            physics.add(Body::fixed(vec3(x, 0.0, z)).with_box(4.0, 4.0, 0.5).with_tag("platform"));
32        }
33        // A lift going up and down at x = 40, and a heavy crate at x = 30.
34        let lift = physics.add(Body::kinematic(vec3(40.0, 0.0, 0.0)).with_box(3.0, 4.0, 0.5).with_tag("lift"));
35        physics.add(Body::dynamic(vec3(30.0, 0.0, 0.5)).with_box(1.0, 1.0, 1.0).with_mass(5.0).with_tag("crate"));
36        // Facing north, so x maps to left and right in the ears.
37        let player = physics
38            .add(Body::dynamic(vec3(0.0, 0.0, 1.0)).with_box(0.8, 0.8, 1.6).with_max_speed(25.0).with_tag("player"));
39
40        ctx.follow(player);
41        ctx.loop_on(lift, "lift");
42        // Coins are sensors with a looping sound, so you can hear where they are.
43        for (x, z) in [(5.0, 1.0), (14.0, 5.5), (25.0, 1.0), (40.0, 8.0)] {
44            let coin = ctx.physics.add(Body::sensor(vec3(x, 0.0, z)).with_sphere(0.6).with_tag("coin"));
45            ctx.loop_on(coin, "coin").set_volume(0.3);
46        }
47
48        let run_sound = ctx.audio.sound("run").looped().volume(0.0).play();
49        Self { player, lift, run_sound, score: 0 }
50    }
Source

pub fn at(self, position: Vec3) -> Self

Place the sound in the world. It is panned and attenuated relative to the listener.

Examples found in repository?
examples/bell_catcher.rs (line 183)
179    fn ring(&mut self, ctx: &mut Context) {
180        // 0.0 when the bell starts falling, 1.0 when it lands.
181        let progress = 1.0 - self.left / self.fall;
182        let position = self.bell_position();
183        ctx.audio.sound("bell").at(position).pitch(1.0 + progress * 0.6).play();
184        self.next_ring = 0.12 + 0.45 * (1.0 - progress);
185    }
186
187    fn land(&mut self, ctx: &mut Context) {
188        if self.lane == self.bell_lane {
189            self.score += 1;
190            self.fall = (self.fall * 0.93).max(FASTEST_FALL);
191            ctx.play("catch");
192            if self.score.is_multiple_of(5) {
193                ctx.speak(format!("{} bells!", self.score));
194            }
195        } else {
196            self.lives -= 1;
197            ctx.play("miss");
198            match self.lives {
199                0 => return self.game_over(ctx),
200                1 => ctx.speak("Missed! Last life."),
201                n => ctx.speak(format!("Missed! {n} lives left.")),
202            }
203        }
204        // A short break before the next bell.
205        self.left = -0.8;
206    }
207
208    fn game_over(&mut self, ctx: &mut Context) {
209        self.over = true;
210        let best: u32 = ctx.storage.get_or("best", 0);
211        let text = if self.score > best {
212            ctx.storage.set("best", self.score);
213            format!("Game over. {} bells, a new best score!", self.score)
214        } else {
215            format!("Game over. {} bells. Your best is {best}.", self.score)
216        };
217        ctx.ui.message("over", text);
218    }
219}
220
221impl Scene for Game {
222    fn start(&mut self, ctx: &mut Context) {
223        ctx.speak("Catch the bells! Left and right to move.");
224        // Give the player a moment to listen to the first bell.
225        self.left = -1.5;
226    }
227
228    fn update(&mut self, ctx: &mut Context) {
229        if self.over {
230            if ctx.ui.closed("over") {
231                ctx.switch_scene(Title);
232            }
233            return;
234        }
235        if ctx.pressed(Key::Escape) {
236            ctx.switch_scene(Title);
237            return;
238        }
239        if ctx.pressed(Key::S) {
240            ctx.speak(format!("{} bells, {} lives", self.score, self.lives));
241        }
242        for (key, step) in [(Key::Left, -1), (Key::Right, 1)] {
243            if ctx.input.repeating(key, 0.3, 0.15) {
244                let lane = (self.lane + step).clamp(-LANES, LANES);
245                if lane == self.lane {
246                    ctx.play("edge");
247                } else {
248                    self.lane = lane;
249                    ctx.audio.sound("step").at(vec3(step as f32 * 0.5, 0.0, 0.0)).random_pitch(0.9, 1.1).play();
250                }
251            }
252        }
253
254        let dt = ctx.time.delta();
255        if self.left < 0.0 {
256            self.left += dt;
257            if self.left >= 0.0 {
258                self.drop_bell();
259            }
260            return;
261        }
262        self.left -= dt;
263        self.next_ring -= dt;
264        if self.left <= 0.0 {
265            self.land(ctx);
266        } else if self.next_ring <= 0.0 {
267            self.ring(ctx);
268        }
269    }
Source

pub fn distance(self, min: f32, max: f32) -> Self

Audible range: full volume up to min meters, silent from max meters. Default 1.0 and 30.0 (see Audio::set_default_distance).

Source

pub fn rolloff(self, rolloff: f32) -> Self

How quickly volume drops with distance (see Audio::set_default_rolloff).

Source

pub fn group(self, group: Group) -> Self

Volume group (default Group::Sound).

Source

pub fn fade_in(self, seconds: f32) -> Self

Fades in from silence to the volume over the given seconds.

Source

pub fn start_at(self, seconds: f32) -> Self

Start playback this many seconds into the sound.

Source

pub fn paused(self) -> Self

Create the sound paused; call Sound::resume to start it.

Source

pub fn play(self) -> Sound

Plays the sound and returns its handle.

Examples found in repository?
examples/bell_catcher.rs (line 183)
179    fn ring(&mut self, ctx: &mut Context) {
180        // 0.0 when the bell starts falling, 1.0 when it lands.
181        let progress = 1.0 - self.left / self.fall;
182        let position = self.bell_position();
183        ctx.audio.sound("bell").at(position).pitch(1.0 + progress * 0.6).play();
184        self.next_ring = 0.12 + 0.45 * (1.0 - progress);
185    }
186
187    fn land(&mut self, ctx: &mut Context) {
188        if self.lane == self.bell_lane {
189            self.score += 1;
190            self.fall = (self.fall * 0.93).max(FASTEST_FALL);
191            ctx.play("catch");
192            if self.score.is_multiple_of(5) {
193                ctx.speak(format!("{} bells!", self.score));
194            }
195        } else {
196            self.lives -= 1;
197            ctx.play("miss");
198            match self.lives {
199                0 => return self.game_over(ctx),
200                1 => ctx.speak("Missed! Last life."),
201                n => ctx.speak(format!("Missed! {n} lives left.")),
202            }
203        }
204        // A short break before the next bell.
205        self.left = -0.8;
206    }
207
208    fn game_over(&mut self, ctx: &mut Context) {
209        self.over = true;
210        let best: u32 = ctx.storage.get_or("best", 0);
211        let text = if self.score > best {
212            ctx.storage.set("best", self.score);
213            format!("Game over. {} bells, a new best score!", self.score)
214        } else {
215            format!("Game over. {} bells. Your best is {best}.", self.score)
216        };
217        ctx.ui.message("over", text);
218    }
219}
220
221impl Scene for Game {
222    fn start(&mut self, ctx: &mut Context) {
223        ctx.speak("Catch the bells! Left and right to move.");
224        // Give the player a moment to listen to the first bell.
225        self.left = -1.5;
226    }
227
228    fn update(&mut self, ctx: &mut Context) {
229        if self.over {
230            if ctx.ui.closed("over") {
231                ctx.switch_scene(Title);
232            }
233            return;
234        }
235        if ctx.pressed(Key::Escape) {
236            ctx.switch_scene(Title);
237            return;
238        }
239        if ctx.pressed(Key::S) {
240            ctx.speak(format!("{} bells, {} lives", self.score, self.lives));
241        }
242        for (key, step) in [(Key::Left, -1), (Key::Right, 1)] {
243            if ctx.input.repeating(key, 0.3, 0.15) {
244                let lane = (self.lane + step).clamp(-LANES, LANES);
245                if lane == self.lane {
246                    ctx.play("edge");
247                } else {
248                    self.lane = lane;
249                    ctx.audio.sound("step").at(vec3(step as f32 * 0.5, 0.0, 0.0)).random_pitch(0.9, 1.1).play();
250                }
251            }
252        }
253
254        let dt = ctx.time.delta();
255        if self.left < 0.0 {
256            self.left += dt;
257            if self.left >= 0.0 {
258                self.drop_bell();
259            }
260            return;
261        }
262        self.left -= dt;
263        self.next_ring -= dt;
264        if self.left <= 0.0 {
265            self.land(ctx);
266        } else if self.next_ring <= 0.0 {
267            self.ring(ctx);
268        }
269    }
More examples
Hide additional examples
examples/beacon.rs (line 75)
58    fn update(&mut self, ctx: &mut Context) {
59        if ctx.pressed(Key::Escape) {
60            ctx.push_scene(Pause);
61        }
62        if ctx.pressed(Key::Left) || ctx.pressed(Key::Right) {
63            let degrees = if ctx.pressed(Key::Left) { -45.0 } else { 45.0 };
64            let facing = ctx.physics.turn(self.player, degrees);
65            ctx.speak(math::direction_name(facing));
66        }
67
68        let forward = ctx.input.repeating(Key::Up, 0.35, 0.35);
69        let backward = ctx.input.repeating(Key::Down, 0.35, 0.35);
70        if forward || backward {
71            let step = if forward { 1.0 } else { -1.0 };
72            if ctx.physics.move_forward(self.player, step).blocked {
73                ctx.play("wall");
74            } else {
75                ctx.audio.sound("step").random_pitch(0.85, 1.15).play();
76            }
77        }
78
79        if ctx.pressed(Key::C) {
80            let position = ctx.physics.position(self.player);
81            match ctx.physics.nearest_with_tag(position, "beacon") {
82                Some(beacon) => {
83                    let text = ctx.physics.describe(self.player, beacon);
84                    ctx.speak(text);
85                }
86                None => ctx.speak("No beacons left"),
87            }
88        }
89        if ctx.pressed(Key::F) {
90            let facing = ctx.physics.facing(self.player);
91            ctx.speak(format!("Facing {}", math::direction_name(facing)));
92        }
93
94        if let Some(beacon) = ctx.physics.entered_tag(self.player, "beacon") {
95            ctx.physics.remove(beacon);
96            self.beacons.retain(|b| *b != beacon);
97            ctx.play("found");
98            if self.beacons.is_empty() {
99                self.finish(ctx);
100            } else {
101                ctx.speak(format!("Beacon found! {} left.", self.beacons.len()));
102            }
103        }
104        if ctx.ui.closed("done") {
105            ctx.quit();
106        }
107    }
examples/platformer.rs (line 48)
19    fn new(ctx: &mut Context) -> Self {
20        let audio = &mut ctx.audio;
21        audio.register("jump", Synth::square(300.0).sweep_to(700.0).length(0.15).decay(10.0).volume(0.2));
22        audio.register("land", Synth::noise().length(0.1).decay(40.0).with(Synth::sine(90.0).length(0.1).decay(30.0)));
23        audio.register("coin", Synth::triangle(988.0).length(0.08).then(Synth::triangle(1319.0).length(0.17).decay(15.0)));
24        audio.register("run", Synth::noise().length(0.06).decay(60.0).volume(0.3).then(Synth::silence(0.09)));
25        audio.register("lift", Synth::saw(55.0).length(1.0).volume(0.1));
26        audio.register("push", Synth::noise().length(0.3).volume(0.15));
27
28        let physics = &mut ctx.physics;
29        physics.add(Body::fixed(vec3(0.0, 0.0, -0.5)).with_box(200.0, 4.0, 1.0).with_tag("ground"));
30        for (x, z) in [(8.0, 2.0), (14.0, 4.0), (20.0, 2.5)] {
31            physics.add(Body::fixed(vec3(x, 0.0, z)).with_box(4.0, 4.0, 0.5).with_tag("platform"));
32        }
33        // A lift going up and down at x = 40, and a heavy crate at x = 30.
34        let lift = physics.add(Body::kinematic(vec3(40.0, 0.0, 0.0)).with_box(3.0, 4.0, 0.5).with_tag("lift"));
35        physics.add(Body::dynamic(vec3(30.0, 0.0, 0.5)).with_box(1.0, 1.0, 1.0).with_mass(5.0).with_tag("crate"));
36        // Facing north, so x maps to left and right in the ears.
37        let player = physics
38            .add(Body::dynamic(vec3(0.0, 0.0, 1.0)).with_box(0.8, 0.8, 1.6).with_max_speed(25.0).with_tag("player"));
39
40        ctx.follow(player);
41        ctx.loop_on(lift, "lift");
42        // Coins are sensors with a looping sound, so you can hear where they are.
43        for (x, z) in [(5.0, 1.0), (14.0, 5.5), (25.0, 1.0), (40.0, 8.0)] {
44            let coin = ctx.physics.add(Body::sensor(vec3(x, 0.0, z)).with_sphere(0.6).with_tag("coin"));
45            ctx.loop_on(coin, "coin").set_volume(0.3);
46        }
47
48        let run_sound = ctx.audio.sound("run").looped().volume(0.0).play();
49        Self { player, lift, run_sound, score: 0 }
50    }
51}
52
53impl Scene for Platformer {
54    fn start(&mut self, ctx: &mut Context) {
55        ctx.speak("Collect the coins. Left and right to run, space to jump, S for score, escape to quit.");
56    }
57
58    fn update(&mut self, ctx: &mut Context) {
59        if ctx.pressed(Key::Escape) {
60            ctx.quit();
61        }
62
63        // The lift goes up for 4 seconds and down for 4 seconds.
64        let lift_up = (ctx.time.elapsed() % 8.0) < 4.0;
65        ctx.physics.set_velocity(self.lift, vec3(0.0, 0.0, if lift_up { 2.0 } else { -2.0 }));
66
67        let run = ctx.input.axis(Key::Left, Key::Right);
68        let mut velocity = ctx.physics.velocity(self.player);
69        velocity.x = run * 6.0;
70        ctx.physics.set_velocity(self.player, velocity);
71        if ctx.pressed(Key::Space) && ctx.physics.jump(self.player, 2.5) {
72            ctx.play("jump");
73        }
74        let running = ctx.physics.on_ground(self.player) && run != 0.0;
75        self.run_sound.set_volume(if running { 0.6 } else { 0.0 });
76
77        if let Some(speed) = ctx.physics.landed(self.player) {
78            ctx.audio.sound("land").volume((speed / 12.0).clamp(0.2, 1.0)).play();
79            if speed > 15.0 {
80                ctx.speak("Ouch!");
81            }
82        }
83        if let Some((other, _)) = ctx.physics.collided(self.player)
84            && ctx.physics.tag(other) == "crate"
85        {
86            ctx.play_on(other, "push");
87        }
88        if let Some(coin) = ctx.physics.entered_tag(self.player, "coin") {
89            ctx.physics.remove(coin); // its looping sound stops by itself
90            self.score += 1;
91            ctx.audio.sound("coin").pitch(1.5).play();
92            ctx.speak(format!("{} of {COINS} coins", self.score));
93            if self.score == COINS {
94                ctx.ui.message("won", "You collected every coin! Press enter to quit.");
95            }
96        }
97        if ctx.ui.closed("won") {
98            ctx.quit();
99        }
100
101        if ctx.pressed(Key::S) {
102            let x = ctx.physics.position(self.player).x;
103            ctx.speak(format!("Score {}, position {x:.0}", self.score));
104        }
105    }

Auto Trait Implementations§

§

impl<'a> !RefUnwindSafe for SoundBuilder<'a>

§

impl<'a> !UnwindSafe for SoundBuilder<'a>

§

impl<'a> Freeze for SoundBuilder<'a>

§

impl<'a> Send for SoundBuilder<'a>

§

impl<'a> Sync for SoundBuilder<'a>

§

impl<'a> Unpin for SoundBuilder<'a>

§

impl<'a> UnsafeUnpin for SoundBuilder<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more