When developing 2D games in Godot Engine, choosing the right node for giving objects "physical behavior" is crucial. The three main physics body nodes—CharacterBody2D, RigidBody2D, and StaticBody2D—each have different design philosophies and purposes. Using them correctly directly affects game performance, ease of control, and bug prevention.
This article explains the basic definitions, characteristics, and appropriate use cases for these three physics body nodes with concrete code examples.
The Three Physics Bodies: Fundamental Concepts
All three nodes inherit from CollisionObject2D and can have collision shapes, but the "mover" of each is completely different.
| Node Name | Control Authority | Physics Engine Influence | Interaction with Other Objects | Primary Uses |
|---|---|---|---|---|
| StaticBody2D | None (fixed) | Not affected | Becomes a "wall" that blocks collisions | Ground, walls, immobile obstacles |
| RigidBody2D | Physics Engine | Affected (fully simulated) | Moved by collisions and moves others | Balls, boxes, physics puzzle elements |
| CharacterBody2D | Developer's Code | Partially affected (collision detection) | Blocks collisions but generally isn't moved | Players, enemies, moving platforms |
1. StaticBody2D: The Immovable Reference Point
StaticBody2D is the "immovable presence" in the physics world. It never moves due to gravity or external forces. Its role is to provide "walls" and "floors" for other objects to collide with.
- Main Properties:
constant_linear_velocity,constant_angular_velocity - Key Point: It should generally not be moved, but if you need to move it, consider using
AnimatableBody2D. This is a special static body that moves in sync with animations.
2. RigidBody2D: The Embodiment of Physics Laws
RigidBody2D is the star of the physics engine. Once placed in the world, it moves autonomously according to physics laws like gravity, friction, and collisions. Developers control it indirectly by applying forces and impulses rather than directly manipulating position.
Practical Code Example: Throwing Objects with RigidBody2D
extends RigidBody2D
@export var throw_force: float = 500.0
func throw(direction: Vector2):
# Reset existing motion
linear_velocity = Vector2.ZERO
angular_velocity = 0.0
# Apply impulse in specified direction
apply_central_impulse(direction.normalized() * throw_force)
Performance Considerations
RigidBody2D is computationally expensive. In scenes where hundreds of RigidBody2D collide with each other, performance can significantly degrade. Enabling the sleep (can_sleep) setting to skip physics calculations when objects are stationary is very important.
3. CharacterBody2D: Precise Control Through Code
CharacterBody2D is the most specialized and important node, designed for developers to completely control character movement. While utilizing the physics engine's collision detection capability, movement itself is determined through code via the move_and_slide() method.
Practical Code Example: More Practical Player Control
In Godot 4, velocity became a built-in property. Below is more practical code that considers jump input buffering (Coyote Time) and jump buffering.
extends CharacterBody2D
@export var speed: float = 300.0
@export var jump_velocity: float = -450.0
@export var gravity: float = 980.0
# Timers for coyote time and jump buffer
# CoyoteTimer: wait_time = 0.1, one_shot = true
# JumpBufferTimer: wait_time = 0.15, one_shot = true
@onready var coyote_timer: Timer = $CoyoteTimer
@onready var jump_buffer_timer: Timer = $JumpBufferTimer
var was_on_floor: bool = false
func _physics_process(delta: float) -> void:
# 1. Apply gravity
if not is_on_floor():
velocity.y += gravity * delta
# 2. Buffer jump input
if Input.is_action_just_pressed("jump"):
jump_buffer_timer.start()
# 3. Execute jump
# Jump if on floor or during coyote time, and jump is buffered
if (is_on_floor() or not coyote_timer.is_stopped()) and not jump_buffer_timer.is_stopped():
velocity.y = jump_velocity
jump_buffer_timer.stop()
# 4. Horizontal movement
var direction: float = Input.get_axis("move_left", "move_right")
velocity.x = direction * speed
# 5. Movement and collision detection
var was_on_floor_before = is_on_floor()
move_and_slide()
# 6. Start coyote timer the moment leaving the floor
if not is_on_floor() and was_on_floor_before:
coyote_timer.start()
This code implements important techniques that make platform games "feel good to control," beyond just basic movement.
Common Mistakes and Best Practices
| Common Mistake | Why It's a Problem | Best Practice |
|---|---|---|
Using RigidBody2D for player character | Physics simulation inertia and rebound prevent responsive input-driven movement. | Always use CharacterBody2D for players and enemies. |
Directly modifying CharacterBody2D's position | Collision detection via move_and_slide() doesn't work, causing clipping through walls. | Always update velocity and call move_and_slide(). |
Performing physics calculations in _process(delta) | _physics_process(delta) is called at a fixed frame rate, ensuring physics calculation stability. | Write all physics-related processing in _physics_process(delta). |
Using overly complex CollisionShape2D | More polygon vertices increase collision detection computational cost. | Use simple shapes (RectangleShape2D, CircleShape2D) whenever possible. |
Tips for Choosing the Right Node
When uncertain about node selection, answer these questions:
- Does the object move?
- No: Choose
StaticBody2D. (e.g., walls, ground)
- No: Choose
- Do you want the object's movement completely governed by physics laws (gravity, collisions, forces)?
- Yes: Choose
RigidBody2D. (e.g., balls, boxes)
- Yes: Choose
- Do you want to precisely control the object's movement through player input or AI logic?
- Yes: Choose
CharacterBody2D. (e.g., players, enemies)
- Yes: Choose
Summary
Godot Engine's 2D physics bodies have clearly defined roles:
- StaticBody2D: Immovable environmental elements.
- RigidBody2D: Objects completely governed by physics laws.
- CharacterBody2D: Characters precisely controlled through code.
By understanding these differences and choosing the appropriate node based on your game object's nature, you can develop games that behave more smoothly and as intended.