Skip to main content

CharacterBody

Struct CharacterBody 

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

A walking body: feet-positioned collision box + velocity + contact flags. Construct with CharacterBody::new, place with teleport, then call walk once per frame.

Implementations§

Source§

impl CharacterBody

Source

pub fn new(def: CharacterDef) -> Self

A body at the world origin with zero velocity — call teleport before the first walk.

Examples found in repository?
examples/book_controller.rs (lines 68-78)
61fn main() {
62    let scene = build_world();
63
64    // ANCHOR: setup
65    // A walking body: a feet-positioned collision box, f64 world.
66    // Distances are voxels, times seconds; +z is DOWN, so gravity is
67    // positive and a jump impulse negative.
68    let mut body = CharacterBody::new(CharacterDef {
69        radius: 0.4,      // xy half-extent of the box
70        height: 1.8,      // feet → head (toward -z)
71        eye_height: 1.62, // feet → camera anchor
72        step_up: 1.05,    // auto-step ledges up to 1 voxel
73        solidity: Solidity {
74            bedrock_blocks: false, // match your renderer's policy
75            passable: Some(water_passes),
76        },
77        ..CharacterDef::default()
78    });
79    body.teleport(DVec3::new(50.0, 50.0, 95.0)); // FEET position
80                                                 // ANCHOR_END: setup
81
82    // ANCHOR: frame
83    // Per frame: build a wish direction from input (unit-length or
84    // zero — walk() clamps), call walk() EVERY frame (a zero wish is
85    // what stops the body), then anchor the camera at the eye.
86    let dt = 1.0 / 60.0;
87    for frame in 0..480 {
88        let input = WalkInput {
89            wish: DVec3::new(1.0, 0.2, 0.0), // toward the ledge
90            jump: frame == 120,              // one hop on the way
91        };
92        body.walk(&scene, dt, input);
93    }
94    let eye = body.eye_pos(); // = Camera::from_yaw_pitch(eye.into(), yaw, pitch)
95                              // ANCHOR_END: frame
96
97    // The trajectory the walk above promises, pinned:
98    assert!(body.on_ground(), "settled after the hop");
99    // It fell to the floor plane, then stepped up the 1-voxel ledge
100    // (surface z = 99) while walking +x.
101    assert!(body.pos().x > 90.0, "reached the ledge region");
102    assert!(
103        (body.pos().z - 99.0).abs() < 0.01,
104        "standing on the ledge, feet at {}",
105        body.pos().z
106    );
107    assert!(eye.z < body.pos().z, "the eye is above the feet (-z is up)");
108
109    // Fly mode is the demos' free camera: no gravity, full-3D wish,
110    // instant start/stop, still sliding along walls.
111    body.set_mode(MoveMode::Fly);
112    body.walk(&scene, dt, WalkInput::default());
113    assert_eq!(body.vel(), DVec3::ZERO, "fly idles in place");
114
115    // The water curtain from `build_world` is passable: probe the
116    // veto directly (the same query walk() uses).
117    let wading = roxlap_scene::box_overlaps_solid(
118        &scene,
119        DVec3::new(40.2, 50.0, 90.0),
120        DVec3::new(40.8, 50.6, 91.8),
121        body.def().solidity,
122    );
123    assert!(!wading, "water is passable under the veto");
124
125    println!("book_controller: all controller assertions hold");
126}
Source

pub fn mode(&self) -> MoveMode

Current movement mode.

Source

pub fn set_mode(&mut self, mode: MoveMode)

Switch movement mode. Velocity is kept — dropping out of Fly mid-air falls with whatever speed you had.

Examples found in repository?
examples/book_controller.rs (line 111)
61fn main() {
62    let scene = build_world();
63
64    // ANCHOR: setup
65    // A walking body: a feet-positioned collision box, f64 world.
66    // Distances are voxels, times seconds; +z is DOWN, so gravity is
67    // positive and a jump impulse negative.
68    let mut body = CharacterBody::new(CharacterDef {
69        radius: 0.4,      // xy half-extent of the box
70        height: 1.8,      // feet → head (toward -z)
71        eye_height: 1.62, // feet → camera anchor
72        step_up: 1.05,    // auto-step ledges up to 1 voxel
73        solidity: Solidity {
74            bedrock_blocks: false, // match your renderer's policy
75            passable: Some(water_passes),
76        },
77        ..CharacterDef::default()
78    });
79    body.teleport(DVec3::new(50.0, 50.0, 95.0)); // FEET position
80                                                 // ANCHOR_END: setup
81
82    // ANCHOR: frame
83    // Per frame: build a wish direction from input (unit-length or
84    // zero — walk() clamps), call walk() EVERY frame (a zero wish is
85    // what stops the body), then anchor the camera at the eye.
86    let dt = 1.0 / 60.0;
87    for frame in 0..480 {
88        let input = WalkInput {
89            wish: DVec3::new(1.0, 0.2, 0.0), // toward the ledge
90            jump: frame == 120,              // one hop on the way
91        };
92        body.walk(&scene, dt, input);
93    }
94    let eye = body.eye_pos(); // = Camera::from_yaw_pitch(eye.into(), yaw, pitch)
95                              // ANCHOR_END: frame
96
97    // The trajectory the walk above promises, pinned:
98    assert!(body.on_ground(), "settled after the hop");
99    // It fell to the floor plane, then stepped up the 1-voxel ledge
100    // (surface z = 99) while walking +x.
101    assert!(body.pos().x > 90.0, "reached the ledge region");
102    assert!(
103        (body.pos().z - 99.0).abs() < 0.01,
104        "standing on the ledge, feet at {}",
105        body.pos().z
106    );
107    assert!(eye.z < body.pos().z, "the eye is above the feet (-z is up)");
108
109    // Fly mode is the demos' free camera: no gravity, full-3D wish,
110    // instant start/stop, still sliding along walls.
111    body.set_mode(MoveMode::Fly);
112    body.walk(&scene, dt, WalkInput::default());
113    assert_eq!(body.vel(), DVec3::ZERO, "fly idles in place");
114
115    // The water curtain from `build_world` is passable: probe the
116    // veto directly (the same query walk() uses).
117    let wading = roxlap_scene::box_overlaps_solid(
118        &scene,
119        DVec3::new(40.2, 50.0, 90.0),
120        DVec3::new(40.8, 50.6, 91.8),
121        body.def().solidity,
122    );
123    assert!(!wading, "water is passable under the veto");
124
125    println!("book_controller: all controller assertions hold");
126}
Source

pub fn def(&self) -> &CharacterDef

The construction parameters.

Examples found in repository?
examples/book_controller.rs (line 121)
61fn main() {
62    let scene = build_world();
63
64    // ANCHOR: setup
65    // A walking body: a feet-positioned collision box, f64 world.
66    // Distances are voxels, times seconds; +z is DOWN, so gravity is
67    // positive and a jump impulse negative.
68    let mut body = CharacterBody::new(CharacterDef {
69        radius: 0.4,      // xy half-extent of the box
70        height: 1.8,      // feet → head (toward -z)
71        eye_height: 1.62, // feet → camera anchor
72        step_up: 1.05,    // auto-step ledges up to 1 voxel
73        solidity: Solidity {
74            bedrock_blocks: false, // match your renderer's policy
75            passable: Some(water_passes),
76        },
77        ..CharacterDef::default()
78    });
79    body.teleport(DVec3::new(50.0, 50.0, 95.0)); // FEET position
80                                                 // ANCHOR_END: setup
81
82    // ANCHOR: frame
83    // Per frame: build a wish direction from input (unit-length or
84    // zero — walk() clamps), call walk() EVERY frame (a zero wish is
85    // what stops the body), then anchor the camera at the eye.
86    let dt = 1.0 / 60.0;
87    for frame in 0..480 {
88        let input = WalkInput {
89            wish: DVec3::new(1.0, 0.2, 0.0), // toward the ledge
90            jump: frame == 120,              // one hop on the way
91        };
92        body.walk(&scene, dt, input);
93    }
94    let eye = body.eye_pos(); // = Camera::from_yaw_pitch(eye.into(), yaw, pitch)
95                              // ANCHOR_END: frame
96
97    // The trajectory the walk above promises, pinned:
98    assert!(body.on_ground(), "settled after the hop");
99    // It fell to the floor plane, then stepped up the 1-voxel ledge
100    // (surface z = 99) while walking +x.
101    assert!(body.pos().x > 90.0, "reached the ledge region");
102    assert!(
103        (body.pos().z - 99.0).abs() < 0.01,
104        "standing on the ledge, feet at {}",
105        body.pos().z
106    );
107    assert!(eye.z < body.pos().z, "the eye is above the feet (-z is up)");
108
109    // Fly mode is the demos' free camera: no gravity, full-3D wish,
110    // instant start/stop, still sliding along walls.
111    body.set_mode(MoveMode::Fly);
112    body.walk(&scene, dt, WalkInput::default());
113    assert_eq!(body.vel(), DVec3::ZERO, "fly idles in place");
114
115    // The water curtain from `build_world` is passable: probe the
116    // veto directly (the same query walk() uses).
117    let wading = roxlap_scene::box_overlaps_solid(
118        &scene,
119        DVec3::new(40.2, 50.0, 90.0),
120        DVec3::new(40.8, 50.6, 91.8),
121        body.def().solidity,
122    );
123    assert!(!wading, "water is passable under the veto");
124
125    println!("book_controller: all controller assertions hold");
126}
Source

pub fn def_mut(&mut self) -> &mut CharacterDef

Tune parameters at runtime — sprint (walk_speed / fly_speed), variable jump height, etc. Growing radius / height while standing in a tight spot can leave the body overlapping solid; the stuck-escape rule makes that recoverable rather than a jam.

Source

pub fn pos(&self) -> DVec3

Feet position, world space.

Examples found in repository?
examples/book_controller.rs (line 101)
61fn main() {
62    let scene = build_world();
63
64    // ANCHOR: setup
65    // A walking body: a feet-positioned collision box, f64 world.
66    // Distances are voxels, times seconds; +z is DOWN, so gravity is
67    // positive and a jump impulse negative.
68    let mut body = CharacterBody::new(CharacterDef {
69        radius: 0.4,      // xy half-extent of the box
70        height: 1.8,      // feet → head (toward -z)
71        eye_height: 1.62, // feet → camera anchor
72        step_up: 1.05,    // auto-step ledges up to 1 voxel
73        solidity: Solidity {
74            bedrock_blocks: false, // match your renderer's policy
75            passable: Some(water_passes),
76        },
77        ..CharacterDef::default()
78    });
79    body.teleport(DVec3::new(50.0, 50.0, 95.0)); // FEET position
80                                                 // ANCHOR_END: setup
81
82    // ANCHOR: frame
83    // Per frame: build a wish direction from input (unit-length or
84    // zero — walk() clamps), call walk() EVERY frame (a zero wish is
85    // what stops the body), then anchor the camera at the eye.
86    let dt = 1.0 / 60.0;
87    for frame in 0..480 {
88        let input = WalkInput {
89            wish: DVec3::new(1.0, 0.2, 0.0), // toward the ledge
90            jump: frame == 120,              // one hop on the way
91        };
92        body.walk(&scene, dt, input);
93    }
94    let eye = body.eye_pos(); // = Camera::from_yaw_pitch(eye.into(), yaw, pitch)
95                              // ANCHOR_END: frame
96
97    // The trajectory the walk above promises, pinned:
98    assert!(body.on_ground(), "settled after the hop");
99    // It fell to the floor plane, then stepped up the 1-voxel ledge
100    // (surface z = 99) while walking +x.
101    assert!(body.pos().x > 90.0, "reached the ledge region");
102    assert!(
103        (body.pos().z - 99.0).abs() < 0.01,
104        "standing on the ledge, feet at {}",
105        body.pos().z
106    );
107    assert!(eye.z < body.pos().z, "the eye is above the feet (-z is up)");
108
109    // Fly mode is the demos' free camera: no gravity, full-3D wish,
110    // instant start/stop, still sliding along walls.
111    body.set_mode(MoveMode::Fly);
112    body.walk(&scene, dt, WalkInput::default());
113    assert_eq!(body.vel(), DVec3::ZERO, "fly idles in place");
114
115    // The water curtain from `build_world` is passable: probe the
116    // veto directly (the same query walk() uses).
117    let wading = roxlap_scene::box_overlaps_solid(
118        &scene,
119        DVec3::new(40.2, 50.0, 90.0),
120        DVec3::new(40.8, 50.6, 91.8),
121        body.def().solidity,
122    );
123    assert!(!wading, "water is passable under the veto");
124
125    println!("book_controller: all controller assertions hold");
126}
Source

pub fn eye_pos(&self) -> DVec3

Eye position — the camera anchor: eye_height above the feet, i.e. toward −z.

Examples found in repository?
examples/book_controller.rs (line 94)
61fn main() {
62    let scene = build_world();
63
64    // ANCHOR: setup
65    // A walking body: a feet-positioned collision box, f64 world.
66    // Distances are voxels, times seconds; +z is DOWN, so gravity is
67    // positive and a jump impulse negative.
68    let mut body = CharacterBody::new(CharacterDef {
69        radius: 0.4,      // xy half-extent of the box
70        height: 1.8,      // feet → head (toward -z)
71        eye_height: 1.62, // feet → camera anchor
72        step_up: 1.05,    // auto-step ledges up to 1 voxel
73        solidity: Solidity {
74            bedrock_blocks: false, // match your renderer's policy
75            passable: Some(water_passes),
76        },
77        ..CharacterDef::default()
78    });
79    body.teleport(DVec3::new(50.0, 50.0, 95.0)); // FEET position
80                                                 // ANCHOR_END: setup
81
82    // ANCHOR: frame
83    // Per frame: build a wish direction from input (unit-length or
84    // zero — walk() clamps), call walk() EVERY frame (a zero wish is
85    // what stops the body), then anchor the camera at the eye.
86    let dt = 1.0 / 60.0;
87    for frame in 0..480 {
88        let input = WalkInput {
89            wish: DVec3::new(1.0, 0.2, 0.0), // toward the ledge
90            jump: frame == 120,              // one hop on the way
91        };
92        body.walk(&scene, dt, input);
93    }
94    let eye = body.eye_pos(); // = Camera::from_yaw_pitch(eye.into(), yaw, pitch)
95                              // ANCHOR_END: frame
96
97    // The trajectory the walk above promises, pinned:
98    assert!(body.on_ground(), "settled after the hop");
99    // It fell to the floor plane, then stepped up the 1-voxel ledge
100    // (surface z = 99) while walking +x.
101    assert!(body.pos().x > 90.0, "reached the ledge region");
102    assert!(
103        (body.pos().z - 99.0).abs() < 0.01,
104        "standing on the ledge, feet at {}",
105        body.pos().z
106    );
107    assert!(eye.z < body.pos().z, "the eye is above the feet (-z is up)");
108
109    // Fly mode is the demos' free camera: no gravity, full-3D wish,
110    // instant start/stop, still sliding along walls.
111    body.set_mode(MoveMode::Fly);
112    body.walk(&scene, dt, WalkInput::default());
113    assert_eq!(body.vel(), DVec3::ZERO, "fly idles in place");
114
115    // The water curtain from `build_world` is passable: probe the
116    // veto directly (the same query walk() uses).
117    let wading = roxlap_scene::box_overlaps_solid(
118        &scene,
119        DVec3::new(40.2, 50.0, 90.0),
120        DVec3::new(40.8, 50.6, 91.8),
121        body.def().solidity,
122    );
123    assert!(!wading, "water is passable under the veto");
124
125    println!("book_controller: all controller assertions hold");
126}
Source

pub fn vel(&self) -> DVec3

Current velocity, world units per second.

Examples found in repository?
examples/book_controller.rs (line 113)
61fn main() {
62    let scene = build_world();
63
64    // ANCHOR: setup
65    // A walking body: a feet-positioned collision box, f64 world.
66    // Distances are voxels, times seconds; +z is DOWN, so gravity is
67    // positive and a jump impulse negative.
68    let mut body = CharacterBody::new(CharacterDef {
69        radius: 0.4,      // xy half-extent of the box
70        height: 1.8,      // feet → head (toward -z)
71        eye_height: 1.62, // feet → camera anchor
72        step_up: 1.05,    // auto-step ledges up to 1 voxel
73        solidity: Solidity {
74            bedrock_blocks: false, // match your renderer's policy
75            passable: Some(water_passes),
76        },
77        ..CharacterDef::default()
78    });
79    body.teleport(DVec3::new(50.0, 50.0, 95.0)); // FEET position
80                                                 // ANCHOR_END: setup
81
82    // ANCHOR: frame
83    // Per frame: build a wish direction from input (unit-length or
84    // zero — walk() clamps), call walk() EVERY frame (a zero wish is
85    // what stops the body), then anchor the camera at the eye.
86    let dt = 1.0 / 60.0;
87    for frame in 0..480 {
88        let input = WalkInput {
89            wish: DVec3::new(1.0, 0.2, 0.0), // toward the ledge
90            jump: frame == 120,              // one hop on the way
91        };
92        body.walk(&scene, dt, input);
93    }
94    let eye = body.eye_pos(); // = Camera::from_yaw_pitch(eye.into(), yaw, pitch)
95                              // ANCHOR_END: frame
96
97    // The trajectory the walk above promises, pinned:
98    assert!(body.on_ground(), "settled after the hop");
99    // It fell to the floor plane, then stepped up the 1-voxel ledge
100    // (surface z = 99) while walking +x.
101    assert!(body.pos().x > 90.0, "reached the ledge region");
102    assert!(
103        (body.pos().z - 99.0).abs() < 0.01,
104        "standing on the ledge, feet at {}",
105        body.pos().z
106    );
107    assert!(eye.z < body.pos().z, "the eye is above the feet (-z is up)");
108
109    // Fly mode is the demos' free camera: no gravity, full-3D wish,
110    // instant start/stop, still sliding along walls.
111    body.set_mode(MoveMode::Fly);
112    body.walk(&scene, dt, WalkInput::default());
113    assert_eq!(body.vel(), DVec3::ZERO, "fly idles in place");
114
115    // The water curtain from `build_world` is passable: probe the
116    // veto directly (the same query walk() uses).
117    let wading = roxlap_scene::box_overlaps_solid(
118        &scene,
119        DVec3::new(40.2, 50.0, 90.0),
120        DVec3::new(40.8, 50.6, 91.8),
121        body.def().solidity,
122    );
123    assert!(!wading, "water is passable under the veto");
124
125    println!("book_controller: all controller assertions hold");
126}
Source

pub fn set_vel(&mut self, vel: DVec3)

Overwrite the velocity — knockback, launch pads, spawn state.

Source

pub fn on_ground(&self) -> bool

true while the feet rest on solid ground (skin probe below the feet, updated by walk).

Examples found in repository?
examples/book_controller.rs (line 98)
61fn main() {
62    let scene = build_world();
63
64    // ANCHOR: setup
65    // A walking body: a feet-positioned collision box, f64 world.
66    // Distances are voxels, times seconds; +z is DOWN, so gravity is
67    // positive and a jump impulse negative.
68    let mut body = CharacterBody::new(CharacterDef {
69        radius: 0.4,      // xy half-extent of the box
70        height: 1.8,      // feet → head (toward -z)
71        eye_height: 1.62, // feet → camera anchor
72        step_up: 1.05,    // auto-step ledges up to 1 voxel
73        solidity: Solidity {
74            bedrock_blocks: false, // match your renderer's policy
75            passable: Some(water_passes),
76        },
77        ..CharacterDef::default()
78    });
79    body.teleport(DVec3::new(50.0, 50.0, 95.0)); // FEET position
80                                                 // ANCHOR_END: setup
81
82    // ANCHOR: frame
83    // Per frame: build a wish direction from input (unit-length or
84    // zero — walk() clamps), call walk() EVERY frame (a zero wish is
85    // what stops the body), then anchor the camera at the eye.
86    let dt = 1.0 / 60.0;
87    for frame in 0..480 {
88        let input = WalkInput {
89            wish: DVec3::new(1.0, 0.2, 0.0), // toward the ledge
90            jump: frame == 120,              // one hop on the way
91        };
92        body.walk(&scene, dt, input);
93    }
94    let eye = body.eye_pos(); // = Camera::from_yaw_pitch(eye.into(), yaw, pitch)
95                              // ANCHOR_END: frame
96
97    // The trajectory the walk above promises, pinned:
98    assert!(body.on_ground(), "settled after the hop");
99    // It fell to the floor plane, then stepped up the 1-voxel ledge
100    // (surface z = 99) while walking +x.
101    assert!(body.pos().x > 90.0, "reached the ledge region");
102    assert!(
103        (body.pos().z - 99.0).abs() < 0.01,
104        "standing on the ledge, feet at {}",
105        body.pos().z
106    );
107    assert!(eye.z < body.pos().z, "the eye is above the feet (-z is up)");
108
109    // Fly mode is the demos' free camera: no gravity, full-3D wish,
110    // instant start/stop, still sliding along walls.
111    body.set_mode(MoveMode::Fly);
112    body.walk(&scene, dt, WalkInput::default());
113    assert_eq!(body.vel(), DVec3::ZERO, "fly idles in place");
114
115    // The water curtain from `build_world` is passable: probe the
116    // veto directly (the same query walk() uses).
117    let wading = roxlap_scene::box_overlaps_solid(
118        &scene,
119        DVec3::new(40.2, 50.0, 90.0),
120        DVec3::new(40.8, 50.6, 91.8),
121        body.def().solidity,
122    );
123    assert!(!wading, "water is passable under the veto");
124
125    println!("book_controller: all controller assertions hold");
126}
Source

pub fn hit_head(&self) -> bool

true if the head hit a ceiling during the last walk.

Source

pub fn set_pos(&mut self, pos: DVec3)

Reposition the feet, KEEPING velocity and contact state — for world-bounds clamps and moving-platform style corrections. For spawns and respawns use teleport.

Source

pub fn teleport(&mut self, pos: DVec3)

Hard-place the feet at pos, zeroing velocity and contact flags. No collision check — placing inside solid is allowed (the stuck-escape rule below makes it recoverable).

Examples found in repository?
examples/book_controller.rs (line 79)
61fn main() {
62    let scene = build_world();
63
64    // ANCHOR: setup
65    // A walking body: a feet-positioned collision box, f64 world.
66    // Distances are voxels, times seconds; +z is DOWN, so gravity is
67    // positive and a jump impulse negative.
68    let mut body = CharacterBody::new(CharacterDef {
69        radius: 0.4,      // xy half-extent of the box
70        height: 1.8,      // feet → head (toward -z)
71        eye_height: 1.62, // feet → camera anchor
72        step_up: 1.05,    // auto-step ledges up to 1 voxel
73        solidity: Solidity {
74            bedrock_blocks: false, // match your renderer's policy
75            passable: Some(water_passes),
76        },
77        ..CharacterDef::default()
78    });
79    body.teleport(DVec3::new(50.0, 50.0, 95.0)); // FEET position
80                                                 // ANCHOR_END: setup
81
82    // ANCHOR: frame
83    // Per frame: build a wish direction from input (unit-length or
84    // zero — walk() clamps), call walk() EVERY frame (a zero wish is
85    // what stops the body), then anchor the camera at the eye.
86    let dt = 1.0 / 60.0;
87    for frame in 0..480 {
88        let input = WalkInput {
89            wish: DVec3::new(1.0, 0.2, 0.0), // toward the ledge
90            jump: frame == 120,              // one hop on the way
91        };
92        body.walk(&scene, dt, input);
93    }
94    let eye = body.eye_pos(); // = Camera::from_yaw_pitch(eye.into(), yaw, pitch)
95                              // ANCHOR_END: frame
96
97    // The trajectory the walk above promises, pinned:
98    assert!(body.on_ground(), "settled after the hop");
99    // It fell to the floor plane, then stepped up the 1-voxel ledge
100    // (surface z = 99) while walking +x.
101    assert!(body.pos().x > 90.0, "reached the ledge region");
102    assert!(
103        (body.pos().z - 99.0).abs() < 0.01,
104        "standing on the ledge, feet at {}",
105        body.pos().z
106    );
107    assert!(eye.z < body.pos().z, "the eye is above the feet (-z is up)");
108
109    // Fly mode is the demos' free camera: no gravity, full-3D wish,
110    // instant start/stop, still sliding along walls.
111    body.set_mode(MoveMode::Fly);
112    body.walk(&scene, dt, WalkInput::default());
113    assert_eq!(body.vel(), DVec3::ZERO, "fly idles in place");
114
115    // The water curtain from `build_world` is passable: probe the
116    // veto directly (the same query walk() uses).
117    let wading = roxlap_scene::box_overlaps_solid(
118        &scene,
119        DVec3::new(40.2, 50.0, 90.0),
120        DVec3::new(40.8, 50.6, 91.8),
121        body.def().solidity,
122    );
123    assert!(!wading, "water is passable under the veto");
124
125    println!("book_controller: all controller assertions hold");
126}
Source

pub fn walk(&mut self, scene: &Scene, dt: f64, input: WalkInput)

Advance the body by dt seconds against scene. Behaviour follows the current MoveMode; everything below describes MoveMode::Walk.

Integration order: horizontal velocity approaches wish · walk_speed at the ground/air accel rate; gravity; jump if grounded; then the displacement is applied in substeps of at most radius per axis (the no-tunnel guarantee), each substep moving x, then y, then z, clamping flush against the blocking cell plane and zeroing that velocity component on contact — unless a grounded horizontal block steps up a ledge (CharacterDef::step_up). +z contact grounds the body; −z contact is a head bump.

Stuck escape (kept verbatim from the demos): if the body starts the frame overlapping solid — an edit carved under the player, a bake reclassified a column — the whole frame moves without collision so the player can escape rather than jam.

Examples found in repository?
examples/book_controller.rs (line 92)
61fn main() {
62    let scene = build_world();
63
64    // ANCHOR: setup
65    // A walking body: a feet-positioned collision box, f64 world.
66    // Distances are voxels, times seconds; +z is DOWN, so gravity is
67    // positive and a jump impulse negative.
68    let mut body = CharacterBody::new(CharacterDef {
69        radius: 0.4,      // xy half-extent of the box
70        height: 1.8,      // feet → head (toward -z)
71        eye_height: 1.62, // feet → camera anchor
72        step_up: 1.05,    // auto-step ledges up to 1 voxel
73        solidity: Solidity {
74            bedrock_blocks: false, // match your renderer's policy
75            passable: Some(water_passes),
76        },
77        ..CharacterDef::default()
78    });
79    body.teleport(DVec3::new(50.0, 50.0, 95.0)); // FEET position
80                                                 // ANCHOR_END: setup
81
82    // ANCHOR: frame
83    // Per frame: build a wish direction from input (unit-length or
84    // zero — walk() clamps), call walk() EVERY frame (a zero wish is
85    // what stops the body), then anchor the camera at the eye.
86    let dt = 1.0 / 60.0;
87    for frame in 0..480 {
88        let input = WalkInput {
89            wish: DVec3::new(1.0, 0.2, 0.0), // toward the ledge
90            jump: frame == 120,              // one hop on the way
91        };
92        body.walk(&scene, dt, input);
93    }
94    let eye = body.eye_pos(); // = Camera::from_yaw_pitch(eye.into(), yaw, pitch)
95                              // ANCHOR_END: frame
96
97    // The trajectory the walk above promises, pinned:
98    assert!(body.on_ground(), "settled after the hop");
99    // It fell to the floor plane, then stepped up the 1-voxel ledge
100    // (surface z = 99) while walking +x.
101    assert!(body.pos().x > 90.0, "reached the ledge region");
102    assert!(
103        (body.pos().z - 99.0).abs() < 0.01,
104        "standing on the ledge, feet at {}",
105        body.pos().z
106    );
107    assert!(eye.z < body.pos().z, "the eye is above the feet (-z is up)");
108
109    // Fly mode is the demos' free camera: no gravity, full-3D wish,
110    // instant start/stop, still sliding along walls.
111    body.set_mode(MoveMode::Fly);
112    body.walk(&scene, dt, WalkInput::default());
113    assert_eq!(body.vel(), DVec3::ZERO, "fly idles in place");
114
115    // The water curtain from `build_world` is passable: probe the
116    // veto directly (the same query walk() uses).
117    let wading = roxlap_scene::box_overlaps_solid(
118        &scene,
119        DVec3::new(40.2, 50.0, 90.0),
120        DVec3::new(40.8, 50.6, 91.8),
121        body.def().solidity,
122    );
123    assert!(!wading, "water is passable under the veto");
124
125    println!("book_controller: all controller assertions hold");
126}

Trait Implementations§

Source§

impl Clone for CharacterBody

Source§

fn clone(&self) -> CharacterBody

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 Copy for CharacterBody

Source§

impl Debug for CharacterBody

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
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> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.