Skip to content

Movement Types in Godot 4 (2D)

Overview

Not all moveable objects need physics! Godot offers multiple movement approaches depending on your needs.

1. Direct Transform Movement (Non-Physics)

# ============================================
# SIMPLE POSITION MOVEMENT
# ============================================
extends Node2D

@export var speed: float = 100.0

func _process(delta):
    # Direct position change
    position.x += speed * delta
    position.y += speed * delta

    # Or use += with Vector2
    position += Vector2(speed, 0) * delta

func _process(delta):
    # Rotation
    rotation += 1.0 * delta

    # Scale
    scale += Vector2(0.1, 0.1) * delta

Characteristics:

  • No collision detection by default
  • Fastest performance
  • Full manual control
  • Objects pass through each other

Use Cases:

  • Background parallax layers
  • Decorative objects
  • Visual effects (particles, trails)
  • UI elements
  • Cutscene objects
  • Non-interactive animations
# ============================================
# EXAMPLE: FLOATING COIN
# ============================================
extends Sprite2D

var float_height: float = 10.0
var float_speed: float = 2.0
var time: float = 0.0

func _process(delta):
    time += delta
    # Sine wave movement
    position.y += sin(time * float_speed) * float_height * delta

2. CharacterBody2D (Kinematic Physics)

# ============================================
# CONTROLLED MOVEMENT WITH COLLISIONS
# ============================================
extends CharacterBody2D

@export var speed: float = 200.0
@export var jump_velocity: float = -400.0
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

func _physics_process(delta):
    # Apply gravity
    if not is_on_floor():
        velocity.y += gravity * delta

    # Get input
    var direction = Input.get_axis("left", "right")
    velocity.x = direction * speed

    # Jump
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = jump_velocity

    # Move with collision detection
    move_and_slide()

Characteristics:

  • You control movement directly
  • Built-in collision detection and response
  • Helper functions: is_on_floor(), is_on_wall()
  • Doesn't respond to external physics forces
  • Predictable, responsive movement

Use Cases:

  • Player characters
  • NPCs with scripted movement
  • Enemies
  • Any character needing precise control
  • Platformer characters
  • Top-down RPG characters
# ============================================
# EXAMPLE: TOP-DOWN MOVEMENT
# ============================================
extends CharacterBody2D

@export var speed: float = 300.0

func _physics_process(delta):
    # Get 2D movement input
    var input_vector = Input.get_vector("left", "right", "up", "down")

    # Set velocity
    velocity = input_vector * speed

    # Move with collisions
    move_and_slide()

3. RigidBody2D (Dynamic Physics)

# ============================================
# PHYSICS-SIMULATED MOVEMENT
# ============================================
extends RigidBody2D

@export var thrust_force: float = 500.0

func _physics_process(delta):
    # Apply forces (physics engine handles movement)
    if Input.is_action_pressed("thrust"):
        apply_force(Vector2(thrust_force, 0))

func _ready():
    # Apply initial impulse
    apply_impulse(Vector2(100, -200))

func hit_by_explosion(explosion_position: Vector2, force: float):
    # Calculate direction from explosion
    var direction = global_position - explosion_position
    apply_impulse(direction.normalized() * force)

Characteristics:

  • Physics engine controls movement
  • Responds to gravity, forces, impulses
  • Realistic physics behavior
  • Can bounce, rotate, interact with other bodies
  • Less predictable than CharacterBody2D

Use Cases:

  • Projectiles (arrows, bullets)
  • Physics objects (boxes, barrels)
  • Ragdolls
  • Destructible objects
  • Physics puzzles
  • Vehicles with realistic physics
  • Bouncing objects
# ============================================
# EXAMPLE: PHYSICS PROJECTILE
# ============================================
extends RigidBody2D

@export var launch_speed: float = 500.0

func launch(direction: Vector2):
    linear_velocity = direction.normalized() * launch_speed

func _on_body_entered(body):
    # Explode on impact
    explode()
    queue_free()

4. AnimatableBody2D (Animated Kinematic)

# ============================================
# ANIMATION-DRIVEN MOVEMENT
# ============================================
extends AnimatableBody2D

# Moved by AnimationPlayer or Tween
# Other physics bodies react to its movement

func _ready():
    # Move with tween
    var tween = create_tween()
    tween.set_loops()
    tween.tween_property(self, "position:x", 500, 2.0)
    tween.tween_property(self, "position:x", 100, 2.0)

Characteristics:

  • Moved by animations, tweens, or scripts
  • Other physics bodies can ride on it
  • Pushes CharacterBody2D and RigidBody2D
  • Doesn't respond to physics forces
  • Synchronized collision updates

Use Cases:

  • Moving platforms
  • Elevators
  • Doors
  • Conveyor belts
  • Rotating platforms
  • Crushing traps
  • Any moving obstacle
# ============================================
# EXAMPLE: MOVING PLATFORM
# ============================================
extends AnimatableBody2D

@export var move_distance: float = 300.0
@export var move_duration: float = 3.0

func _ready():
    var start_pos = position
    var end_pos = position + Vector2(move_distance, 0)

    var tween = create_tween()
    tween.set_loops()
    tween.set_trans(Tween.TRANS_SINE)
    tween.set_ease(Tween.EASE_IN_OUT)

    tween.tween_property(self, "position", end_pos, move_duration)
    tween.tween_property(self, "position", start_pos, move_duration)

5. StaticBody2D (No Movement)

# ============================================
# STATIC COLLISION OBJECTS
# ============================================
extends StaticBody2D

# Used for walls, floors, obstacles
# Doesn't move, but can be moved manually in code
# Other bodies collide with it

Characteristics:

  • Designed to never move
  • Optimized for static collision
  • Can be moved in code but not recommended for frequent movement

Use Cases:

  • Walls
  • Floors
  • Static obstacles
  • Invisible barriers

6. Area2D (Detection Only)

# ============================================
# OVERLAP DETECTION (No Physics)
# ============================================
extends Area2D

signal player_entered

func _ready():
    body_entered.connect(_on_body_entered)

func _on_body_entered(body):
    if body.is_in_group("player"):
        player_entered.emit()

# Area2D can move freely
func _process(delta):
    position += Vector2(100, 0) * delta

Characteristics:

  • Detects overlaps, no collision response
  • Lightweight
  • Can move freely
  • No physics forces

Use Cases:

  • Triggers
  • Collectibles (coins, powerups)
  • Detection zones (enemy sight range)
  • Damage areas
  • Level boundaries

Movement Comparison

Type Collision Physics Forces Control Performance Best For
Node2D ❌ No ❌ No ✅ Full ⚡ Fastest Visuals, UI
CharacterBody2D ✅ Yes ❌ No ✅ Full ⚡ Fast Player, NPCs
RigidBody2D ✅ Yes ✅ Yes ⚠️ Indirect 🐌 Slower Physics objects
AnimatableBody2D ✅ Yes* ❌ No ✅ Full ⚡ Fast Platforms
StaticBody2D ✅ Yes ❌ No ❌ None ⚡ Fastest Walls
Area2D ⚠️ Detection ❌ No ✅ Full ⚡ Fast Triggers

*Affects other bodies but not itself

Decision Tree

Does it need to detect collisions?
├─ NO → Node2D (position movement)
└─ YES
   ├─ Does it need to respond to physics forces?
   │  ├─ YES → RigidBody2D
   │  └─ NO
   │     ├─ Does it move?
   │     │  ├─ NO → StaticBody2D
   │     │  └─ YES
   │     │     ├─ Do other objects need to ride on it?
   │     │     │  ├─ YES → AnimatableBody2D
   │     │     │  └─ NO → CharacterBody2D
   │     └─ Only detection needed?
   │        └─ YES → Area2D

Hybrid Approaches

# ============================================
# COMBINING MOVEMENT TYPES
# ============================================

# Example 1: Manual collision detection with Node2D
extends Node2D

var velocity: Vector2 = Vector2.ZERO

func _process(delta):
    position += velocity * delta
    check_manual_collisions()

func check_manual_collisions():
    # Use raycasts or Area2D for detection
    pass

# Example 2: CharacterBody2D with physics forces
extends CharacterBody2D

func _physics_process(delta):
    # Apply gravity manually
    velocity.y += 980 * delta

    # Apply wind force
    velocity.x += wind_force * delta

    move_and_slide()

# Example 3: Switch between RigidBody and CharacterBody
# Use RigidBody for ragdoll, then switch to CharacterBody when recovered

Best Practices

# ============================================
# CHOOSING THE RIGHT TYPE
# ============================================

# ✅ DO: Use CharacterBody2D for player
extends CharacterBody2D  # Responsive, predictable

# ❌ DON'T: Use RigidBody2D for player
extends RigidBody2D  # Slippery, hard to control

# ✅ DO: Use RigidBody2D for physics objects
extends RigidBody2D  # Realistic interactions

# ✅ DO: Use Node2D for background elements
extends Node2D  # No collision overhead

# ✅ DO: Use Area2D for collectibles
extends Area2D  # Lightweight detection

Quick Reference:

  • Visual only → Node2D
  • Player control → CharacterBody2D
  • Realistic physics → RigidBody2D
  • Moving platforms → AnimatableBody2D
  • Walls/floors → StaticBody2D
  • Triggers/collectibles → Area2D