Quick answer: CharacterBody2D snagging on tile seams comes from rectangular colliders catching the corner of adjacent tile shapes. Switch to a CapsuleShape2D, raise safe_margin from 0.08 to 0.1, enable floor_snap_length, and merge tile collisions where possible. The ghost collisions disappear.
Here is how to fix Godot 4 CharacterBody2D bodies that snag on otherwise flat ground. The character runs smoothly across a TileMap floor until it suddenly stops dead at a perfectly flat seam between two tiles. Or it bounces a few pixels into the air every time it crosses a tile boundary on a slope. Both problems share the same root cause: rectangular colliders meeting a stack of independent tile collision shapes.
The Symptom
Movement looks correct in the Scene view but the running character occasionally hitches, stops, or pops upward when crossing TileMap seams. Smooth ground feels lumpy. On slopes, the character may briefly leave the ground at every tile boundary, breaking is_on_floor() checks and triggering false “in air” animation states.
What Causes This
Rectangular collider on the character. A RectangleShape2D has sharp corners. When moving over a tile-tile seam, the leading bottom corner can catch the rising edge of the next tile’s collision shape (even if visually they line up perfectly).
Independent per-tile collisions. TileMap collisions are built per cell. Adjacent floor tiles each contribute their own shape; numerical precision makes the seam slightly imperfect.
safe_margin too small. The default of 0.08 px is fine for most cases but tiny on tilemaps with subpixel-precise placement. Raising it gives the engine more slack to resolve seams correctly.
floor_snap_length disabled. Without it, the character physically leaves the ground for one frame at every minor bump, breaking continuous floor contact.
The Fix
Step 1: Use a CapsuleShape2D. Replace the RectangleShape2D on the CollisionShape2D with a Capsule. Set the capsule’s radius to half the character width and height to roughly the character height. Rounded bottom edges roll over tile seams instead of catching them.
Step 2: Tune CharacterBody2D properties.
extends CharacterBody2D
const SPEED := 200.0
func _ready():
floor_snap_length = 8.0 # Glue to floor across small gaps
floor_max_angle = deg_to_rad(50)
safe_margin = 0.1 # Default 0.08 -> 0.1
slide_on_ceiling = true
max_slides = 4 # Default already 4, set explicitly
func _physics_process(delta: float):
var dir = Input.get_axis("left", "right")
velocity.x = dir * SPEED
velocity.y += 980.0 * delta
move_and_slide()
Step 3: Merge TileMap collisions where possible. Open the TileSet, select your floor tiles, and ensure the Physics layer collision polygons are identical and span the full tile. Where you have long stretches of identical floor, consider replacing TileMap collisions with a single StaticBody2D that has one wide CollisionShape2D — no seams, no problem.
Step 4: Use floor_constant_speed. When walking up gentle slopes, the default behavior reduces horizontal speed by the cosine of the slope angle. Setting floor_constant_speed = true keeps horizontal movement constant regardless of slope, which feels better and avoids small per-frame velocity changes that interact badly with seams.
Step 5: Verify with the collision debugger. Run the project, enable Debug → Visible Collision Shapes. Move slowly across a problem seam. If you see the character collider catching on a corner, you have visual confirmation. Adjusting capsule radius, safe_margin, or merging tile collisions should resolve it.
When You Cannot Use a Capsule
Some games need a rectangular hitbox for gameplay reasons (pixel-art platformer with strict bounds). In that case, use a separate CollisionShape2D for movement (capsule) and another for gameplay (rectangle), with the rectangle on a different collision layer that only the gameplay queries use.
# Movement collider (capsule, layer 1)
$MovementShape.shape = CapsuleShape2D.new()
# Hitbox (rectangle, layer 2 only)
$HitArea.collision_layer = 2
$HitArea.collision_mask = 2
“Capsule colliders roll over seams. Rectangles catch them. Choose your weapon.”
Related Issues
For floor snap problems specifically, see CharacterBody2D Floor Snap. For collision shape disable issues, see CollisionShape Disabled Not Re-Enabling.
Round the bottom. Raise the margin. Snap to floor. The seams disappear.