Physics
PhysicsBody is an axis-aligned box. Position is the feet, at the bottom center, not the center of
mass. Default size is 0.6 m wide and 1.8 m tall, with the eye 1.62 m above the feet.
Velocity and acceleration are mutable Vec3s. Acceleration is cleared at the end of each step.
One step
PhysicsEngine.step(body, world, dt) integrates that many seconds. Order is fixed: sample the
blocks overlapping the body, add gravity (scaled in fluid, plus buoyancy), integrate acceleration, apply
drag, clamp terminal velocity, then move X, Y, and Z separately. A colliding axis move is reverted and that
axis velocity is zeroed. A downward hit sets onGround. A short downward probe
(groundStick, default 0.08 m) keeps the body grounded on shallow slopes and stair lips.
applyMoveWish sets horizontal velocity from a desired XZ direction. Length is normalized, then
scaled by walk or sprint speed and by the surface speed multiplier. In fluid the speed is reduced further
and multiplied by the water block’s speed multiplier. With no wish, a grounded body stops. An airborne body
keeps 92% of its horizontal velocity.
tryJump sets upward velocity from defaultJumpVelocity times the surface jump
multiplier. In fluid, holding jump raises the body toward swimUpSpeed instead.
When the native library is loaded, these three methods run in Rust and match the Java formulas for that
single dt. The 1 ms frame loop does not call step again. It integrates
inside Rust up to frameNs. Sixteen 1 ms steps are not the same numbers as one 16 ms
step. That difference is the point of the fast clock.
Defaults
PhysicsSettings starts at outdoor values. Change them on the instance you pass to the controller. They are not toml keys.
| Field | Default |
|---|---|
| Gravity | −28 m/s² |
| Terminal velocity | −55 m/s |
| Air drag | 0.03 |
| Walk / sprint | 6.2 / 12.2 m/s |
| Jump velocity | 11.8 m/s |
| Swim-up speed | 6.2 m/s |
| Fluid gravity scale | 0.35 |
OptiShoot replaces walk and sprint with 8.4 and 13.5. Sandbox keeps the defaults. First person also uses a
coyote window of 0.14 s and a jump buffer of 0.18 s. Those constants live on
FirstPersonController and are copied into Rust when the sim is published. Third person jumps on
the rising edge of the jump key and has no coyote window.
Voxels
VoxelPhysicsQuery.getBlock returns the block at integer coordinates, or null for air and for
chunks that are not loaded. Solids stop the body. Fluids set drag and buoyancy from the block prototype in
BlockRegistry. The block under the feet sets friction, speed, and jump multipliers for the next
wish.
A one-off step against any query copies the nearby cells into a Rust scratch volume, then
integrates. The 1 ms loop does not do that. It reads the voxel mirror: every chunk ChunkStore
loads or edits is uploaded as raw bytes (0 is air, otherwise BlockType.ordinal() + 1). Unloaded
space is air, same as a null query.
Chunks are 16³. ChunkStreamingManager loads a Chebyshev square in XZ times a Y range from
render_distance and render_distance_y. Empty chunks are published with no GPU
buffer. The load screen counts those as finished. It does not wait for a mesh that will never exist.
Controllers
FirstPersonController faces with camera yaw, builds a wish from WASD, steps, then
writes the eye position back onto the camera. Falling below Y = −20 respawns at the stored point.
ThirdPersonController keeps its own yaw and pitch. The camera sits on the look ray behind the
eyes. Default boom length is 4 m. A voxel ray shortens the boom when a wall is in the way.
setMoveMul scales speed for aim and weapons. Crouch multiplies that by 0.55. The view cycle is
cyclePerspective.