Overview
When starting game development in Godot, CharacterBody2D is often the first choice for moving a player character. However, just calling move_and_slide() can result in strange behavior. "The jump feels wrong," "The character sticks to walls," "Why is my top-down character falling due to gravity?" Many of these issues stem from the CharacterBody2D Motion Mode setting.
This article explains the differences between CharacterBody2D's two main Motion Modes: Grounded and Floating. We'll cover which game genres each mode suits best and implementation best practices, with concrete code examples.
What is Motion Mode?
Motion Mode is the most important property determining how CharacterBody2D behaves in physical space—specifically how it interprets "floor," "wall," and "gravity." Configured from the CharacterBody2D properties in the inspector, it defines the foundation of character physics behavior.
| Mode | Key Characteristics | Optimal Game Type |
|---|---|---|
| Grounded | (Ground Contact Mode) Affected by gravity, clearly distinguishes floor/wall/ceiling. Useful floor detection APIs like is_on_floor() are available. | Side-scrolling platformers, physics-based action games |
| Floating | (Floating Mode) Ignores gravity, no concept of floor. Treats collisions from any direction equally as "walls." | Top-down RPGs, shooters, spaceship games |
Grounded Mode: For Platformers
Grounded, as the name suggests, is for characters "with feet on the ground." In genres like Mario or Metroidvania where gravity is fundamental to gameplay, this is almost always the necessary choice.
Key Features and Benefits
- Automatic Gravity Application: Gravity defined in project settings affects the character. Simply adding gravity to
velocity.yachieves natural falling. - Reliable Floor Detection: The
is_on_floor()function accurately determines whether the character stands on a surface defined as "floor." This easily prevents infinite jumping in mid-air. - Convenient API Set: Functions like
is_on_wall()(wall contact),is_on_ceiling()(ceiling contact), andget_floor_angle()(floor slope angle) accelerate platformer development. - Smooth Slope Handling:
move_and_slide()automatically adjusts velocity for characters to smoothly traverse slopes.
Practical Code Example
Here's a more practical Grounded mode code incorporating basic movement, jumping, and wall jumping:
@export var speed = 300.0
@export var jump_velocity = -400.0
@export var wall_jump_velocity = Vector2(400.0, -300.0)
# Get gravity from project settings
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
func _physics_process(delta):
# Apply gravity
if not is_on_floor():
velocity.y += gravity * delta
# Jump handling
if Input.is_action_just_pressed("jump"):
if is_on_floor():
velocity.y = jump_velocity
elif is_on_wall():
# Wall jump in opposite direction character is facing
var wall_normal = get_wall_normal()
velocity.x = wall_normal.x * wall_jump_velocity.x
velocity.y = wall_jump_velocity.y
# Horizontal movement
var direction = Input.get_axis("move_left", "move_right")
velocity.x = direction * speed
move_and_slide()
Floating Mode: For Top-Down Games
Floating mode frees characters from gravity. It's ideal for top-down RPGs like The Legend of Zelda or omnidirectional shooters like Vampire Survivors, where characters move freely around the map.
Key Features and Benefits
- Freedom from Gravity: Gravity isn't automatically applied in this mode. If
velocityisVector2.ZERO, the character stays stationary. - No Floor Concept:
is_on_floor()always returnsfalse. All collisions are treated equally regardless of direction. - Simple Movement Logic: Without needing to consider gravity or floor state, movement logic becomes very simple.
Practical Code Example
Here's Floating mode code implementing 8-directional movement and facing the mouse cursor:
@export var speed = 400.0
func _physics_process(delta):
# Get normalized movement vector from input
var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = direction * speed
move_and_slide()
# Face the mouse cursor
look_at(get_global_mouse_position())
Advanced: Smooth Movement with Inertia
For spaceship games or characters sliding on ice—implementing inertia (continuing to slide after releasing input)—gradually decay velocity instead of zeroing it immediately:
@export var speed = 400.0
@export var friction = 0.05 # Friction coefficient (closer to 0 = more slippery)
func _physics_process(delta):
var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
if direction != Vector2.ZERO:
# Accelerate when there's input
velocity = velocity.lerp(direction * speed, 0.1)
else:
# Gradually decelerate when no input (inertia)
velocity = velocity.lerp(Vector2.ZERO, friction)
move_and_slide()
Adjusting the friction value lets you express various textures from slippery ice to grippy ground.
Comparison with Alternative Patterns
For simple top-down movement, using Area2D instead of CharacterBody2D is another approach. Area2D doesn't perform physics collision, making it very lightweight. It may suit objects like enemy bullets that don't need to bounce off walls. However, for players and enemies requiring wall collision response, CharacterBody2D's Floating mode is the robust, optimal choice.
Common Mistakes and Best Practices
Here are common pitfalls when working with Motion Mode and best practices to avoid them:
| Common Mistake | Best Practice |
|---|---|
| Forgetting to manually add gravity in Grounded mode | Always add gravity to velocity.y when is_on_floor() is false in _physics_process. |
| Using Grounded mode for top-down games | Choose Floating mode for games that don't need gravity. Code becomes simpler and prevents unintended falling. |
Misunderstanding when to check is_on_floor() | is_on_floor() is updated by move_and_slide() execution. This means it shows the ground contact state from the previous frame's move_and_slide(). The common pattern is to check is_on_floor() before move_and_slide() when detecting jump input, then call move_and_slide() afterward. |
| Excessive performance concerns | move_and_slide() is optimized. Unless you're moving hundreds of characters simultaneously, performance is rarely an issue. First, write code that works correctly. |
Summary
CharacterBody2D's Motion Mode isn't just a configuration option—it's a design decision that defines your game's physical foundation.
- For side-scrolling and platformers where gravity and floors play important roles → Grounded Mode
- For top-down RPGs and shooters where you want free movement → Floating Mode
Making this choice correctly from the start prevents many character control issues and streamlines development.
Next Steps
You've mastered CharacterBody2D basics with this article. To deepen your understanding, explore these topics:
CharacterBody3D: Character control in 3D space. Basic concepts are shared with 2D.up_directionProperty: How to change gravity direction and implement special movement like walking on walls.move_and_collide: A lower-level movement function thanmove_and_slide(). Use when you need more detailed control over collision information.