Skip to main content

Physics Body Types

Godot provides four main 2D physics body types, each suited for different use cases:
Body TypeUse CaseAffected by Physics
RigidBody2DDynamic objects (boxes, ragdolls)Yes
CharacterBody2DPlayer/AI controlled charactersNo
StaticBody2DWalls, floors, static obstaclesNo
AnimatableBody2DMoving platforms, doorsNo

RigidBody2D

Physics-simulated bodies affected by gravity, forces, and collisions.

Basic Setup

extends RigidBody2D

func _ready():
    # Set mass
    mass = 10.0
    
    # Set gravity scale (1.0 = normal gravity)
    gravity_scale = 1.0
    
    # Allow rotation
    lock_rotation = false

Applying Forces

# Apply central impulse (at center of mass)
apply_central_impulse(Vector2(0, -500))  # Jump

# Apply impulse at position (causes rotation)
apply_impulse(Vector2(100, 0), Vector2(0, 50))

# Apply torque impulse (rotation)
apply_torque_impulse(1000.0)

Velocity Control

# Set velocity directly
linear_velocity = Vector2(200, -300)

# Set angular velocity (rotation speed)
angular_velocity = 2.0

# Set velocity on specific axis
set_axis_velocity(Vector2(0, -400))  # Useful for jumping

Damping

# Linear damping (resistance to movement)
linear_damp = 0.5
linear_damp_mode = RigidBody2D.DAMP_MODE_COMBINE

# Angular damping (resistance to rotation)
angular_damp = 1.0
angular_damp_mode = RigidBody2D.DAMP_MODE_REPLACE

Collision Detection

extends RigidBody2D

func _ready():
    # Enable collision monitoring
    contact_monitor = true
    max_contacts_reported = 10
    
    # Connect signals
    body_entered.connect(_on_body_entered)
    body_exited.connect(_on_body_exited)

func _on_body_entered(body: Node):
    print("Collided with: ", body.name)
    if body.is_in_group("enemies"):
        # Handle collision
        pass

func _on_body_exited(body: Node):
    print("Stopped colliding with: ", body.name)

Custom Integration

extends RigidBody2D

func _ready():
    # Disable standard physics, use custom
    custom_integrator = true

func _integrate_forces(state: PhysicsDirectBodyState2D):
    # Custom physics logic
    var transform = state.transform
    var velocity = state.linear_velocity
    
    # Apply custom forces
    if Input.is_action_pressed("move_right"):
        state.apply_central_force(Vector2(1000, 0))
    
    # Modify velocity directly
    state.linear_velocity = velocity.limit_length(500)

CharacterBody2D

Best for player and AI-controlled characters with precise movement control.

Basic Movement

extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0

func _physics_process(delta):
    # Add gravity
    if not is_on_floor():
        velocity += get_gravity() * delta
    
    # Handle jump
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY
    
    # Get input direction
    var direction = Input.get_axis("move_left", "move_right")
    if direction:
        velocity.x = direction * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
    
    move_and_slide()

Collision Detection

func _physics_process(delta):
    velocity += get_gravity() * delta
    move_and_slide()
    
    # Check if on floor/wall/ceiling
    if is_on_floor():
        print("On ground")
    if is_on_wall():
        print("Touching wall")
    if is_on_ceiling():
        print("Hit ceiling")

Getting Collision Information

func _physics_process(delta):
    move_and_slide()
    
    # Iterate through all collisions
    for i in get_slide_collision_count():
        var collision = get_slide_collision(i)
        print("Collided with: ", collision.get_collider().name)
        print("At position: ", collision.get_position())
        print("Normal: ", collision.get_normal())

Platformer Movement Settings

extends CharacterBody2D

func _ready():
    # Floor detection angle (45 degrees)
    floor_max_angle = deg_to_rad(45)
    
    # Snap to floor when going down slopes
    floor_snap_length = 8.0
    
    # Stop on slopes when not moving
    floor_stop_on_slope = true
    
    # Constant speed on slopes
    floor_constant_speed = false
    
    # Max number of slides per move
    max_slides = 4
    
    # Up direction for floor detection
    up_direction = Vector2.UP

Wall Jumping

extends CharacterBody2D

const WALL_JUMP_VELOCITY = Vector2(300, -400)

func _physics_process(delta):
    if not is_on_floor():
        velocity += get_gravity() * delta
    
    # Wall jump
    if is_on_wall() and Input.is_action_just_pressed("jump"):
        var wall_normal = get_wall_normal()
        velocity = Vector2(wall_normal.x * WALL_JUMP_VELOCITY.x, WALL_JUMP_VELOCITY.y)
    
    move_and_slide()

Platform Movement

# Get platform velocity when on a moving platform
func _physics_process(delta):
    move_and_slide()
    
    if is_on_floor():
        var platform_velocity = get_platform_velocity()
        print("Platform moving at: ", platform_velocity)

StaticBody2D

Immovable bodies for walls, floors, and obstacles.
extends StaticBody2D

func _ready():
    # Set constant linear velocity (conveyor belt)
    constant_linear_velocity = Vector2(100, 0)
    
    # Set constant angular velocity (rotating platform)
    constant_angular_velocity = 1.0
constant_linear_velocity doesn’t move the body but affects touching bodies as if it were moving.

AnimatableBody2D

For moving platforms and animated obstacles.
extends AnimatableBody2D

var direction = 1
const SPEED = 100.0
const DISTANCE = 200.0
var start_position: Vector2

func _ready():
    start_position = position

func _physics_process(delta):
    # Move platform back and forth
    position.x += direction * SPEED * delta
    
    if abs(position.x - start_position.x) > DISTANCE:
        direction *= -1

Collision Shapes

All physics bodies need collision shapes:
# Add collision shape in code
var shape = RectangleShape2D.new()
shape.size = Vector2(32, 32)

var collision = CollisionShape2D.new()
collision.shape = shape
add_child(collision)

Common Shape Types

  • RectangleShape2D - Rectangles and squares
  • CircleShape2D - Circles
  • CapsuleShape2D - Capsules (good for characters)
  • ConvexPolygonShape2D - Convex polygons
  • ConcavePolygonShape2D - Concave polygons (static only)

Physics Layers and Masks

Control which objects collide with each other:
# Collision layer: What layer is this object on?
collision_layer = 0b0001  # Layer 1

# Collision mask: Which layers does this object detect?
collision_mask = 0b0011  # Layers 1 and 2

Layer Examples

# Layer definitions
const LAYER_PLAYER = 1
const LAYER_ENEMY = 2
const LAYER_WORLD = 3
const LAYER_PICKUP = 4

# Player setup
player.collision_layer = 1 << (LAYER_PLAYER - 1)
player.collision_mask = (1 << (LAYER_ENEMY - 1)) | (1 << (LAYER_WORLD - 1)) | (1 << (LAYER_PICKUP - 1))

# Enemy setup
enemy.collision_layer = 1 << (LAYER_ENEMY - 1)
enemy.collision_mask = (1 << (LAYER_PLAYER - 1)) | (1 << (LAYER_WORLD - 1))

Raycasting

RayCast2D Node

extends RayCast2D

func _ready():
    # Enable raycast
    enabled = true
    
    # Set target position
    target_position = Vector2(0, 100)
    
    # Collision mask
    collision_mask = 0b0001

func _physics_process(delta):
    if is_colliding():
        var collider = get_collider()
        var collision_point = get_collision_point()
        var collision_normal = get_collision_normal()
        print("Hit: ", collider.name, " at ", collision_point)

PhysicsRayQueryParameters2D

func cast_ray(from: Vector2, to: Vector2) -> Dictionary:
    var space_state = get_world_2d().direct_space_state
    var query = PhysicsRayQueryParameters2D.create(from, to)
    query.collision_mask = 1
    query.exclude = [self]
    
    var result = space_state.intersect_ray(query)
    return result

func _physics_process(delta):
    var result = cast_ray(global_position, global_position + Vector2(0, 100))
    if result:
        print("Hit: ", result.collider.name)
        print("Position: ", result.position)
        print("Normal: ", result.normal)

Area2D

Detect overlaps without physics simulation:
extends Area2D

func _ready():
    # Connect signals
    body_entered.connect(_on_body_entered)
    body_exited.connect(_on_body_exited)
    area_entered.connect(_on_area_entered)

func _on_body_entered(body: Node2D):
    if body.is_in_group("players"):
        print("Player entered area")

func _on_body_exited(body: Node2D):
    print("Body exited")

func _on_area_entered(area: Area2D):
    print("Another area overlapped")

Manual Overlap Detection

func _physics_process(delta):
    # Get all overlapping bodies
    var overlapping_bodies = get_overlapping_bodies()
    for body in overlapping_bodies:
        print("Overlapping: ", body.name)
    
    # Get all overlapping areas
    var overlapping_areas = get_overlapping_areas()

Physics Material

Control friction and bounce:
# Create physics material
var material = PhysicsMaterial.new()
material.friction = 0.8
material.bounce = 0.5

# Apply to body
physics_material_override = material

Best Practices

CharacterBody2D provides better control and is deterministic, perfect for player characters.
Objects that should react realistically to forces like crates, balls, and ragdolls.
Properly configured layers prevent unnecessary collision checks and improve performance.
On RigidBody2D, only enable contact_monitor when you need collision signals to save performance.

Next Steps

TileMaps

Add physics to tile-based levels

Canvas Layers

Organize your game’s visual layers

Build docs developers (and LLMs) love