Physics Bodies, Areas, Collision Layers & Raycasts
Don't set position of a rigid body every frame; use forces/impulses, freeze, or _integrate_forces (teleport via state.transform).
21.1 Collision layers and masks
Layer: which layers this object is on.
Mask: which layers this object detects/collides with.
Name layers in *Project Settings → Layer Names → 2D Physics* (e.g., 1 World, 2 Player, 3 Enemies, 4 PlayerBullets, 5 Pickups).
World, Enemies, Pickups World, Enemies
collision_layer = 1 << 1 # only layer 2
collision_mask = 0b1101 # layers 1, 3, 4
set_collision_layer_value(3, true) # 1-based layer number
set_collision_mask_value(5, false)
print(get_collision_mask_value(1))21.2 Areas
get_overlapping_bodies(), get_overlapping_areas(), has_overlapping_bodies() — updated once per physics frame.
Areas can override gravity, damping, and audio reverb in a region (gravity_space_override).
Hitbox/Hurtbox pattern: attacks are Area2D "hitboxes"; characters have "hurtbox" areas; the hurtbox's area_entered applies damage.
# coin.gd
extends Area2D
@export var value := 1
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player"):
Events.coin_collected.emit(value)
queue_free()21.3 RigidBody2D
Don't set position of a rigid body every frame; use forces/impulses, freeze, or _integrate_forces (teleport via state.transform).
extends RigidBody2D
func _ready() -> void:
contact_monitor = true # required for body_entered on rigid bodies
max_contacts_reported = 4
body_entered.connect(func(b): print("hit ", b.name))
func kick(direction: Vector2) -> void:
apply_central_impulse(direction * 400.0) # instant change
apply_torque_impulse(200.0)
func _physics_process(_delta: float) -> void:
apply_central_force(Vector2(0, -50)) # continuous force (per physics step)
func _integrate_forces(state: PhysicsDirectBodyState2D) -> void:
# Safe place to directly modify the body's state
if state.linear_velocity.length() > 800.0:
state.linear_velocity = state.linear_velocity.limit_length(800.0)21.4 RayCast2D / ShapeCast2D nodes
@onready var ray: RayCast2D = $RayCast2D # target_position set in the Inspector
func _physics_process(_delta: float) -> void:
if ray.is_colliding():
var hit := ray.get_collider()
var point := ray.get_collision_point()
var normal := ray.get_collision_normal()
ray.target_position = Vector2(200, 0)
ray.force_raycast_update() # update immediately (otherwise next physics frame)21.5 Raycasts in code (direct space state)
Only query the space state during _physics_process (it's locked otherwise in some cases).
func line_of_sight(from: Vector2, to: Vector2) -> bool:
var space := get_world_2d().direct_space_state
var query := PhysicsRayQueryParameters2D.create(from, to, 0b0001, [get_rid()])
query.collide_with_areas = false
var result := space.intersect_ray(query)
return result.is_empty() # result keys: position, normal, collider, collider_id, rid, shape
func objects_at_point(p: Vector2) -> Array[Dictionary]:
var params := PhysicsPointQueryParameters2D.new()
params.position = p
return get_world_2d().direct_space_state.intersect_point(params)21.6 3D equivalents
CharacterBody3D, RigidBody3D, StaticBody3D, Area3D, RayCast3D, PhysicsRayQueryParameters3D, get_world_3d().direct_space_state. Godot 4.4+ ships Jolt Physics as an alternative 3D engine (*Project Settings → Physics → 3D → Physics Engine*); it is faster and more stable for many bodies.