Skip to main content

World

Struct World 

Source
pub struct World { /* private fields */ }
Expand description

The physics world. Reach it through ctx.physics.

Implementations§

Source§

impl World

Source

pub fn build_level(&mut self, map: &str, tile_size: f32) -> Level

Creates walls from a text map. See the level module.

Examples found in repository?
examples/beacon.rs (line 39)
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/driving.rs (line 40)
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    }
Source§

impl World

Source

pub fn new() -> Self

Creates an empty world without gravity.

Source

pub fn set_gravity(&mut self, gravity: Vec3)

Sets gravity, e.g. EARTH_GRAVITY. Default is no gravity, which suits top down games. Also available as Engine::gravity.

Source

pub fn up(&self) -> Vec3

Direction pointing away from gravity (+z when there is no gravity).

Source

pub fn gravity(&self) -> Vec3

Current gravity.

Source

pub fn add(&mut self, body: Body) -> BodyId

Adds a body and returns its id.

Examples found in repository?
examples/beacon.rs (line 40)
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 40)
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 49)
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 29)
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 remove(&mut self, id: BodyId) -> Option<Body>

Removes a body. Returns it if it existed.

Examples found in repository?
examples/platformer.rs (line 89)
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/beacon.rs (line 95)
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 121)
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    }
Source

pub fn clear(&mut self)

Removes every body.

Source

pub fn get(&self, id: BodyId) -> Option<&Body>

Access a body.

Source

pub fn get_mut(&mut self, id: BodyId) -> Option<&mut Body>

Mutable access to a body.

Source

pub fn position(&self, id: BodyId) -> Vec3

Position of a body (zero if it does not exist).

Examples found in repository?
examples/platformer.rs (line 102)
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/beacon.rs (line 80)
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 102)
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    }
Source

pub fn set_position(&mut self, id: BodyId, position: Vec3)

Teleports a body without collision checks.

Source

pub fn velocity(&self, id: BodyId) -> Vec3

Velocity of a body (zero if it does not exist).

Examples found in repository?
examples/platformer.rs (line 68)
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 set_velocity(&mut self, id: BodyId, velocity: Vec3)

Sets the velocity of a body.

Examples found in repository?
examples/platformer.rs (line 65)
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 apply_impulse(&mut self, id: BodyId, impulse: Vec3)

Adds impulse (meters per second) to the velocity at once, regardless of mass, e.g. a kick or knockback: apply_impulse(ball, vec3(0.0, 10.0, 0.0)).

Source

pub fn apply_force(&mut self, id: BodyId, force: Vec3)

Pushes a body with a force in newtons during the next physics step, e.g. wind, a conveyor belt or a rocket. Each second, a force adds force / mass meters per second to the velocity, so 20 N speed up a 1 kg body by 20 m/s per second and a 10 kg body by 2 m/s per second. Call it every frame for a continuous force.

Source

pub fn jump(&mut self, id: BodyId, height: f32) -> bool

Makes a dynamic body standing on the ground jump so that it reaches height meters above where it stands, taking the current gravity into account. Returns false (and does nothing) while in the air, without gravity, or for other body kinds.

if ctx.input.pressed(Key::Space) && ctx.physics.jump(player, 1.5) {
    ctx.audio.play("jump");
}
Examples found in repository?
examples/platformer.rs (line 71)
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 on_ground(&self, id: BodyId) -> bool

Whether a dynamic body stands on something (always false for other kinds).

Examples found in repository?
examples/platformer.rs (line 74)
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 tag(&self, id: BodyId) -> &str

The tag of a body (“” if it does not exist).

Examples found in repository?
examples/flight.rs (line 64)
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    }
More examples
Hide additional examples
examples/platformer.rs (line 84)
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 bodies(&self) -> impl Iterator<Item = (BodyId, &Body)>

Iterates over all bodies.

Source

pub fn with_tag<'a>(&'a self, tag: &'a str) -> impl Iterator<Item = BodyId> + 'a

All bodies with a tag.

Source

pub fn len(&self) -> usize

Number of bodies.

Source

pub fn is_empty(&self) -> bool

Whether the world is empty.

Source

pub fn events(&self) -> &[PhysicsEvent]

Events from the physics steps of this frame.

Source

pub fn sensors_of(&self, body: BodyId) -> Vec<BodyId>

Sensors body is currently inside.

Source

pub fn overlaps(&self, a: BodyId, b: BodyId) -> bool

Whether two bodies overlap right now.

Source

pub fn at_point(&self, point: Vec3) -> Vec<BodyId>

Bodies whose shape contains point.

Source

pub fn in_radius(&self, center: Vec3, radius: f32) -> Vec<BodyId>

Bodies whose center lies within radius of center, nearest first.

Source

pub fn nearest_with_tag(&self, center: Vec3, tag: &str) -> Option<BodyId>

Nearest body with the given tag.

Examples found in repository?
examples/beacon.rs (line 81)
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    }
More examples
Hide additional examples
examples/flight.rs (line 103)
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    }
Source

pub fn raycast( &self, origin: Vec3, direction: Vec3, max_distance: f32, ignore: Option<BodyId>, ) -> Option<RayHit>

Sends a straight line from origin in direction (any length) and returns the first solid body hit within max_distance meters. ignore is skipped: pass the caster itself, otherwise the ray hits it first. Sensors are ignored.

Source

pub fn move_by(&mut self, id: BodyId, delta: Vec3) -> MoveResult

Moves a body by delta meters right now, sliding along and stopping at solid bodies, and pushing dynamic bodies out of the way. This is the easiest way to move a player: walls block, and you learn what was hit. Sensor events are updated at once; PhysicsEvent::CollisionStarted is not created.

Source

pub fn step(&mut self, dt: f32)

Advances the simulation by dt seconds. The engine calls this for you.

Source§

impl World

Convenience helpers for typical audio game code.

Source

pub fn facing(&self, id: BodyId) -> f32

Compass direction a body faces (0 if it does not exist).

Examples found in repository?
examples/beacon.rs (line 90)
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    }
More examples
Hide additional examples
examples/flight.rs (line 112)
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    }
Source

pub fn set_facing(&mut self, id: BodyId, degrees: f32)

Sets the compass direction a body faces.

Source

pub fn turn(&mut self, id: BodyId, degrees: f32) -> f32

Turns a body by degrees (positive = clockwise / right, negative = left) and returns the new facing, 0.0 to 360.0.

Examples found in repository?
examples/beacon.rs (line 64)
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    }
More examples
Hide additional examples
examples/flight.rs (line 80)
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    }
Source

pub fn pitch(&self, id: BodyId) -> f32

How far a body looks up (positive) or down (negative) in degrees (0 if it does not exist).

Examples found in repository?
examples/flight.rs (line 112)
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    }
Source

pub fn set_pitch(&mut self, id: BodyId, degrees: f32)

Sets how far a body looks up or down in degrees, clamped to -90..=90.

Source

pub fn tilt(&mut self, id: BodyId, degrees: f32) -> f32

Tilts a body’s view up (positive degrees) or down (negative) and returns the new pitch, -90.0 to 90.0. Together with World::turn this aims in any 3D direction.

Examples found in repository?
examples/flight.rs (line 84)
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    }
Source

pub fn look_at(&mut self, id: BodyId, point: Vec3)

Turns a body so that it faces point: sets both the facing and the pitch.

Source

pub fn move_forward(&mut self, id: BodyId, distance: f32) -> MoveResult

Moves a body distance meters in the direction it faces (negative = backwards), sliding along walls. The pitch counts too: a body looking 30° up also rises. With the default pitch of 0 it moves level. See World::move_by.

Examples found in repository?
examples/beacon.rs (line 72)
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    }
More examples
Hide additional examples
examples/flight.rs (line 92)
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    }
Source

pub fn move_up(&mut self, id: BodyId, distance: f32) -> MoveResult

Moves a body distance meters straight up (negative = down), sliding along walls, e.g. to fly, swim, climb a ladder or take a lift. “Up” is +z, or against gravity if gravity points elsewhere. See World::move_by.

Examples found in repository?
examples/flight.rs (line 97)
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    }
Source

pub fn strafe(&mut self, id: BodyId, distance: f32) -> MoveResult

Moves a body sideways, distance meters to its right (negative = left).

Source

pub fn distance(&self, a: BodyId, b: BodyId) -> f32

Distance between two bodies’ centers (infinity if one does not exist).

Source

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

Where target is from the point of view of from, ready to speak: "3 o'clock, 12 meters". Uses the facing of from: 12 o’clock is straight ahead, 3 o’clock to the right, 6 o’clock behind. A height difference of a meter or more is added ("12 o'clock, 11 meters, 4 meters above"), and a target straight above or below is "directly above, 5 meters". See math::describe_position.

ctx.speak(ctx.physics.describe(player, beacon));
Examples found in repository?
examples/beacon.rs (line 83)
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    }
More examples
Hide additional examples
examples/flight.rs (line 105)
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 119)
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 landed(&self, body: BodyId) -> Option<f32>

If body landed on something this frame: the falling speed in meters per second. With earth gravity, a 1 m fall lands at about 4.4 m/s, a 3 m fall at 7.7 m/s.

Examples found in repository?
examples/platformer.rs (line 77)
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 collided(&self, body: BodyId) -> Option<(BodyId, f32)>

If body started touching another solid body this frame: that body and the impact speed.

Examples found in repository?
examples/platformer.rs (line 83)
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 entered(&self, body: BodyId) -> Option<BodyId>

If body walked into a sensor this frame: the sensor.

Source

pub fn entered_tag(&self, body: BodyId, tag: &str) -> Option<BodyId>

If body walked into a sensor with this tag this frame: the sensor.

if let Some(coin) = ctx.physics.entered_tag(player, "coin") {
    ctx.physics.remove(coin);   // sounds attached with ctx.loop_on stop by themselves
    ctx.play("pickup");
}
Examples found in repository?
examples/platformer.rs (line 88)
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/beacon.rs (line 94)
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 120)
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 137)
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 left(&self, body: BodyId) -> Option<BodyId>

If body left a sensor this frame: the sensor.

Source

pub fn vehicle(&self, id: BodyId) -> Option<&Vehicle>

The vehicle part of a body, if it is a car or truck.

if let Some(v) = ctx.physics.vehicle(car) {
    let text = v.describe_speed();
    ctx.speak(text);
}
Examples found in repository?
examples/driving.rs (line 79)
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 vehicle_mut(&mut self, id: BodyId) -> Option<&mut Vehicle>

Mutable access to the vehicle part of a body: pedals, steering, load, fuel, settings.

Examples found in repository?
examples/driving.rs (line 88)
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 weight(&self, id: BodyId) -> f32

Total weight of a body in kilograms: its mass plus the load of its vehicle (0 if the body does not exist).

Examples found in repository?
examples/driving.rs (line 105)
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 speed(&self, id: BodyId) -> f32

How fast a body moves in meters per second: the length of its velocity. For vehicles this is their driving speed. Kinematic bodies moved with move_by have no velocity, so they report 0.

Source

pub fn crashed(&self, vehicle: BodyId) -> Option<(BodyId, f32)>

If vehicle crashed into something this frame: what it hit and the speed lost in meters per second.

Examples found in repository?
examples/driving.rs (line 131)
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 gear_changed(&self, vehicle: BodyId) -> Option<i32>

If vehicle changed gear this frame: the new gear (-1 reverse, 0 neutral, 1 and up).

Examples found in repository?
examples/driving.rs (line 126)
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 triggered(&self, sensor: BodyId) -> Option<BodyId>

Whether anything entered sensor this frame; returns the body.

Trait Implementations§

Source§

impl Clone for World

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for World

Source§

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

Formats the value using the given formatter. Read more
Source§

impl Default for World

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for World

§

impl RefUnwindSafe for World

§

impl Send for World

§

impl Sync for World

§

impl Unpin for World

§

impl UnsafeUnpin for World

§

impl UnwindSafe for World

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

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

Source§

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

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

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

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

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

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

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

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

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

Source§

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

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

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

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

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

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

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

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

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

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

Source§

fn into_sample(self) -> T

Source§

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

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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