Crouch and crawl with hitbox change
const STANDING_COLLIDER = Collider(width = 16, height = 32)
const CROUCH_COLLIDER = Collider(width = 16, height = 16)
const CRAWL_SPEED = 70.0 // replaces MAX_SPEED while crouched
state is_crouching = false
state collider = STANDING_COLLIDER
function can_stand_up():
// Shapecast the STANDING collider's extra height upward from the current
// (crouched) top, to see if there's room to expand into.
extra_height = STANDING_COLLIDER.height - CROUCH_COLLIDER.height
return cast_shape_up(shape = STANDING_COLLIDER, distance = extra_height) == null
function set_collider(new_collider):
// Anchor at the FEET, not the center — resizing must not move the ground
// contact point, or the character will visibly sink or pop upward.
feet_y = position.y + collider.height / 2.0
collider = new_collider
position.y = feet_y - collider.height / 2.0
function handle_crouch_input():
wants_crouch = input.is_down("crouch")
if wants_crouch and not is_crouching:
is_crouching = true
set_collider(CROUCH_COLLIDER)
elif not wants_crouch and is_crouching:
if can_stand_up():
is_crouching = false
set_collider(STANDING_COLLIDER)
// else: stay crouched, ceiling is blocking — re-check every frame
// until the player releases crouch AND there's room.
function apply_crawl_speed_modifier():
if is_crouching:
MAX_SPEED_EFFECTIVE = CRAWL_SPEED
else:
MAX_SPEED_EFFECTIVE = MAX_SPEED
function update(dt):
handle_crouch_input()
apply_crawl_speed_modifier()
apply_horizontal_acceleration(dt) // using MAX_SPEED_EFFECTIVE as target
if not is_crouching:
handle_jump_input() // typically disallowed while crouched
apply_gravity(dt)
integrate_position(dt)
integrate_vertical(dt)
How it works¶
This is fundamentally a State Machine (standing / crouching) that swaps out the physics collider on transition, plus a speed modifier applied while crouched — "crawling" is just "moving while the short collider is active," not a separate movement system.
Two details make the resize behave correctly instead of buggily:
- Anchor at the feet. A collider's
positiontypically refers to its center. If you swapSTANDING_COLLIDERforCROUCH_COLLIDERwithout correcting position, the center stays put and the character either sinks half the height difference into the floor (collider shrank, center didn't move down) or hovers (grew without moving up).set_collidercomputes the world-space feet position first, applies the resize, then re-derivesposition.yfrom the new collider so the feet stay planted. - Can't stand into a ceiling. Releasing crouch under a low overhang must not let the standing collider clip into geometry above it.
can_stand_up()shapecasts the would-be standing collider's extra height before committing to the swap; if blocked, the character simply stays crouched and the check re-runs every frame until the ceiling clears — this is why the check lives in the "player wants to stop crouching" branch rather than being a one-time trigger.
Jumping is disabled while crouched in the pseudocode above, which is a common but not universal choice — some games instead implement a "crouch-jump" that stands the character up as part of the jump's takeoff (running can_stand_up() and set_collider(STANDING) at the same moment start_jump() fires).