Quick answer: Set CharacterController Step Offset = step_height + 0.05 (e.g. 0.4 for 0.35m steps). Skin Width 0.08. Slope Limit 45. Or replace stair geometry with a sloped collider for trivial traversal.

Player walks up stairs. Stops at every riser. CharacterController treats anything taller than Step Offset as a wall.

The Symptom

Player movement halts at vertical jumps in geometry. Doesn’t happen on slopes. Climbing happens only after a jump input.

The Fix

CharacterController Inspector:
  Slope Limit:     45      // degrees
  Step Offset:     0.4     // must exceed your tallest step
  Skin Width:      0.08    // must be less than Step Offset
  Min Move Distance: 0.001
  Center:          (0, 0.9, 0)
  Radius:          0.3
  Height:          1.8

Step Offset is in meters — the maximum vertical distance the controller can step over per Move call. Match it to your tallest step plus a small margin.

Slope Alternative

Most often the cleaner solution: visible stairs but a sloped invisible collider beneath. Geometry shows steps, physics treats it as a 30-45deg ramp. CharacterController just walks up.

Use a child GameObject with a thin Box rotated to the slope angle, child of the visible stairs.

Move vs SimpleMove

Move(motion) lets you handle gravity yourself. SimpleMove applies gravity automatically. Step climbing works either way; just be aware Move requires you to add Vector3.down * gravity * dt to the motion vector.

Verifying

Walk into stairs. Player should slide up the riser without halting. If they stop, raise Step Offset. If they climb but visually pop, Step Offset is too high relative to step height (legs jitter).

“Step Offset above the riser. Or use a slope collider. Stairs traverse.”

Related Issues

For NavMeshAgent stuck, see NavMesh corner. For Godot stairs, see Godot stairs.

Step Offset above the riser. Or slope. Stairs work.