Quick answer: CharacterBody3D slides down slopes because gravity is continuously applied even when the character is on a floor surface. If the floor_max_angle is not high enough to include the slope, Godot treats it as a wall and applies gravity-driven sliding.
Here is how to fix CharacterBody3D sliding down slopes idle Godot. Your 3D character stands perfectly still on flat ground, but the moment they step onto a slope — even a gentle one — they slowly slide downhill without any player input. This unwanted slope sliding is a classic CharacterBody3D issue in Godot 4, and it is almost always caused by gravity being applied when it should not be, combined with insufficient floor detection configuration.
The Symptom
When your CharacterBody3D stands on a sloped surface with no input from the player, the character drifts downhill. The speed of the drift varies with the slope angle — steeper slopes cause faster sliding. On very gentle slopes (5–10 degrees), the sliding might be barely perceptible, but on slopes of 20–30 degrees it becomes obvious and distracting.
The sliding typically occurs because your _physics_process() function applies gravity every frame, and the move_and_slide() call converts that downward velocity into movement along the slope surface. Even though the character is technically “on the floor,” the gravity component parallel to the slope surface creates a net force that pushes the character downhill.
You might also notice that is_on_floor() flickers between true and false while the character is on the slope. This happens because the sliding motion briefly lifts the character off the surface, at which point gravity kicks in again, pulls it back down, and the cycle repeats. The result is a jittery, sliding character that never settles.
What Causes This
The root cause is a combination of three factors:
1. Gravity applied unconditionally. Most character movement tutorials include a pattern like if not is_on_floor(): velocity.y -= gravity * delta. But on slopes, is_on_floor() can return false intermittently, causing gravity to accumulate and push the character along the slope even when they should be stationary.
2. floor_max_angle too low. The floor_max_angle property defines the steepest surface angle that counts as “floor.” The default is approximately 45 degrees (0.785 radians). If your slope is steeper than this, Godot treats it as a wall, and is_on_floor() returns false. But even for slopes within the limit, edge cases near the threshold can cause flickering.
3. Insufficient floor_snap_length. When floor_snap_length is too short, the character cannot maintain consistent contact with sloped surfaces. Each physics frame, the character moves slightly away from the surface due to the collision response, then snaps back. If the snap distance is less than this displacement, the character loses floor contact and starts sliding.
The Fix
Step 1: Configure floor properties. Set floor_max_angle to cover your steepest walkable slope, enable floor_stop_on_slope, and increase floor_snap_length:
extends CharacterBody3D
const SPEED = 5.0
const GRAVITY = 9.8
func _ready():
floor_max_angle = deg_to_rad(46.0)
floor_snap_length = 1.0
floor_stop_on_slope = true
The floor_stop_on_slope property is the key setting here. When enabled, it tells move_and_slide() to prevent any downhill movement on floors when the character has no horizontal velocity. This directly addresses the symptom.
Step 2: Zero velocity when idle on floor. When the player is not pressing any movement keys and the character is on the floor, explicitly set horizontal velocity to zero and skip gravity application:
func _physics_process(delta):
var input_dir = Input.get_vector(
"move_left", "move_right",
"move_forward", "move_back"
)
if is_on_floor():
if input_dir == Vector2.ZERO:
# No input + on floor = stop completely
velocity.x = move_toward(velocity.x, 0.0, SPEED)
velocity.z = move_toward(velocity.z, 0.0, SPEED)
velocity.y = 0.0
else:
var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
velocity.y = 0.0
else:
# Only apply gravity when truly airborne
velocity.y -= GRAVITY * delta
move_and_slide()
Step 3: Handle the transition from air to slope. When a character lands on a slope after a jump or fall, there is a brief frame where they have downward velocity from gravity. If not cleared, this causes a slide pulse. Ensure you zero out the Y velocity on landing:
var was_on_floor = false
func _physics_process(delta):
var on_floor_now = is_on_floor()
# Just landed - clear downward velocity
if on_floor_now and not was_on_floor:
velocity.y = 0.0
was_on_floor = on_floor_now
# ... rest of movement code
Why This Works
floor_stop_on_slope is the most important setting. When enabled, move_and_slide() actively counteracts any velocity component that would move the character downhill on a floor surface. It does this by projecting the velocity onto the slope normal and subtracting the downhill component. Without this, even small floating-point errors in the velocity calculation can cause perceptible drift.
Zeroing velocity when idle on floor prevents gravity accumulation from creating sliding force. If the character is grounded and has no input, there is no reason to have any velocity at all. Setting it to zero each frame is a clean, deterministic approach that eliminates edge cases.
Increased floor_snap_length keeps the character locked to the surface. The snap mechanism runs after move_and_slide() and pulls the character back toward the floor if it drifted slightly upward during collision response. A value of 1.0 unit provides generous margin for 3D characters, ensuring consistent is_on_floor() readings on uneven terrain.
Landing velocity clearing prevents the brief slide that occurs when downward velocity from a fall is converted into slope-parallel movement on the first frame of ground contact.
Related Issues
Slope sliding shares configuration parameters with CharacterBody2D wall jitter, which also involves floor_snap_length and floor_max_angle tuning. If your character slides on slopes but also reports wrong collision normals, the normal issue may be causing the floor detection to misclassify slopes as walls. For 3D physics bodies that vibrate when resting on surfaces, see our guide on stacked physics bodies vibrating.