Quick answer: Set floor_snap_length to ~0.5 (stair height). Set floor_max_angle to ~45°. Build stairs as a sloped collider (Box rotated, or a single ramp mesh) for the smoothest experience. Per-step colliders trigger floor-loss snap each step.
Player runs down stairs. Each step launches them into the air for a frame. Sound effect for landing fires repeatedly. CharacterBody3D’s default snap distance doesn’t reach the next step.
The Symptom
Going down stairs: bouncing or stuttering. Going up: bumping into the riser, then either step-up or stop. Audio events tied to grounded state fire repeatedly.
What Causes This
CharacterBody3D’s move_and_slide checks for a floor below within floor_snap_length each tick. When the ground drops away by more than the snap distance (the next step in a staircase), the body becomes airborne, gravity applies, then re-touches the next step the frame after.
The Fix
Step 1: Set floor_snap_length.
extends CharacterBody3D
@export var speed := 5.0
@export var jump_velocity := 5.0
func _ready() -> void:
floor_snap_length = 0.5 # up to 0.5m drop holds the body to ground
floor_max_angle = deg_to_rad(45) # anything steeper is a wall
floor_constant_speed = true # constant horizontal on slopes
floor_stop_on_slope = false # keep momentum on hills
Step 2: Build stairs as a slope. Easiest. Replace step geometry with a single ramp mesh (or a Box collider rotated to ~30°). The character slides up/down without per-step snapping.
Step 3: Or, write step detection. When a horizontal collision is below step_height, raycast for a higher floor and snap.
func _physics_process(delta: float) -> void:
velocity.y -= GRAVITY * delta
velocity.x = input_dir.x * speed
velocity.z = input_dir.z * speed
move_and_slide()
# Step-up after a wall hit
if get_slide_collision_count() > 0:
var col = get_slide_collision(0)
if col.get_normal().y < 0.1: # a wall
var step = try_step_up(col.get_position())
if step:
global_position.y += STEP_HEIGHT
Verifying
Walk up and down a staircase. Position.y should change smoothly. is_on_floor() should stay true. Tracked landing/grounding signals fire only when actually leaving the floor (jump, fall).
“Snap length covers the step. Slope collider over per-step. Constant speed on slopes. Stairs feel solid.”
Related Issues
For physics interpolation jitter, see interpolation jitter. For input deadzone, see deadzone.
Snap length. Slope. Smooth.