Skip to main content

Context

Struct Context 

Source
pub struct Context {
    pub time: Time,
    pub input: Input,
    pub audio: Audio,
    pub speech: Speech,
    pub ui: Ui,
    pub physics: World,
    pub storage: Storage,
    pub lang: Lang,
    /* private fields */
}
Expand description

Everything your game can use, passed to every Scene method.

Fields§

§time: Time

Frame timing and scheduling.

§input: Input

Keyboard state.

§audio: Audio

Sound effects, positioned sounds and music.

§speech: Speech

Screen reader / text-to-speech output.

§ui: Ui

Virtual menus and dialogs.

§physics: World

Physics world.

§storage: Storage

Values that are saved when the game quits (high scores, settings).

§lang: Lang

Translations and the current language.

Implementations§

Source§

impl Context

Source

pub fn quit(&mut self)

Requests the game to end after this frame.

Examples found in repository?
examples/hello.rs (line 24)
14    fn update(&mut self, ctx: &mut Context) {
15        if ctx.input.pressed(Key::Space) {
16            // A pitch that rises over time, so every beep sounds different.
17            let pitch = 300.0 + (ctx.time.elapsed() as f32 * 50.0) % 900.0;
18            ctx.audio.play_tone(pitch.round(), 0.15);
19        }
20        if ctx.input.pressed(Key::F1) {
21            ctx.speech.repeat_last();
22        }
23        if ctx.input.pressed(Key::Escape) {
24            ctx.quit();
25        }
26    }
More examples
Hide additional examples
examples/clicker.rs (line 34)
8fn main() -> audio_code::Result<()> {
9    let mut score = 0;
10
11    audio_code::run("Clicker", move |ctx| {
12        if ctx.once("intro") {
13            let best: u32 = ctx.storage.get_or("best", 0);
14            ctx.speak(format!("Press space as often as you can in ten seconds. Your best is {best}. Go!"));
15            ctx.time.after("end", 10.0);
16        }
17
18        if ctx.time.is_scheduled("end") && ctx.pressed(Key::Space) {
19            score += 1;
20            ctx.audio.play_tone(300.0 + score as f32 * 15.0, 0.05);
21        }
22
23        if ctx.time.fired("end") {
24            let best: u32 = ctx.storage.get_or("best", 0);
25            if score > best {
26                ctx.storage.set("best", score);
27                ctx.ui.message("result", format!("{score} presses. New record!"));
28            } else {
29                ctx.ui.message("result", format!("{score} presses. Your best is {best}."));
30            }
31        }
32
33        if ctx.ui.closed("result") || ctx.pressed(Key::Escape) {
34            ctx.quit();
35        }
36    })
37}
examples/menus.rs (line 82)
50    fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
51        match event {
52            UiEvent::Activated { menu, item } => match (menu.as_str(), item.as_str()) {
53                ("main", "play") => {
54                    let s = ctx.get::<Settings>().expect("settings exist");
55                    let text = format!("Good luck, {}! (this is where your game would start)", s.name);
56                    ctx.ui.message("intro", text);
57                }
58                ("main", "options") => {
59                    let menu = options_menu(ctx.get::<Settings>().expect("settings exist"));
60                    ctx.ui.open(menu);
61                }
62                ("main", "name") => ctx.ui.open(TextInput::new("name", "Enter your name").max_length(20).required()),
63                ("main", "quit") => ctx.ui.open(Confirm::new("quit", "Do you really want to quit?").default_no()),
64                ("options", "back") => ctx.ui.close(),
65                _ => {}
66            },
67            UiEvent::Changed { menu, item, value } if menu == "options" => {
68                let s = ctx.get_mut::<Settings>().expect("settings exist");
69                match item.as_str() {
70                    "volume" => s.volume = value.as_f32() / 100.0,
71                    "difficulty" => s.difficulty = value.as_index(),
72                    "name" => s.name = value.as_str().to_string(),
73                    _ => {}
74                }
75                let volume = s.volume;
76                ctx.audio.set_master_volume(volume);
77            }
78            UiEvent::TextSubmitted { id, text } if id == "name" => {
79                ctx.get_mut::<Settings>().expect("settings exist").name = text.clone();
80                ctx.speech.speak_queued(format!("Hello, {text}"));
81            }
82            UiEvent::Confirmed { id, yes: true } if id == "quit" => ctx.quit(),
83            _ => {}
84        }
85    }
examples/platformer.rs (line 60)
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    }
examples/beacon.rs (line 105)
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    }
108
109    // The clock should not run while the pause menu is open.
110    fn pause(&mut self, _ctx: &mut Context) {
111        self.clock.pause();
112    }
113
114    fn resume(&mut self, _ctx: &mut Context) {
115        self.clock.resume();
116    }
117}
118
119impl Maze {
120    fn finish(&mut self, ctx: &mut Context) {
121        let seconds = self.clock.elapsed_secs().round() as u32;
122        let best: u32 = ctx.storage.get_or("best_time", u32::MAX);
123        let mut text = format!("You found every beacon in {seconds} seconds.");
124        if seconds < best {
125            ctx.storage.set("best_time", seconds);
126            text.push_str(" That is a new record!");
127        } else {
128            text.push_str(&format!(" Your record is {best} seconds."));
129        }
130        ctx.ui.message("done", text);
131    }
132}
133
134struct Pause;
135
136impl Scene for Pause {
137    fn start(&mut self, ctx: &mut Context) {
138        ctx.time.pause();
139        ctx.ui.open(Menu::new("pause", "Paused").button("resume", "Resume").button("quit", "Quit game"));
140    }
141
142    fn update(&mut self, ctx: &mut Context) {
143        if ctx.ui.clicked("pause", "resume") || ctx.ui.closed("pause") {
144            ctx.ui.close_id("pause");
145            ctx.pop_scene();
146        }
147        if ctx.ui.clicked("pause", "quit") {
148            ctx.quit();
149        }
150    }
examples/bell_catcher.rs (line 113)
92    fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
93        match event {
94            UiEvent::Activated { menu, item } if menu == "title" => match item.as_str() {
95                "play" => {
96                    ctx.ui.close_all();
97                    ctx.switch_scene(Game::default());
98                }
99                "voice" => match voice_menu(ctx) {
100                    Some(menu) => ctx.ui.open(menu),
101                    None => ctx.ui.message(
102                        "no voices",
103                        "Your screen reader speaks with its own voice. Change it in the screen reader's settings.",
104                    ),
105                },
106                "help" => ctx.ui.message(
107                    "help",
108                    "Bells fall in one of seven lanes in front of you. Listen where a bell rings, \
109                     step under it with left and right, and catch it before it lands. \
110                     The bell rings faster and higher the closer it gets. You have three lives. \
111                     Press S for your score, Escape for the menu.",
112                ),
113                "quit" => ctx.quit(),
114                _ => {}
115            },
116            UiEvent::Activated { menu, item } if menu == "voices" => {
117                if item == "back" {
118                    ctx.ui.close();
119                    return;
120                }
121                if item == "default" {
122                    ctx.speech.reset_voice();
123                    ctx.storage.remove("voice");
124                } else if let Some(voice) =
125                    item.strip_prefix("voice").and_then(|i| i.parse::<usize>().ok()).and_then(|i| ctx.speech.voices().get(i).cloned())
126                    && ctx.speech.set_voice(&voice.id)
127                {
128                    ctx.storage.set("voice", &voice.id);
129                }
130                ctx.ui.close();
131                refresh_title(ctx, "voice");
132                let name = ctx.speech.voice().map_or("Default voice".to_string(), |v| v.name.clone());
133                ctx.speech.speak_queued(format!("{name}. This is how I sound now."));
134            }
135            UiEvent::Changed { menu, item, value } if menu == "title" && item == "rate" => {
136                ctx.speech.set_rate(value.as_f32() / 100.0);
137                ctx.storage.set("rate", value.as_f32());
138            }
139            _ => {}
140        }
141    }
Source

pub fn is_quitting(&self) -> bool

Whether Context::quit was called.

Source

pub fn title(&self) -> &str

The game title given to Engine::new.

Source

pub fn switch_scene(&mut self, scene: impl Scene)

Replaces the current scene with another one after this frame.

Examples found in repository?
examples/bell_catcher.rs (line 97)
92    fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
93        match event {
94            UiEvent::Activated { menu, item } if menu == "title" => match item.as_str() {
95                "play" => {
96                    ctx.ui.close_all();
97                    ctx.switch_scene(Game::default());
98                }
99                "voice" => match voice_menu(ctx) {
100                    Some(menu) => ctx.ui.open(menu),
101                    None => ctx.ui.message(
102                        "no voices",
103                        "Your screen reader speaks with its own voice. Change it in the screen reader's settings.",
104                    ),
105                },
106                "help" => ctx.ui.message(
107                    "help",
108                    "Bells fall in one of seven lanes in front of you. Listen where a bell rings, \
109                     step under it with left and right, and catch it before it lands. \
110                     The bell rings faster and higher the closer it gets. You have three lives. \
111                     Press S for your score, Escape for the menu.",
112                ),
113                "quit" => ctx.quit(),
114                _ => {}
115            },
116            UiEvent::Activated { menu, item } if menu == "voices" => {
117                if item == "back" {
118                    ctx.ui.close();
119                    return;
120                }
121                if item == "default" {
122                    ctx.speech.reset_voice();
123                    ctx.storage.remove("voice");
124                } else if let Some(voice) =
125                    item.strip_prefix("voice").and_then(|i| i.parse::<usize>().ok()).and_then(|i| ctx.speech.voices().get(i).cloned())
126                    && ctx.speech.set_voice(&voice.id)
127                {
128                    ctx.storage.set("voice", &voice.id);
129                }
130                ctx.ui.close();
131                refresh_title(ctx, "voice");
132                let name = ctx.speech.voice().map_or("Default voice".to_string(), |v| v.name.clone());
133                ctx.speech.speak_queued(format!("{name}. This is how I sound now."));
134            }
135            UiEvent::Changed { menu, item, value } if menu == "title" && item == "rate" => {
136                ctx.speech.set_rate(value.as_f32() / 100.0);
137                ctx.storage.set("rate", value.as_f32());
138            }
139            _ => {}
140        }
141    }
142
143    fn update(&mut self, _ctx: &mut Context) {}
144}
145
146// ------------------------------------------------------------------------- game
147
148struct Game {
149    lane: i32,
150    bell_lane: i32,
151    /// Seconds a bell needs to land; shrinks with every catch.
152    fall: f32,
153    /// Seconds until the current bell lands. Negative while waiting for the next bell.
154    left: f32,
155    next_ring: f32,
156    score: u32,
157    lives: u32,
158    over: bool,
159}
160
161impl Default for Game {
162    fn default() -> Self {
163        Self { lane: 0, bell_lane: 0, fall: FIRST_FALL, left: 0.0, next_ring: 0.0, score: 0, lives: LIVES, over: false }
164    }
165}
166
167impl Game {
168    fn drop_bell(&mut self) {
169        self.bell_lane = random::int(-LANES as i64, LANES as i64) as i32;
170        self.left = self.fall;
171        self.next_ring = 0.0;
172    }
173
174    /// Where the bell is, seen from the player: lanes to the side, a little in front.
175    fn bell_position(&self) -> Vec3 {
176        vec3((self.bell_lane - self.lane) as f32 * LANE_WIDTH, 2.0, 0.0)
177    }
178
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 push_scene(&mut self, scene: impl Scene)

Puts a scene on top of the current one (which is paused) after this frame.

Examples found in repository?
examples/beacon.rs (line 60)
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 pop_scene(&mut self)

Removes the current scene after this frame. Popping the last scene quits the game.

Examples found in repository?
examples/beacon.rs (line 145)
142    fn update(&mut self, ctx: &mut Context) {
143        if ctx.ui.clicked("pause", "resume") || ctx.ui.closed("pause") {
144            ctx.ui.close_id("pause");
145            ctx.pop_scene();
146        }
147        if ctx.ui.clicked("pause", "quit") {
148            ctx.quit();
149        }
150    }
Source

pub fn insert<T: 'static>(&mut self, value: T)

Stores a value shared by all scenes (settings, score, save data…). There is one slot per type; storing again replaces the old value.

Examples found in repository?
examples/menus.rs (line 46)
36    fn start(&mut self, ctx: &mut Context) {
37        // Own interface sounds, designed in code. Files work too:
38        // ctx.ui.set_sound(UiSound::Navigate, Some("ui/click"));
39        ctx.ui.set_sound_data(UiSound::Navigate, Synth::triangle(1800.0).length(0.025).decay(150.0).volume(0.4));
40        ctx.ui.set_sound_data(UiSound::Wrap, Synth::triangle(1800.0).sweep_to(2600.0).length(0.06).decay(60.0).volume(0.4));
41        ctx.ui.set_sound_data(UiSound::Typing, Synth::noise().length(0.012).decay(300.0).volume(0.25));
42        ctx.ui.set_sound_data(
43            UiSound::Activate,
44            Synth::sine(880.0).length(0.04).volume(0.35).then(Synth::sine(1320.0).length(0.08).decay(40.0).volume(0.35)),
45        );
46        ctx.insert(Settings { volume: 0.8, difficulty: 1, name: "Player".into() });
47        ctx.ui.open(main_menu());
48    }
Source

pub fn get<T: 'static>(&self) -> Option<&T>

Reads a shared value stored with Context::insert.

Examples found in repository?
examples/menus.rs (line 54)
50    fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
51        match event {
52            UiEvent::Activated { menu, item } => match (menu.as_str(), item.as_str()) {
53                ("main", "play") => {
54                    let s = ctx.get::<Settings>().expect("settings exist");
55                    let text = format!("Good luck, {}! (this is where your game would start)", s.name);
56                    ctx.ui.message("intro", text);
57                }
58                ("main", "options") => {
59                    let menu = options_menu(ctx.get::<Settings>().expect("settings exist"));
60                    ctx.ui.open(menu);
61                }
62                ("main", "name") => ctx.ui.open(TextInput::new("name", "Enter your name").max_length(20).required()),
63                ("main", "quit") => ctx.ui.open(Confirm::new("quit", "Do you really want to quit?").default_no()),
64                ("options", "back") => ctx.ui.close(),
65                _ => {}
66            },
67            UiEvent::Changed { menu, item, value } if menu == "options" => {
68                let s = ctx.get_mut::<Settings>().expect("settings exist");
69                match item.as_str() {
70                    "volume" => s.volume = value.as_f32() / 100.0,
71                    "difficulty" => s.difficulty = value.as_index(),
72                    "name" => s.name = value.as_str().to_string(),
73                    _ => {}
74                }
75                let volume = s.volume;
76                ctx.audio.set_master_volume(volume);
77            }
78            UiEvent::TextSubmitted { id, text } if id == "name" => {
79                ctx.get_mut::<Settings>().expect("settings exist").name = text.clone();
80                ctx.speech.speak_queued(format!("Hello, {text}"));
81            }
82            UiEvent::Confirmed { id, yes: true } if id == "quit" => ctx.quit(),
83            _ => {}
84        }
85    }
Source

pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T>

Mutable access to a shared value.

Examples found in repository?
examples/menus.rs (line 68)
50    fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
51        match event {
52            UiEvent::Activated { menu, item } => match (menu.as_str(), item.as_str()) {
53                ("main", "play") => {
54                    let s = ctx.get::<Settings>().expect("settings exist");
55                    let text = format!("Good luck, {}! (this is where your game would start)", s.name);
56                    ctx.ui.message("intro", text);
57                }
58                ("main", "options") => {
59                    let menu = options_menu(ctx.get::<Settings>().expect("settings exist"));
60                    ctx.ui.open(menu);
61                }
62                ("main", "name") => ctx.ui.open(TextInput::new("name", "Enter your name").max_length(20).required()),
63                ("main", "quit") => ctx.ui.open(Confirm::new("quit", "Do you really want to quit?").default_no()),
64                ("options", "back") => ctx.ui.close(),
65                _ => {}
66            },
67            UiEvent::Changed { menu, item, value } if menu == "options" => {
68                let s = ctx.get_mut::<Settings>().expect("settings exist");
69                match item.as_str() {
70                    "volume" => s.volume = value.as_f32() / 100.0,
71                    "difficulty" => s.difficulty = value.as_index(),
72                    "name" => s.name = value.as_str().to_string(),
73                    _ => {}
74                }
75                let volume = s.volume;
76                ctx.audio.set_master_volume(volume);
77            }
78            UiEvent::TextSubmitted { id, text } if id == "name" => {
79                ctx.get_mut::<Settings>().expect("settings exist").name = text.clone();
80                ctx.speech.speak_queued(format!("Hello, {text}"));
81            }
82            UiEvent::Confirmed { id, yes: true } if id == "quit" => ctx.quit(),
83            _ => {}
84        }
85    }
Source

pub fn get_or_default<T: Default + 'static>(&mut self) -> &mut T

Mutable access to a shared value, inserting T::default() if it is missing.

Source

pub fn remove<T: 'static>(&mut self) -> Option<T>

Removes a shared value and returns it.

Source§

impl Context

Source

pub fn speak(&mut self, text: impl AsRef<str>)

Speaks text, interrupting current speech. Same as ctx.speech.speak(text).

Examples found in repository?
examples/platformer.rs (line 55)
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    }
More examples
Hide additional examples
examples/driving.rs (line 68)
67    fn start(&mut self, ctx: &mut Context) {
68        ctx.speak("You sit in the car. Up drives, down brakes. Drive to the garage in the north east. F1 for help.");
69    }
70
71    fn update(&mut self, ctx: &mut Context) {
72        let id = self.driving;
73        ctx.drive(id);
74
75        if ctx.pressed(Key::H) {
76            ctx.horn(id);
77        }
78        if ctx.pressed(Key::E) {
79            let running = ctx.physics.vehicle(id).is_some_and(|v| v.is_engine_on());
80            if running {
81                ctx.stop_engine(id);
82                ctx.speak("Engine off");
83            } else if !ctx.start_engine(id) {
84                ctx.speak("The tank is empty");
85            }
86        }
87        if ctx.pressed(Key::Tab) {
88            if let Some(v) = ctx.physics.vehicle_mut(id) {
89                v.throttle = 0.0;
90                v.brake = 0.0;
91                v.handbrake = true;
92            }
93            self.driving = if id == self.car { self.truck } else { self.car };
94            ctx.follow(self.driving);
95            let text = format!("You switch to the {}. E starts the engine.", self.name());
96            ctx.speak(text);
97        }
98        if ctx.pressed(Key::L) && self.driving == self.truck {
99            let speed = ctx.physics.vehicle(self.truck).map_or(0.0, |v| v.speed().abs());
100            if speed > 0.5 {
101                ctx.speak("Stop first");
102            } else if let Some(v) = ctx.physics.vehicle_mut(self.truck) {
103                let load = if v.load > 0.0 { 0.0 } else { v.max_load };
104                v.set_load(load);
105                let text = format!("Load {} tons, total weight {} tons", load / 1000.0, ctx.physics.weight(self.truck) / 1000.0);
106                ctx.speak(text);
107            }
108        }
109        if ctx.pressed(Key::F)
110            && let Some(v) = ctx.physics.vehicle(id)
111        {
112            let mut text = format!("{}, gear {}", v.describe_speed(), v.gear_name());
113            if let Some(fuel) = v.fuel() {
114                text.push_str(&format!(", {fuel:.1} liters left"));
115            }
116            ctx.speak(text);
117        }
118        if ctx.pressed(Key::G) {
119            let text = ctx.physics.describe(id, self.garage);
120            ctx.speak(text);
121        }
122        if ctx.pressed(Key::F1) {
123            ctx.speak("Up gas, down brake and reverse, left and right steer, space handbrake, H horn, E engine, Tab switch vehicle, L load the truck, F speed, G garage.");
124        }
125
126        if let Some(gear) = ctx.physics.gear_changed(id)
127            && gear == -1
128        {
129            ctx.speak("Reverse");
130        }
131        if let Some((_, speed)) = ctx.physics.crashed(id)
132            && speed > 5.0
133        {
134            let text = format!("Crash at {:.0} kilometers per hour", speed * 3.6);
135            ctx.speak(text);
136        }
137        if ctx.physics.entered_tag(id, "garage").is_some() {
138            let text = format!("You parked the {} in the garage. Well done!", self.name());
139            ctx.speak(text);
140            ctx.quit();
141        }
142        if ctx.pressed(Key::Escape) {
143            ctx.quit();
144        }
145    }
examples/beacon.rs (line 55)
54    fn start(&mut self, ctx: &mut Context) {
55        ctx.speak("Find all three beacons. Up and down to walk, left and right to turn. C locates the nearest beacon.");
56    }
57
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/flight.rs (line 72)
71    fn start(&mut self, ctx: &mut Context) {
72        ctx.speak("Find the four birds. Arrows fly and turn, page up and down look up and down, E and Q rise and sink. C locates the nearest bird.");
73    }
74
75    fn update(&mut self, ctx: &mut Context) {
76        if ctx.pressed(Key::Escape) {
77            ctx.quit();
78        }
79        if ctx.pressed(Key::Left) || ctx.pressed(Key::Right) {
80            let facing = ctx.physics.turn(self.drone, if ctx.pressed(Key::Left) { -30.0 } else { 30.0 });
81            ctx.speak(math::direction_name(facing));
82        }
83        if ctx.pressed(Key::PageUp) || ctx.pressed(Key::PageDown) {
84            let pitch = ctx.physics.tilt(self.drone, if ctx.pressed(Key::PageUp) { 30.0 } else { -30.0 });
85            ctx.speak(describe_pitch(pitch));
86        }
87
88        // Smooth flight: 4 meters per second while a key is held.
89        let speed = 4.0 * ctx.time.delta();
90        let forward = ctx.input.axis(Key::Down, Key::Up);
91        if forward != 0.0 {
92            let result = ctx.physics.move_forward(self.drone, forward * speed);
93            self.fly(ctx, result);
94        }
95        let rise = ctx.input.axis(Key::Q, Key::E);
96        if rise != 0.0 {
97            let result = ctx.physics.move_up(self.drone, rise * speed);
98            self.fly(ctx, result);
99        }
100
101        if ctx.pressed(Key::C) {
102            let position = ctx.physics.position(self.drone);
103            match ctx.physics.nearest_with_tag(position, "bird") {
104                Some(bird) => {
105                    let text = ctx.physics.describe(self.drone, bird); // e.g. "2 o'clock, 9 meters, 6 meters above"
106                    ctx.speak(text);
107                }
108                None => ctx.speak("No birds left"),
109            }
110        }
111        if ctx.pressed(Key::F) {
112            let (facing, pitch) = (ctx.physics.facing(self.drone), ctx.physics.pitch(self.drone));
113            ctx.speak(format!("Facing {}, {}", math::direction_name(facing), describe_pitch(pitch)));
114        }
115        if ctx.pressed(Key::H) {
116            let height = ctx.physics.position(self.drone).z;
117            ctx.speak(format!("{height:.0} meters high"));
118        }
119
120        if let Some(bird) = ctx.physics.entered_tag(self.drone, "bird") {
121            ctx.physics.remove(bird);
122            self.birds -= 1;
123            ctx.play("found");
124            if self.birds == 0 {
125                ctx.ui.message("done", "You found every bird!");
126            } else {
127                ctx.speak(format!("Bird found! {} left.", self.birds));
128            }
129        }
130        if ctx.ui.closed("done") {
131            ctx.quit();
132        }
133    }
examples/bell_catcher.rs (line 193)
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    }
examples/clicker.rs (line 14)
8fn main() -> audio_code::Result<()> {
9    let mut score = 0;
10
11    audio_code::run("Clicker", move |ctx| {
12        if ctx.once("intro") {
13            let best: u32 = ctx.storage.get_or("best", 0);
14            ctx.speak(format!("Press space as often as you can in ten seconds. Your best is {best}. Go!"));
15            ctx.time.after("end", 10.0);
16        }
17
18        if ctx.time.is_scheduled("end") && ctx.pressed(Key::Space) {
19            score += 1;
20            ctx.audio.play_tone(300.0 + score as f32 * 15.0, 0.05);
21        }
22
23        if ctx.time.fired("end") {
24            let best: u32 = ctx.storage.get_or("best", 0);
25            if score > best {
26                ctx.storage.set("best", score);
27                ctx.ui.message("result", format!("{score} presses. New record!"));
28            } else {
29                ctx.ui.message("result", format!("{score} presses. Your best is {best}."));
30            }
31        }
32
33        if ctx.ui.closed("result") || ctx.pressed(Key::Escape) {
34            ctx.quit();
35        }
36    })
37}
Source

pub fn tr(&self, key: &str) -> String

The text for key in the current language. Same as ctx.lang.get(key).

ctx.speak(ctx.tr("intro"));
Source

pub fn tr_with(&self, key: &str, args: &[(&str, &dyn Display)]) -> String

The text for key with {name} placeholders filled in. Same as ctx.lang.format(key, args).

// In lang/en.txt: score = You have {points} points.
ctx.speak(ctx.tr_with("score", &[("points", &score)]));
Source

pub fn set_language(&mut self, code: &str) -> bool

Switches the language and remembers it in Context::storage, so the game starts in it next time. Returns false if there are no texts for it.

let mut menu = Menu::new("language", "Language");
for code in ctx.lang.languages() {
    menu = menu.button(code.clone(), ctx.lang.name(&code));
}
ctx.ui.open(menu);
// ...later, in update():
if let Some(code) = ctx.ui.activated("language").map(String::from) {
    ctx.set_language(&code);
    ctx.ui.close();
}
Source

pub fn describe(&self, from: BodyId, target: BodyId) -> String

Where target is as seen from from, in the current language, e.g. "3 o'clock, 12 meters" or "3 Uhr, 12 Meter". Like World::describe, which is always English.

Source

pub fn play(&mut self, name: &str) -> Sound

Plays a sound once. Same as ctx.audio.play(name).

Examples found in repository?
examples/flight.rs (line 63)
59    fn fly(&mut self, ctx: &mut Context, result: MoveResult) {
60        if result.blocked && !self.blocked
61            && let Some(&hit) = result.hits.first()
62        {
63            ctx.play("bump");
64            ctx.speech.speak(ctx.physics.tag(hit));
65        }
66        self.blocked = result.blocked;
67    }
68}
69
70impl Scene for Flight {
71    fn start(&mut self, ctx: &mut Context) {
72        ctx.speak("Find the four birds. Arrows fly and turn, page up and down look up and down, E and Q rise and sink. C locates the nearest bird.");
73    }
74
75    fn update(&mut self, ctx: &mut Context) {
76        if ctx.pressed(Key::Escape) {
77            ctx.quit();
78        }
79        if ctx.pressed(Key::Left) || ctx.pressed(Key::Right) {
80            let facing = ctx.physics.turn(self.drone, if ctx.pressed(Key::Left) { -30.0 } else { 30.0 });
81            ctx.speak(math::direction_name(facing));
82        }
83        if ctx.pressed(Key::PageUp) || ctx.pressed(Key::PageDown) {
84            let pitch = ctx.physics.tilt(self.drone, if ctx.pressed(Key::PageUp) { 30.0 } else { -30.0 });
85            ctx.speak(describe_pitch(pitch));
86        }
87
88        // Smooth flight: 4 meters per second while a key is held.
89        let speed = 4.0 * ctx.time.delta();
90        let forward = ctx.input.axis(Key::Down, Key::Up);
91        if forward != 0.0 {
92            let result = ctx.physics.move_forward(self.drone, forward * speed);
93            self.fly(ctx, result);
94        }
95        let rise = ctx.input.axis(Key::Q, Key::E);
96        if rise != 0.0 {
97            let result = ctx.physics.move_up(self.drone, rise * speed);
98            self.fly(ctx, result);
99        }
100
101        if ctx.pressed(Key::C) {
102            let position = ctx.physics.position(self.drone);
103            match ctx.physics.nearest_with_tag(position, "bird") {
104                Some(bird) => {
105                    let text = ctx.physics.describe(self.drone, bird); // e.g. "2 o'clock, 9 meters, 6 meters above"
106                    ctx.speak(text);
107                }
108                None => ctx.speak("No birds left"),
109            }
110        }
111        if ctx.pressed(Key::F) {
112            let (facing, pitch) = (ctx.physics.facing(self.drone), ctx.physics.pitch(self.drone));
113            ctx.speak(format!("Facing {}, {}", math::direction_name(facing), describe_pitch(pitch)));
114        }
115        if ctx.pressed(Key::H) {
116            let height = ctx.physics.position(self.drone).z;
117            ctx.speak(format!("{height:.0} meters high"));
118        }
119
120        if let Some(bird) = ctx.physics.entered_tag(self.drone, "bird") {
121            ctx.physics.remove(bird);
122            self.birds -= 1;
123            ctx.play("found");
124            if self.birds == 0 {
125                ctx.ui.message("done", "You found every bird!");
126            } else {
127                ctx.speak(format!("Bird found! {} left.", self.birds));
128            }
129        }
130        if ctx.ui.closed("done") {
131            ctx.quit();
132        }
133    }
More examples
Hide additional examples
examples/bell_catcher.rs (line 191)
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    }
examples/platformer.rs (line 72)
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    }
examples/beacon.rs (line 73)
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 play_at(&mut self, name: &str, position: Vec3) -> Sound

Plays a sound once at a position. Same as ctx.audio.play_at(name, position).

Source

pub fn pressed(&self, key: Key) -> bool

Whether a key went down this frame. Same as ctx.input.pressed(key).

Examples found in repository?
examples/clicker.rs (line 18)
8fn main() -> audio_code::Result<()> {
9    let mut score = 0;
10
11    audio_code::run("Clicker", move |ctx| {
12        if ctx.once("intro") {
13            let best: u32 = ctx.storage.get_or("best", 0);
14            ctx.speak(format!("Press space as often as you can in ten seconds. Your best is {best}. Go!"));
15            ctx.time.after("end", 10.0);
16        }
17
18        if ctx.time.is_scheduled("end") && ctx.pressed(Key::Space) {
19            score += 1;
20            ctx.audio.play_tone(300.0 + score as f32 * 15.0, 0.05);
21        }
22
23        if ctx.time.fired("end") {
24            let best: u32 = ctx.storage.get_or("best", 0);
25            if score > best {
26                ctx.storage.set("best", score);
27                ctx.ui.message("result", format!("{score} presses. New record!"));
28            } else {
29                ctx.ui.message("result", format!("{score} presses. Your best is {best}."));
30            }
31        }
32
33        if ctx.ui.closed("result") || ctx.pressed(Key::Escape) {
34            ctx.quit();
35        }
36    })
37}
More examples
Hide additional examples
examples/bell_catcher.rs (line 235)
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    }
examples/platformer.rs (line 59)
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    }
examples/beacon.rs (line 59)
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/flight.rs (line 76)
75    fn update(&mut self, ctx: &mut Context) {
76        if ctx.pressed(Key::Escape) {
77            ctx.quit();
78        }
79        if ctx.pressed(Key::Left) || ctx.pressed(Key::Right) {
80            let facing = ctx.physics.turn(self.drone, if ctx.pressed(Key::Left) { -30.0 } else { 30.0 });
81            ctx.speak(math::direction_name(facing));
82        }
83        if ctx.pressed(Key::PageUp) || ctx.pressed(Key::PageDown) {
84            let pitch = ctx.physics.tilt(self.drone, if ctx.pressed(Key::PageUp) { 30.0 } else { -30.0 });
85            ctx.speak(describe_pitch(pitch));
86        }
87
88        // Smooth flight: 4 meters per second while a key is held.
89        let speed = 4.0 * ctx.time.delta();
90        let forward = ctx.input.axis(Key::Down, Key::Up);
91        if forward != 0.0 {
92            let result = ctx.physics.move_forward(self.drone, forward * speed);
93            self.fly(ctx, result);
94        }
95        let rise = ctx.input.axis(Key::Q, Key::E);
96        if rise != 0.0 {
97            let result = ctx.physics.move_up(self.drone, rise * speed);
98            self.fly(ctx, result);
99        }
100
101        if ctx.pressed(Key::C) {
102            let position = ctx.physics.position(self.drone);
103            match ctx.physics.nearest_with_tag(position, "bird") {
104                Some(bird) => {
105                    let text = ctx.physics.describe(self.drone, bird); // e.g. "2 o'clock, 9 meters, 6 meters above"
106                    ctx.speak(text);
107                }
108                None => ctx.speak("No birds left"),
109            }
110        }
111        if ctx.pressed(Key::F) {
112            let (facing, pitch) = (ctx.physics.facing(self.drone), ctx.physics.pitch(self.drone));
113            ctx.speak(format!("Facing {}, {}", math::direction_name(facing), describe_pitch(pitch)));
114        }
115        if ctx.pressed(Key::H) {
116            let height = ctx.physics.position(self.drone).z;
117            ctx.speak(format!("{height:.0} meters high"));
118        }
119
120        if let Some(bird) = ctx.physics.entered_tag(self.drone, "bird") {
121            ctx.physics.remove(bird);
122            self.birds -= 1;
123            ctx.play("found");
124            if self.birds == 0 {
125                ctx.ui.message("done", "You found every bird!");
126            } else {
127                ctx.speak(format!("Bird found! {} left.", self.birds));
128            }
129        }
130        if ctx.ui.closed("done") {
131            ctx.quit();
132        }
133    }
examples/driving.rs (line 75)
71    fn update(&mut self, ctx: &mut Context) {
72        let id = self.driving;
73        ctx.drive(id);
74
75        if ctx.pressed(Key::H) {
76            ctx.horn(id);
77        }
78        if ctx.pressed(Key::E) {
79            let running = ctx.physics.vehicle(id).is_some_and(|v| v.is_engine_on());
80            if running {
81                ctx.stop_engine(id);
82                ctx.speak("Engine off");
83            } else if !ctx.start_engine(id) {
84                ctx.speak("The tank is empty");
85            }
86        }
87        if ctx.pressed(Key::Tab) {
88            if let Some(v) = ctx.physics.vehicle_mut(id) {
89                v.throttle = 0.0;
90                v.brake = 0.0;
91                v.handbrake = true;
92            }
93            self.driving = if id == self.car { self.truck } else { self.car };
94            ctx.follow(self.driving);
95            let text = format!("You switch to the {}. E starts the engine.", self.name());
96            ctx.speak(text);
97        }
98        if ctx.pressed(Key::L) && self.driving == self.truck {
99            let speed = ctx.physics.vehicle(self.truck).map_or(0.0, |v| v.speed().abs());
100            if speed > 0.5 {
101                ctx.speak("Stop first");
102            } else if let Some(v) = ctx.physics.vehicle_mut(self.truck) {
103                let load = if v.load > 0.0 { 0.0 } else { v.max_load };
104                v.set_load(load);
105                let text = format!("Load {} tons, total weight {} tons", load / 1000.0, ctx.physics.weight(self.truck) / 1000.0);
106                ctx.speak(text);
107            }
108        }
109        if ctx.pressed(Key::F)
110            && let Some(v) = ctx.physics.vehicle(id)
111        {
112            let mut text = format!("{}, gear {}", v.describe_speed(), v.gear_name());
113            if let Some(fuel) = v.fuel() {
114                text.push_str(&format!(", {fuel:.1} liters left"));
115            }
116            ctx.speak(text);
117        }
118        if ctx.pressed(Key::G) {
119            let text = ctx.physics.describe(id, self.garage);
120            ctx.speak(text);
121        }
122        if ctx.pressed(Key::F1) {
123            ctx.speak("Up gas, down brake and reverse, left and right steer, space handbrake, H horn, E engine, Tab switch vehicle, L load the truck, F speed, G garage.");
124        }
125
126        if let Some(gear) = ctx.physics.gear_changed(id)
127            && gear == -1
128        {
129            ctx.speak("Reverse");
130        }
131        if let Some((_, speed)) = ctx.physics.crashed(id)
132            && speed > 5.0
133        {
134            let text = format!("Crash at {:.0} kilometers per hour", speed * 3.6);
135            ctx.speak(text);
136        }
137        if ctx.physics.entered_tag(id, "garage").is_some() {
138            let text = format!("You parked the {} in the garage. Well done!", self.name());
139            ctx.speak(text);
140            ctx.quit();
141        }
142        if ctx.pressed(Key::Escape) {
143            ctx.quit();
144        }
145    }
Source

pub fn held(&self, key: Key) -> bool

Whether a key is held. Same as ctx.input.held(key).

Source

pub fn once(&mut self, name: &str) -> bool

Returns true the first time it is called with name, and false afterwards. Handy for one-time setup in closure games and for “say this only once” hints.

if ctx.once("intro") {
    ctx.speak("Welcome! Press F1 for help.");
}
Examples found in repository?
examples/clicker.rs (line 12)
8fn main() -> audio_code::Result<()> {
9    let mut score = 0;
10
11    audio_code::run("Clicker", move |ctx| {
12        if ctx.once("intro") {
13            let best: u32 = ctx.storage.get_or("best", 0);
14            ctx.speak(format!("Press space as often as you can in ten seconds. Your best is {best}. Go!"));
15            ctx.time.after("end", 10.0);
16        }
17
18        if ctx.time.is_scheduled("end") && ctx.pressed(Key::Space) {
19            score += 1;
20            ctx.audio.play_tone(300.0 + score as f32 * 15.0, 0.05);
21        }
22
23        if ctx.time.fired("end") {
24            let best: u32 = ctx.storage.get_or("best", 0);
25            if score > best {
26                ctx.storage.set("best", score);
27                ctx.ui.message("result", format!("{score} presses. New record!"));
28            } else {
29                ctx.ui.message("result", format!("{score} presses. Your best is {best}."));
30            }
31        }
32
33        if ctx.ui.closed("result") || ctx.pressed(Key::Escape) {
34            ctx.quit();
35        }
36    })
37}
Source

pub fn follow(&mut self, body: BodyId)

Makes the listener (the player’s ears) follow a body every frame: its position, its facing and its pitch. After this you never call ctx.audio.set_listener yourself.

Examples found in repository?
examples/beacon.rs (line 41)
32    fn new(ctx: &mut Context) -> Self {
33        ctx.audio.register("step", Synth::noise().length(0.08).decay(60.0));
34        ctx.audio.register("wall", Synth::sine(90.0).length(0.15).decay(25.0).volume(0.8));
35        ctx.audio.register("beacon", Synth::sine(1320.0).length(0.08).then(Synth::silence(0.04)).then(Synth::sine(1320.0).length(0.08)).then(Synth::silence(0.8)));
36        ctx.audio.register("found", Synth::sine(440.0).sweep_to(1200.0).length(0.6).decay(4.0).volume(0.3));
37
38        // Every character is a 2 meter tile: walls, the player start and three beacons.
39        let level = ctx.physics.build_level(MAP, 2.0);
40        let player = ctx.physics.add(Body::kinematic(level.point('P').unwrap()).with_sphere(0.4).with_tag("player"));
41        ctx.follow(player); // the ears go wherever the player goes and face where it faces
42
43        let mut beacons = Vec::new();
44        for spot in level.points('B') {
45            let beacon = ctx.physics.add(Body::sensor(spot).with_sphere(0.8).with_tag("beacon"));
46            ctx.loop_on(beacon, "beacon"); // stops by itself when the beacon is removed
47            beacons.push(beacon);
48        }
49        Self { player, beacons, clock: Timer::new() }
50    }
More examples
Hide additional examples
examples/flight.rs (line 49)
31    fn new(ctx: &mut Context) -> Self {
32        ctx.audio.register("bird", Synth::sine(2200.0).sweep_to(3200.0).length(0.07).then(Synth::silence(0.05)).then(Synth::sine(2600.0).length(0.06)).then(Synth::silence(0.7)));
33        ctx.audio.register("hum", Synth::saw(70.0).length(1.0).volume(0.08));
34        ctx.audio.register("bump", Synth::sine(80.0).length(0.15).decay(25.0).volume(0.8));
35        ctx.audio.register("found", Synth::triangle(660.0).sweep_to(1320.0).length(0.4).decay(6.0).volume(0.3));
36
37        // A hall of 40 x 40 meters and 16 meters high: floor, roof and four walls.
38        // A box is given by its center and its full width (x), depth (y) and height (z).
39        let physics = &mut ctx.physics;
40        physics.add(Body::fixed(vec3(0.0, 0.0, -0.5)).with_box(40.0, 40.0, 1.0).with_tag("floor"));
41        physics.add(Body::fixed(vec3(0.0, 0.0, 16.5)).with_box(40.0, 40.0, 1.0).with_tag("roof"));
42        physics.add(Body::fixed(vec3(0.0, 20.5, 8.0)).with_box(40.0, 1.0, 16.0).with_tag("north wall"));
43        physics.add(Body::fixed(vec3(0.0, -20.5, 8.0)).with_box(40.0, 1.0, 16.0).with_tag("south wall"));
44        physics.add(Body::fixed(vec3(20.5, 0.0, 8.0)).with_box(1.0, 40.0, 16.0).with_tag("east wall"));
45        physics.add(Body::fixed(vec3(-20.5, 0.0, 8.0)).with_box(1.0, 40.0, 16.0).with_tag("west wall"));
46
47        // The drone starts in the middle, 2 meters above the floor. No gravity: it hovers.
48        let drone = ctx.physics.add(Body::kinematic(vec3(0.0, 0.0, 2.0)).with_sphere(0.5).with_tag("drone"));
49        ctx.follow(drone); // the ears use the drone's position, facing and pitch
50        ctx.audio.play_looped("hum");
51
52        for spot in BIRDS {
53            let bird = ctx.physics.add(Body::sensor(spot).with_sphere(1.0).with_tag("bird"));
54            ctx.loop_on(bird, "bird");
55        }
56        Self { drone, birds: BIRDS.len(), blocked: false }
57    }
examples/driving.rs (line 57)
29    fn new(ctx: &mut Context) -> Self {
30        // Engine: a low buzz that loops without clicks (whole cycles, no fades).
31        let engine = Synth::saw(55.0).length(1.0).attack(0.0).release(0.0).volume(0.25)
32            .with(Synth::square(110.0).length(1.0).attack(0.0).release(0.0).volume(0.08));
33        ctx.audio.register("engine", engine);
34        ctx.audio.register("horn", Synth::square(415.0).length(0.5).volume(0.25).with(Synth::square(523.0).length(0.5).volume(0.2)));
35        ctx.audio.register("shift", Synth::noise().length(0.04).decay(90.0).volume(0.3));
36        ctx.audio.register("crash", Synth::noise().length(0.4).decay(10.0).volume(0.8).with(Synth::sine(80.0).sweep_to(30.0).length(0.3)));
37        ctx.audio.register("start", Synth::saw(40.0).sweep_to(55.0).length(0.6).volume(0.3));
38        ctx.audio.register("garage", Synth::sine(880.0).length(0.15).decay(10.0));
39
40        let level = ctx.physics.build_level(MAP, 6.0);
41        let sounds = |v: Vehicle| {
42            v.with_engine_sound("engine")
43                .with_horn_sound("horn")
44                .with_shift_sound("shift")
45                .with_crash_sound("crash")
46                .with_start_sound("start")
47        };
48        let car_start = level.point('P').unwrap_or(Vec3::ZERO);
49        let car = ctx.physics.add(Body::car(car_start).with_facing(90.0).with_vehicle(sounds(Vehicle::car().with_fuel(5.0, 8.0))));
50        let truck_start = level.point('T').unwrap_or(Vec3::ZERO);
51        let truck = ctx.physics.add(
52            Body::truck(truck_start).with_facing(90.0).with_vehicle(sounds(Vehicle::truck()).engine_off()),
53        );
54        let garage_at = level.point('G').unwrap_or(Vec3::ZERO);
55        let garage = ctx.physics.add(Body::sensor(garage_at).with_box(6.0, 6.0, 3.0).with_tag("garage"));
56        ctx.loop_on(garage, "garage");
57        ctx.follow(car);
58        Self { car, truck, driving: car, garage }
59    }
60
61    fn name(&self) -> &'static str {
62        if self.driving == self.car { "car" } else { "truck" }
63    }
64}
65
66impl Scene for Town {
67    fn start(&mut self, ctx: &mut Context) {
68        ctx.speak("You sit in the car. Up drives, down brakes. Drive to the garage in the north east. F1 for help.");
69    }
70
71    fn update(&mut self, ctx: &mut Context) {
72        let id = self.driving;
73        ctx.drive(id);
74
75        if ctx.pressed(Key::H) {
76            ctx.horn(id);
77        }
78        if ctx.pressed(Key::E) {
79            let running = ctx.physics.vehicle(id).is_some_and(|v| v.is_engine_on());
80            if running {
81                ctx.stop_engine(id);
82                ctx.speak("Engine off");
83            } else if !ctx.start_engine(id) {
84                ctx.speak("The tank is empty");
85            }
86        }
87        if ctx.pressed(Key::Tab) {
88            if let Some(v) = ctx.physics.vehicle_mut(id) {
89                v.throttle = 0.0;
90                v.brake = 0.0;
91                v.handbrake = true;
92            }
93            self.driving = if id == self.car { self.truck } else { self.car };
94            ctx.follow(self.driving);
95            let text = format!("You switch to the {}. E starts the engine.", self.name());
96            ctx.speak(text);
97        }
98        if ctx.pressed(Key::L) && self.driving == self.truck {
99            let speed = ctx.physics.vehicle(self.truck).map_or(0.0, |v| v.speed().abs());
100            if speed > 0.5 {
101                ctx.speak("Stop first");
102            } else if let Some(v) = ctx.physics.vehicle_mut(self.truck) {
103                let load = if v.load > 0.0 { 0.0 } else { v.max_load };
104                v.set_load(load);
105                let text = format!("Load {} tons, total weight {} tons", load / 1000.0, ctx.physics.weight(self.truck) / 1000.0);
106                ctx.speak(text);
107            }
108        }
109        if ctx.pressed(Key::F)
110            && let Some(v) = ctx.physics.vehicle(id)
111        {
112            let mut text = format!("{}, gear {}", v.describe_speed(), v.gear_name());
113            if let Some(fuel) = v.fuel() {
114                text.push_str(&format!(", {fuel:.1} liters left"));
115            }
116            ctx.speak(text);
117        }
118        if ctx.pressed(Key::G) {
119            let text = ctx.physics.describe(id, self.garage);
120            ctx.speak(text);
121        }
122        if ctx.pressed(Key::F1) {
123            ctx.speak("Up gas, down brake and reverse, left and right steer, space handbrake, H horn, E engine, Tab switch vehicle, L load the truck, F speed, G garage.");
124        }
125
126        if let Some(gear) = ctx.physics.gear_changed(id)
127            && gear == -1
128        {
129            ctx.speak("Reverse");
130        }
131        if let Some((_, speed)) = ctx.physics.crashed(id)
132            && speed > 5.0
133        {
134            let text = format!("Crash at {:.0} kilometers per hour", speed * 3.6);
135            ctx.speak(text);
136        }
137        if ctx.physics.entered_tag(id, "garage").is_some() {
138            let text = format!("You parked the {} in the garage. Well done!", self.name());
139            ctx.speak(text);
140            ctx.quit();
141        }
142        if ctx.pressed(Key::Escape) {
143            ctx.quit();
144        }
145    }
examples/platformer.rs (line 40)
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 unfollow(&mut self)

Stops following a body with the listener.

Source

pub fn followed(&self) -> Option<BodyId>

The body the listener follows, if any.

Source

pub fn play_on(&mut self, body: BodyId, name: &str) -> Sound

Plays a sound once at a body; the sound moves with the body.

Examples found in repository?
examples/platformer.rs (line 86)
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 loop_on(&mut self, body: BodyId, name: &str) -> Sound

Plays a looping sound at a body. It moves with the body and stops automatically when the body is removed. Perfect for enemies, engines, coins and beacons:

let enemy = ctx.physics.add(Body::kinematic(vec3(5.0, 5.0, 0.0)).with_tag("enemy"));
ctx.loop_on(enemy, "growl");
Examples found in repository?
examples/beacon.rs (line 46)
32    fn new(ctx: &mut Context) -> Self {
33        ctx.audio.register("step", Synth::noise().length(0.08).decay(60.0));
34        ctx.audio.register("wall", Synth::sine(90.0).length(0.15).decay(25.0).volume(0.8));
35        ctx.audio.register("beacon", Synth::sine(1320.0).length(0.08).then(Synth::silence(0.04)).then(Synth::sine(1320.0).length(0.08)).then(Synth::silence(0.8)));
36        ctx.audio.register("found", Synth::sine(440.0).sweep_to(1200.0).length(0.6).decay(4.0).volume(0.3));
37
38        // Every character is a 2 meter tile: walls, the player start and three beacons.
39        let level = ctx.physics.build_level(MAP, 2.0);
40        let player = ctx.physics.add(Body::kinematic(level.point('P').unwrap()).with_sphere(0.4).with_tag("player"));
41        ctx.follow(player); // the ears go wherever the player goes and face where it faces
42
43        let mut beacons = Vec::new();
44        for spot in level.points('B') {
45            let beacon = ctx.physics.add(Body::sensor(spot).with_sphere(0.8).with_tag("beacon"));
46            ctx.loop_on(beacon, "beacon"); // stops by itself when the beacon is removed
47            beacons.push(beacon);
48        }
49        Self { player, beacons, clock: Timer::new() }
50    }
More examples
Hide additional examples
examples/flight.rs (line 54)
31    fn new(ctx: &mut Context) -> Self {
32        ctx.audio.register("bird", Synth::sine(2200.0).sweep_to(3200.0).length(0.07).then(Synth::silence(0.05)).then(Synth::sine(2600.0).length(0.06)).then(Synth::silence(0.7)));
33        ctx.audio.register("hum", Synth::saw(70.0).length(1.0).volume(0.08));
34        ctx.audio.register("bump", Synth::sine(80.0).length(0.15).decay(25.0).volume(0.8));
35        ctx.audio.register("found", Synth::triangle(660.0).sweep_to(1320.0).length(0.4).decay(6.0).volume(0.3));
36
37        // A hall of 40 x 40 meters and 16 meters high: floor, roof and four walls.
38        // A box is given by its center and its full width (x), depth (y) and height (z).
39        let physics = &mut ctx.physics;
40        physics.add(Body::fixed(vec3(0.0, 0.0, -0.5)).with_box(40.0, 40.0, 1.0).with_tag("floor"));
41        physics.add(Body::fixed(vec3(0.0, 0.0, 16.5)).with_box(40.0, 40.0, 1.0).with_tag("roof"));
42        physics.add(Body::fixed(vec3(0.0, 20.5, 8.0)).with_box(40.0, 1.0, 16.0).with_tag("north wall"));
43        physics.add(Body::fixed(vec3(0.0, -20.5, 8.0)).with_box(40.0, 1.0, 16.0).with_tag("south wall"));
44        physics.add(Body::fixed(vec3(20.5, 0.0, 8.0)).with_box(1.0, 40.0, 16.0).with_tag("east wall"));
45        physics.add(Body::fixed(vec3(-20.5, 0.0, 8.0)).with_box(1.0, 40.0, 16.0).with_tag("west wall"));
46
47        // The drone starts in the middle, 2 meters above the floor. No gravity: it hovers.
48        let drone = ctx.physics.add(Body::kinematic(vec3(0.0, 0.0, 2.0)).with_sphere(0.5).with_tag("drone"));
49        ctx.follow(drone); // the ears use the drone's position, facing and pitch
50        ctx.audio.play_looped("hum");
51
52        for spot in BIRDS {
53            let bird = ctx.physics.add(Body::sensor(spot).with_sphere(1.0).with_tag("bird"));
54            ctx.loop_on(bird, "bird");
55        }
56        Self { drone, birds: BIRDS.len(), blocked: false }
57    }
examples/driving.rs (line 56)
29    fn new(ctx: &mut Context) -> Self {
30        // Engine: a low buzz that loops without clicks (whole cycles, no fades).
31        let engine = Synth::saw(55.0).length(1.0).attack(0.0).release(0.0).volume(0.25)
32            .with(Synth::square(110.0).length(1.0).attack(0.0).release(0.0).volume(0.08));
33        ctx.audio.register("engine", engine);
34        ctx.audio.register("horn", Synth::square(415.0).length(0.5).volume(0.25).with(Synth::square(523.0).length(0.5).volume(0.2)));
35        ctx.audio.register("shift", Synth::noise().length(0.04).decay(90.0).volume(0.3));
36        ctx.audio.register("crash", Synth::noise().length(0.4).decay(10.0).volume(0.8).with(Synth::sine(80.0).sweep_to(30.0).length(0.3)));
37        ctx.audio.register("start", Synth::saw(40.0).sweep_to(55.0).length(0.6).volume(0.3));
38        ctx.audio.register("garage", Synth::sine(880.0).length(0.15).decay(10.0));
39
40        let level = ctx.physics.build_level(MAP, 6.0);
41        let sounds = |v: Vehicle| {
42            v.with_engine_sound("engine")
43                .with_horn_sound("horn")
44                .with_shift_sound("shift")
45                .with_crash_sound("crash")
46                .with_start_sound("start")
47        };
48        let car_start = level.point('P').unwrap_or(Vec3::ZERO);
49        let car = ctx.physics.add(Body::car(car_start).with_facing(90.0).with_vehicle(sounds(Vehicle::car().with_fuel(5.0, 8.0))));
50        let truck_start = level.point('T').unwrap_or(Vec3::ZERO);
51        let truck = ctx.physics.add(
52            Body::truck(truck_start).with_facing(90.0).with_vehicle(sounds(Vehicle::truck()).engine_off()),
53        );
54        let garage_at = level.point('G').unwrap_or(Vec3::ZERO);
55        let garage = ctx.physics.add(Body::sensor(garage_at).with_box(6.0, 6.0, 3.0).with_tag("garage"));
56        ctx.loop_on(garage, "garage");
57        ctx.follow(car);
58        Self { car, truck, driving: car, garage }
59    }
examples/platformer.rs (line 41)
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 attach(&mut self, body: BodyId, sound: Sound)

Makes any positioned sound follow a body, e.g. one made with the sound builder: let s = ctx.audio.sound("hum").volume(0.3).looped().at(pos).play(); ctx.attach(body, s);

Source

pub fn stop_sounds_on(&mut self, body: BodyId)

Stops every sound attached to a body.

Source

pub fn drive(&mut self, vehicle: BodyId)

Drives a vehicle with the keyboard: Up or W is the gas pedal, Down or S brakes and, once the vehicle stands still, reverses, Left / Right or A / D steer, Space is the handbrake. Call it every frame. Does nothing for bodies without a vehicle. While a menu is open all pedals are released.

let car = ctx.physics.add(Body::car(Vec3::ZERO));
ctx.follow(car);
// in update():
ctx.drive(car);
Examples found in repository?
examples/driving.rs (line 73)
71    fn update(&mut self, ctx: &mut Context) {
72        let id = self.driving;
73        ctx.drive(id);
74
75        if ctx.pressed(Key::H) {
76            ctx.horn(id);
77        }
78        if ctx.pressed(Key::E) {
79            let running = ctx.physics.vehicle(id).is_some_and(|v| v.is_engine_on());
80            if running {
81                ctx.stop_engine(id);
82                ctx.speak("Engine off");
83            } else if !ctx.start_engine(id) {
84                ctx.speak("The tank is empty");
85            }
86        }
87        if ctx.pressed(Key::Tab) {
88            if let Some(v) = ctx.physics.vehicle_mut(id) {
89                v.throttle = 0.0;
90                v.brake = 0.0;
91                v.handbrake = true;
92            }
93            self.driving = if id == self.car { self.truck } else { self.car };
94            ctx.follow(self.driving);
95            let text = format!("You switch to the {}. E starts the engine.", self.name());
96            ctx.speak(text);
97        }
98        if ctx.pressed(Key::L) && self.driving == self.truck {
99            let speed = ctx.physics.vehicle(self.truck).map_or(0.0, |v| v.speed().abs());
100            if speed > 0.5 {
101                ctx.speak("Stop first");
102            } else if let Some(v) = ctx.physics.vehicle_mut(self.truck) {
103                let load = if v.load > 0.0 { 0.0 } else { v.max_load };
104                v.set_load(load);
105                let text = format!("Load {} tons, total weight {} tons", load / 1000.0, ctx.physics.weight(self.truck) / 1000.0);
106                ctx.speak(text);
107            }
108        }
109        if ctx.pressed(Key::F)
110            && let Some(v) = ctx.physics.vehicle(id)
111        {
112            let mut text = format!("{}, gear {}", v.describe_speed(), v.gear_name());
113            if let Some(fuel) = v.fuel() {
114                text.push_str(&format!(", {fuel:.1} liters left"));
115            }
116            ctx.speak(text);
117        }
118        if ctx.pressed(Key::G) {
119            let text = ctx.physics.describe(id, self.garage);
120            ctx.speak(text);
121        }
122        if ctx.pressed(Key::F1) {
123            ctx.speak("Up gas, down brake and reverse, left and right steer, space handbrake, H horn, E engine, Tab switch vehicle, L load the truck, F speed, G garage.");
124        }
125
126        if let Some(gear) = ctx.physics.gear_changed(id)
127            && gear == -1
128        {
129            ctx.speak("Reverse");
130        }
131        if let Some((_, speed)) = ctx.physics.crashed(id)
132            && speed > 5.0
133        {
134            let text = format!("Crash at {:.0} kilometers per hour", speed * 3.6);
135            ctx.speak(text);
136        }
137        if ctx.physics.entered_tag(id, "garage").is_some() {
138            let text = format!("You parked the {} in the garage. Well done!", self.name());
139            ctx.speak(text);
140            ctx.quit();
141        }
142        if ctx.pressed(Key::Escape) {
143            ctx.quit();
144        }
145    }
Source

pub fn horn(&mut self, vehicle: BodyId) -> Sound

Honks: plays the vehicle’s horn sound at the vehicle. Returns a silent handle if it has none.

Examples found in repository?
examples/driving.rs (line 76)
71    fn update(&mut self, ctx: &mut Context) {
72        let id = self.driving;
73        ctx.drive(id);
74
75        if ctx.pressed(Key::H) {
76            ctx.horn(id);
77        }
78        if ctx.pressed(Key::E) {
79            let running = ctx.physics.vehicle(id).is_some_and(|v| v.is_engine_on());
80            if running {
81                ctx.stop_engine(id);
82                ctx.speak("Engine off");
83            } else if !ctx.start_engine(id) {
84                ctx.speak("The tank is empty");
85            }
86        }
87        if ctx.pressed(Key::Tab) {
88            if let Some(v) = ctx.physics.vehicle_mut(id) {
89                v.throttle = 0.0;
90                v.brake = 0.0;
91                v.handbrake = true;
92            }
93            self.driving = if id == self.car { self.truck } else { self.car };
94            ctx.follow(self.driving);
95            let text = format!("You switch to the {}. E starts the engine.", self.name());
96            ctx.speak(text);
97        }
98        if ctx.pressed(Key::L) && self.driving == self.truck {
99            let speed = ctx.physics.vehicle(self.truck).map_or(0.0, |v| v.speed().abs());
100            if speed > 0.5 {
101                ctx.speak("Stop first");
102            } else if let Some(v) = ctx.physics.vehicle_mut(self.truck) {
103                let load = if v.load > 0.0 { 0.0 } else { v.max_load };
104                v.set_load(load);
105                let text = format!("Load {} tons, total weight {} tons", load / 1000.0, ctx.physics.weight(self.truck) / 1000.0);
106                ctx.speak(text);
107            }
108        }
109        if ctx.pressed(Key::F)
110            && let Some(v) = ctx.physics.vehicle(id)
111        {
112            let mut text = format!("{}, gear {}", v.describe_speed(), v.gear_name());
113            if let Some(fuel) = v.fuel() {
114                text.push_str(&format!(", {fuel:.1} liters left"));
115            }
116            ctx.speak(text);
117        }
118        if ctx.pressed(Key::G) {
119            let text = ctx.physics.describe(id, self.garage);
120            ctx.speak(text);
121        }
122        if ctx.pressed(Key::F1) {
123            ctx.speak("Up gas, down brake and reverse, left and right steer, space handbrake, H horn, E engine, Tab switch vehicle, L load the truck, F speed, G garage.");
124        }
125
126        if let Some(gear) = ctx.physics.gear_changed(id)
127            && gear == -1
128        {
129            ctx.speak("Reverse");
130        }
131        if let Some((_, speed)) = ctx.physics.crashed(id)
132            && speed > 5.0
133        {
134            let text = format!("Crash at {:.0} kilometers per hour", speed * 3.6);
135            ctx.speak(text);
136        }
137        if ctx.physics.entered_tag(id, "garage").is_some() {
138            let text = format!("You parked the {} in the garage. Well done!", self.name());
139            ctx.speak(text);
140            ctx.quit();
141        }
142        if ctx.pressed(Key::Escape) {
143            ctx.quit();
144        }
145    }
Source

pub fn start_engine(&mut self, vehicle: BodyId) -> bool

Starts the engine of a vehicle and plays its start sound. Returns false if the body is no vehicle, the engine already runs or the tank is empty.

Examples found in repository?
examples/driving.rs (line 83)
71    fn update(&mut self, ctx: &mut Context) {
72        let id = self.driving;
73        ctx.drive(id);
74
75        if ctx.pressed(Key::H) {
76            ctx.horn(id);
77        }
78        if ctx.pressed(Key::E) {
79            let running = ctx.physics.vehicle(id).is_some_and(|v| v.is_engine_on());
80            if running {
81                ctx.stop_engine(id);
82                ctx.speak("Engine off");
83            } else if !ctx.start_engine(id) {
84                ctx.speak("The tank is empty");
85            }
86        }
87        if ctx.pressed(Key::Tab) {
88            if let Some(v) = ctx.physics.vehicle_mut(id) {
89                v.throttle = 0.0;
90                v.brake = 0.0;
91                v.handbrake = true;
92            }
93            self.driving = if id == self.car { self.truck } else { self.car };
94            ctx.follow(self.driving);
95            let text = format!("You switch to the {}. E starts the engine.", self.name());
96            ctx.speak(text);
97        }
98        if ctx.pressed(Key::L) && self.driving == self.truck {
99            let speed = ctx.physics.vehicle(self.truck).map_or(0.0, |v| v.speed().abs());
100            if speed > 0.5 {
101                ctx.speak("Stop first");
102            } else if let Some(v) = ctx.physics.vehicle_mut(self.truck) {
103                let load = if v.load > 0.0 { 0.0 } else { v.max_load };
104                v.set_load(load);
105                let text = format!("Load {} tons, total weight {} tons", load / 1000.0, ctx.physics.weight(self.truck) / 1000.0);
106                ctx.speak(text);
107            }
108        }
109        if ctx.pressed(Key::F)
110            && let Some(v) = ctx.physics.vehicle(id)
111        {
112            let mut text = format!("{}, gear {}", v.describe_speed(), v.gear_name());
113            if let Some(fuel) = v.fuel() {
114                text.push_str(&format!(", {fuel:.1} liters left"));
115            }
116            ctx.speak(text);
117        }
118        if ctx.pressed(Key::G) {
119            let text = ctx.physics.describe(id, self.garage);
120            ctx.speak(text);
121        }
122        if ctx.pressed(Key::F1) {
123            ctx.speak("Up gas, down brake and reverse, left and right steer, space handbrake, H horn, E engine, Tab switch vehicle, L load the truck, F speed, G garage.");
124        }
125
126        if let Some(gear) = ctx.physics.gear_changed(id)
127            && gear == -1
128        {
129            ctx.speak("Reverse");
130        }
131        if let Some((_, speed)) = ctx.physics.crashed(id)
132            && speed > 5.0
133        {
134            let text = format!("Crash at {:.0} kilometers per hour", speed * 3.6);
135            ctx.speak(text);
136        }
137        if ctx.physics.entered_tag(id, "garage").is_some() {
138            let text = format!("You parked the {} in the garage. Well done!", self.name());
139            ctx.speak(text);
140            ctx.quit();
141        }
142        if ctx.pressed(Key::Escape) {
143            ctx.quit();
144        }
145    }
Source

pub fn stop_engine(&mut self, vehicle: BodyId)

Stops the engine of a vehicle. Its engine sound fades out and it rolls out.

Examples found in repository?
examples/driving.rs (line 81)
71    fn update(&mut self, ctx: &mut Context) {
72        let id = self.driving;
73        ctx.drive(id);
74
75        if ctx.pressed(Key::H) {
76            ctx.horn(id);
77        }
78        if ctx.pressed(Key::E) {
79            let running = ctx.physics.vehicle(id).is_some_and(|v| v.is_engine_on());
80            if running {
81                ctx.stop_engine(id);
82                ctx.speak("Engine off");
83            } else if !ctx.start_engine(id) {
84                ctx.speak("The tank is empty");
85            }
86        }
87        if ctx.pressed(Key::Tab) {
88            if let Some(v) = ctx.physics.vehicle_mut(id) {
89                v.throttle = 0.0;
90                v.brake = 0.0;
91                v.handbrake = true;
92            }
93            self.driving = if id == self.car { self.truck } else { self.car };
94            ctx.follow(self.driving);
95            let text = format!("You switch to the {}. E starts the engine.", self.name());
96            ctx.speak(text);
97        }
98        if ctx.pressed(Key::L) && self.driving == self.truck {
99            let speed = ctx.physics.vehicle(self.truck).map_or(0.0, |v| v.speed().abs());
100            if speed > 0.5 {
101                ctx.speak("Stop first");
102            } else if let Some(v) = ctx.physics.vehicle_mut(self.truck) {
103                let load = if v.load > 0.0 { 0.0 } else { v.max_load };
104                v.set_load(load);
105                let text = format!("Load {} tons, total weight {} tons", load / 1000.0, ctx.physics.weight(self.truck) / 1000.0);
106                ctx.speak(text);
107            }
108        }
109        if ctx.pressed(Key::F)
110            && let Some(v) = ctx.physics.vehicle(id)
111        {
112            let mut text = format!("{}, gear {}", v.describe_speed(), v.gear_name());
113            if let Some(fuel) = v.fuel() {
114                text.push_str(&format!(", {fuel:.1} liters left"));
115            }
116            ctx.speak(text);
117        }
118        if ctx.pressed(Key::G) {
119            let text = ctx.physics.describe(id, self.garage);
120            ctx.speak(text);
121        }
122        if ctx.pressed(Key::F1) {
123            ctx.speak("Up gas, down brake and reverse, left and right steer, space handbrake, H horn, E engine, Tab switch vehicle, L load the truck, F speed, G garage.");
124        }
125
126        if let Some(gear) = ctx.physics.gear_changed(id)
127            && gear == -1
128        {
129            ctx.speak("Reverse");
130        }
131        if let Some((_, speed)) = ctx.physics.crashed(id)
132            && speed > 5.0
133        {
134            let text = format!("Crash at {:.0} kilometers per hour", speed * 3.6);
135            ctx.speak(text);
136        }
137        if ctx.physics.entered_tag(id, "garage").is_some() {
138            let text = format!("You parked the {} in the garage. Well done!", self.name());
139            ctx.speak(text);
140            ctx.quit();
141        }
142        if ctx.pressed(Key::Escape) {
143            ctx.quit();
144        }
145    }

Trait Implementations§

Source§

impl Debug for Context

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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<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