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: TimeFrame timing and scheduling.
input: InputKeyboard state.
audio: AudioSound effects, positioned sounds and music.
speech: SpeechScreen reader / text-to-speech output.
ui: UiVirtual menus and dialogs.
physics: WorldPhysics world.
storage: StorageValues that are saved when the game quits (high scores, settings).
lang: LangTranslations and the current language.
Implementations§
Source§impl Context
impl Context
Sourcepub fn quit(&mut self)
pub fn quit(&mut self)
Requests the game to end after this frame.
Examples found in repository?
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
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}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 }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 }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 }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 }Sourcepub fn is_quitting(&self) -> bool
pub fn is_quitting(&self) -> bool
Whether Context::quit was called.
Sourcepub fn title(&self) -> &str
pub fn title(&self) -> &str
The game title given to Engine::new.
Sourcepub fn switch_scene(&mut self, scene: impl Scene)
pub fn switch_scene(&mut self, scene: impl Scene)
Replaces the current scene with another one after this frame.
Examples found in repository?
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 }Sourcepub fn push_scene(&mut self, scene: impl Scene)
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?
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 }Sourcepub fn pop_scene(&mut self)
pub fn pop_scene(&mut self)
Removes the current scene after this frame. Popping the last scene quits the game.
Sourcepub fn insert<T: 'static>(&mut self, value: T)
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?
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 }Sourcepub fn get<T: 'static>(&self) -> Option<&T>
pub fn get<T: 'static>(&self) -> Option<&T>
Reads a shared value stored with Context::insert.
Examples found in repository?
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 }Sourcepub fn get_mut<T: 'static>(&mut self) -> Option<&mut T>
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T>
Mutable access to a shared value.
Examples found in repository?
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 }Sourcepub fn get_or_default<T: Default + 'static>(&mut self) -> &mut T
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§impl Context
impl Context
Sourcepub fn speak(&mut self, text: impl AsRef<str>)
pub fn speak(&mut self, text: impl AsRef<str>)
Speaks text, interrupting current speech. Same as ctx.speech.speak(text).
Examples found in repository?
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
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 }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 }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 }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 }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}Sourcepub fn tr(&self, key: &str) -> String
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"));Sourcepub fn tr_with(&self, key: &str, args: &[(&str, &dyn Display)]) -> String
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)]));Sourcepub fn set_language(&mut self, code: &str) -> bool
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();
}Sourcepub fn describe(&self, from: BodyId, target: BodyId) -> String
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.
Sourcepub fn play(&mut self, name: &str) -> Sound
pub fn play(&mut self, name: &str) -> Sound
Plays a sound once. Same as ctx.audio.play(name).
Examples found in repository?
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
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 }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 }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 }Sourcepub fn play_at(&mut self, name: &str, position: Vec3) -> Sound
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).
Sourcepub fn pressed(&self, key: Key) -> bool
pub fn pressed(&self, key: Key) -> bool
Whether a key went down this frame. Same as ctx.input.pressed(key).
Examples found in repository?
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
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 }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 }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 }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 }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 }Sourcepub fn once(&mut self, name: &str) -> bool
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?
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}Sourcepub fn follow(&mut self, body: BodyId)
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?
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
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 }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 }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 }Sourcepub fn play_on(&mut self, body: BodyId, name: &str) -> Sound
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?
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 }Sourcepub fn loop_on(&mut self, body: BodyId, name: &str) -> Sound
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?
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
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 }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 }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 }Sourcepub fn attach(&mut self, body: BodyId, sound: Sound)
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);
Sourcepub fn stop_sounds_on(&mut self, body: BodyId)
pub fn stop_sounds_on(&mut self, body: BodyId)
Stops every sound attached to a body.
Sourcepub fn drive(&mut self, vehicle: BodyId)
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?
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 }Sourcepub fn horn(&mut self, vehicle: BodyId) -> Sound
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?
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 }Sourcepub fn start_engine(&mut self, vehicle: BodyId) -> bool
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?
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 }Sourcepub fn stop_engine(&mut self, vehicle: BodyId)
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?
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§
Auto Trait Implementations§
impl !Freeze for Context
impl !RefUnwindSafe for Context
impl !Send for Context
impl !Sync for Context
impl !UnwindSafe for Context
impl Unpin for Context
impl UnsafeUnpin for Context
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
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)
fn as_any(&self) -> &(dyn Any + 'static)
&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)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.