Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.
Physics Systems for Godot 4.x
Comprehensive patterns for physics, collisions, and movement in Godot 4.x.
Collision Layers and Masks (Godot 4.x Typed System)
Godot 4.x uses bit-based collision layer/mask system for efficient spatial queries.
Configuring Collision Layers
Set layers in the editor via Inspector > CollisionShape3D > Collision Layer (bits 1-32).
# Recommended layer layout
const LAYER_PLAYER = 0x1
const LAYER_ENEMIES = 0x2
const LAYER_PROJECTILES = 0x4
const LAYER_ENVIRONMENT = 0x8
const LAYER_PICKUPS = 0x10
const LAYER_TRIGGERS = 0x20
collision_layer = LAYER_PLAYER
collision_mask = LAYER_ENVIRONMENT | LAYER_PICKUPSLayer Design Best Practices
| Layer | Bit | Use Case |
|---|---|---|
| 1 | 0x1 | Player characters |
| 2 | 0x2 | Enemies and NPCs |
| 4 | 0x4 | Projectiles and weapons |
| 8 | 0x8 | Environment / terrain |
| 16 | 0x10 | Pickups and collectibles |
| 32 | 0x20 | Triggers (Area-only) |
Typed Collision Queries
var ray_query := PhysicsRayQueryParameters3D.new()
ray_query.from = camera.global_position
ray_query.to = global_position + forward * range
ray_query.collision_mask = LAYER_ENVIRONMENT
ray_query.exclude = [self]
var result = get_world_3d().direct_space.intersect_ray(ray_query)
if result:
var collider = result.get("collider")
print("Hit: ", collider.get_name())Rigid Body Dynamics
Basic Rigid Body Setup
class_name PlayerRigidBody extends CharacterBody3D
@export var gravity_scale: float = 1.0
@export var jump_force: float = 8.0
@export var move_speed: float = 5.0
var velocity := Vector3.ZERO
func _physics_process(delta: float) -> void:
velocity.y -= 9.8 * gravity_scale * delta
var input_dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity.x = input_dir.x * move_speed
velocity.z = input_dir.y * move_speed
move_and_slide()
if is_on_floor() and Input.is_action_just_pressed("jump"):
velocity.y = jump_forcePhysics Material
var bouncy_material := PhysicsMaterial.new()
bouncy_material.bounce = 0.8
bouncy_material.friction = 0.2
collision_shape.shape.material = bouncy_materialRigid Body with Forces
class_name Projectile extends RigidBody3D
@export var lifetime: float = 3.0
@export var damage: int = 10
func _ready() -> void:
linear_velocity = transform.basis.z * 20.0
get_tree().create_timer(lifetime).timeout.connect(queue_free)
func _on_body_entered(body: Node3D) -> void:
if body is Enemy:
body.take_damage(damage)
queue_free()Character Controllers
CharacterBody3D Movement Pattern
class_name ThirdPersonController extends CharacterBody3D
@export var walk_speed: float = 4.0
@export var run_speed: float = 8.0
@export var jump_velocity: float = 4.5
@export var gravity_amount: float = 9.8
@export var sprint_factor: float = 2.0
var _is_sprinting := false
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity.y -= gravity_amount * delta
if is_on_floor() and Input.is_action_just_pressed("jump"):
velocity.y = jump_velocity
var input_dir := Vector2.ZERO
input_dir = Vector2(
Input.get_axis("move_left", "move_right"),
Input.get_axis("move_up", "move_down")
)
_is_sprinting = Input.is_action_pressed("sprint")
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
var spd = run_speed * sprint_factor if _is_sprinting else walk_speed
velocity.x = direction.x * spd
velocity.z = direction.z * spd
else:
velocity.x = move_toward(velocity.x, 0, walk_speed)
velocity.z = move_toward(velocity.z, 0, walk_speed)
move_and_slide()CharacterBody2D Platformer Pattern
class_name PlatformerCharacter extends CharacterBody2D
@export var speed: float = 200.0
@export var jump_force: float = -450.0
@export var gravity: float = 1200.0
@export var max_fall_speed: float = 700.0
@export var acceleration: float = 800.0
@export var friction: float = 600.0
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity.y += gravity * delta
velocity.y = min(velocity.y, max_fall_speed)
if is_on_floor() and Input.is_action_just_pressed("jump"):
velocity.y = jump_force
var move_input := Input.get_axis("move_left", "move_right")
if move_input:
velocity.x = move_toward(velocity.x, move_input * speed, acceleration * delta)
else:
velocity.x = move_toward(velocity.x, 0, friction * delta)
move_and_slide()Area Nodes for Triggers and Detection
Basic Pickup Zone
class_name PickupZone extends Area3D
@export var pickup_scene: PackedScene
@export var min_items: int = 1
@export var max_items: int = 3
var _has_been_activated := false
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node3D) -> void:
if _has_been_activated: return
if body is CharacterBody3D:
_spawn_items()
_has_been_activated = true
queue_free()
func _spawn_items() -> void:
var count = randi() % (max_items - min_items + 1) + min_items
for i in range(count):
var item = pickup_scene.instantiate()
item.position = global_position + Vector3(randf() * 2 - 1, 0.5, randf() * 2 - 1)
get_tree().root.add_child(item)Enemy Detection Sensor
class_name EnemySensor extends Area3D
signal player_detected(player: CharacterBody3D)
signal player_lost(player: CharacterBody3D)
@export var detection_radius: float = 10.0
@export var detection_layers: int = 0x1
func _ready() -> void:
body_entered.connect(_on_body_entered)
body_exited.connect(_on_body_exited)
var sphere := SphereShape3D.new()
sphere.radius = detection_radius
$CollisionShape3D.shape = sphere
collision_mask = detection_layers
func _on_body_entered(body: Node3D) -> void:
if body is CharacterBody3D:
player_detected.emit(body)
func _on_body_exited(body: Node3D) -> void:
if body is CharacterBody3D:
player_lost.emit(body)Physics Interpolation (Godot 4.x Feature)
Godot 4.x supports physics interpolation for smooth rendering between physics ticks.
Enabling Physics Interpolation
Enable in Project Settings > Rendering > Other > Physics Interpolation set to "Interpolate". The engine automatically interpolates transforms between physics ticks when rendering. No extra code needed for standard nodes.
For custom rendering, use the built-in interpolation factor available through the rendering pipeline.
Performance: Bulk Physics Queries
Space State Batched Queries
func get_all_collisions_in_area(origin: Vector3, radius: float, mask: int) -> Array:
var query := PhysicsShapeQueryParameters3D.new()
var sphere := SphereShape3D.new()
sphere.radius = radius
query.shape = sphere
query.transform = Transform3D.IDENTITY.translated(origin)
query.collision_mask = mask
query.exclude = [self]
var space = get_world_3d().direct_space
return space.intersect_shape(query)
func aoe_attack(center: Vector3, radius: float, damage: int) -> void:
var hits := get_all_collisions_in_area(center, radius, LAYER_ENEMIES | LAYER_PROJECTILES)
for hit in hits:
var collider = hit.get("collider")
if collider is Enemy:
collider.take_damage(damage)Collision Layer Optimization
- Only check layers you need — avoid
collision_mask = 0xFFFFFFFF - Use
NavigationServer3Dfor pathfinding queries - Use small-radius
Area3Dnodes for proximity checks - Avoid querying the entire world every frame
Godot 4.7 Physics Notes
- Physics interpolation is more robust with improved accuracy
- Use
AreaLight3Dfor dynamic light casting on visible surfaces - Test physics performance with Perfetto tracing on mobile targets
Quick Reference: Movement Methods
| Method | Use Case | Returns |
|---|---|---|
move_and_slide() | Standard character movement | SlideFlags |
move_and_collide() | When you need collision info | CollisionData or null |
move_and_snap() | Platformer with snap-to-floor | SlideFlags |
move_teleport() | Teleportation (no sliding) | CollisionData or null |
MCP Bridge Tools (Optional — Live Editor Integration)
When the MCP bridge is running (Phase 2), these tools enhance physics debugging:
- `physics_debug_draw` — Toggle collision shape visualization in editor
- `physics_query_shape` — Run shape queries from any position without placing nodes
- `rigid_body_apply_force` — Apply forces to rigid bodies for testing
- `physics_stats` — View active physics bodies, sleep count, and frame cost
Note: MCP tools require the Godot editor to be running with the MCP plugin enabled. Skills work independently without the bridge.

