Claude
Skills
Sign in
Back

godot-2d-physics

Included with Lifetime
$97 forever

Expert patterns for Godot 2D physics including collision layers/masks, Area2D triggers, raycasting, and PhysicsDirectSpaceState2D queries. Use when implementing collision detection, trigger zones, line-of-sight systems, or manual physics queries. Trigger keywords: CollisionShape2D, CollisionPolygon2D, collision_layer, collision_mask, set_collision_layer_value, set_collision_mask_value, Area2D, body_entered, body_exited, RayCast2D, force_raycast_update, PhysicsPointQueryParameters2D, PhysicsShapeQueryParameters2D, direct_space_state, move_and_collide, move_and_slide.

Generalscripts

What this skill does


# 2D Physics

Expert guidance for collision detection, triggers, and raycasting in Godot 2D.

## NEVER Do

- **NEVER scale `CollisionShape2D` nodes** — Use the shape handles in the editor, NOT the Node2D scale property. Scaling causes unpredictable physics behavior and incorrect collision normals [12].
- **NEVER confuse `collision_layer` with `collision_mask`** — Layer = "What AM I?", Mask = "What do I DETECT?". Setting both to the same value is usually wrong [13].
- **NEVER multiply velocity by delta when using `move_and_slide()`** — `move_and_slide()` automatically includes timestep. Only multiply gravity/acceleration by delta [14].
- **NEVER forget `force_raycast_update()` for manual mid-frame raycasts** — Raycasts update once per physics frame. If you change target_position, you MUST force an update [15].
- **NEVER use `get_overlapping_bodies()` every frame** — It is expensive. Cache results with `body_entered`/`body_exited` signals instead [16].
- **NEVER modify `RigidBody2D` state directly in `_process`** — Use `_integrate_forces()` for safe, synchronized access to `PhysicsDirectBodyState2D` [17, 411].
- **NEVER move `PhysicsBody2D` nodes in `_process()`** — Use `_physics_process()`. Moving bodies outside the physics step causes stutter and unreliable collision detection.
- **NEVER use `RigidBody2D` for 1000+ simple entities** — Use `PhysicsServer2D` to bypass node overhead for massive performance gains (Swarms/Bullets) [18, 397].
- **NEVER use `Area2D` for high-frequency blocking (Bullets)** — Area signals can be delayed. Use `move_and_collide()` or `ShapeCast2D` for frame-perfect results [19].
- **NEVER ignore 'Physics Jitter' on high-refresh monitors** — Enable Physics Interpolation to prevent micro-stutter in motion [21, 400].
- **NEVER scale collision shapes directly at runtime** — It causes major instability. Resize the shape resource (size/radius) instead.
- **NEVER use `set_deferred` for immediate physics transform logic** — It happens at the end of the frame. Use `force_raycast_update()` or `PhysicsServer2D` instead.
- **NEVER leave Continuous CD (CCD) enabled for slow objects** — It adds significant CPU overhead. Reserve it for high-speed projectiles to prevent tunneling.
- **NEVER use a single collision layer for all tiles/entities** — Separate layers (Ground, Walls, Enemies) to allow selective filtering via masks.
- **NEVER forget to free `PhysicsServer2D` RIDs manually** — They are not garbage collected and will leak memory permanently.

---

## Available Scripts

> **MANDATORY**: Read the script matching your use case before implementation.

### [collision_setup.gd](scripts/collision_setup.gd)
Programmatic layer/mask management with named layer constants and debug visualization.

### [physics_query_cache.gd](scripts/physics_query_cache.gd)
Frame-based caching for PhysicsDirectSpaceState2D queries - eliminates redundant expensive queries.

### [custom_physics.gd](scripts/custom_physics.gd)
Custom physics integration patterns for CharacterBody2D. Covers non-standard gravity, forces, and manual stepping. Use for non-standard physics behavior.

### [physics_queries.gd](scripts/physics_queries.gd)
PhysicsDirectSpaceState2D query patterns for raycasting, point queries, and shape queries. Use for line-of-sight, ground detection, or area scanning.

### [physics_server_swarm.gd](scripts/physics_server_swarm.gd)
Low-level `PhysicsServer2D` usage for thousands of moving objects. Bypasses node overhead for massive performance gains in bullet hells or swarms.

### [substepping_logic.gd](scripts/substepping_logic.gd)
Manual physics sub-stepping for high-velocity projectiles. Ensures frame-perfect collision for objects moving faster than the physics tick.

### [safe_rigidbody_state.gd](scripts/safe_rigidbody_state.gd)
Thread-safe `RigidBody2D` modification using `_integrate_forces`. Ideal for teleporting bodies or applying custom impulses without jitter.

### [physics_direct_query.gd](scripts/physics_direct_query.gd)
Lighweight environment sensing using `PhysicsDirectSpaceState2D`. Performs ray queries without the overhead of RayCast2D nodes.

### [collision_bitmask_helper.gd](scripts/collision_bitmask_helper.gd)
Clean architectural pattern for managing complex collision layers/masks using bitwise Enums and helpers.

### [raycast_vision_stack.gd](scripts/raycast_vision_stack.gd)
Optimized multicasting vision system for AI. Reuses a single RayCast2D to check multiple angles in one physics frame.

### [shapecast_aoe.gd](scripts/shapecast_aoe.gd)
Robust AOE detection using `ShapeCast2D`. Provides instant collision information without the signal-lag of Area2D.

### [custom_gravity_override.gd](scripts/custom_gravity_override.gd)
Logic for localized gravity zones (Water, Space, Wind) and manual character-weight simulation.

### [collision_debouncer.gd](scripts/collision_debouncer.gd)
Expert pattern for preventing signal spam when multi-shape bodies enter triggers.

### [jitter_interpolation_fix.gd](scripts/jitter_interpolation_fix.gd)
Standard configuration and runtime adjustments to ensure smooth character movement on high-refresh-rate monitors.

### [physics_server_direct_body.gd](scripts/physics_server_direct_body.gd)
Direct PhysicsServer2D RID management for peak performance in massive physics simulations.

### [move_and_collide_precision.gd](scripts/move_and_collide_precision.gd)
Expert bounce and friction logic implementation for precision-critical movement.

### [continuous_collision_detection.gd](scripts/continuous_collision_detection.gd)
Advanced CCD management for preventing bullet tunneling at extremely high velocities.

### [performance_batch_mover.gd](scripts/performance_batch_mover.gd)
Optimized batch movement for multiple static/animatable bodies using riders-aware logic.

---

## Collision Layers & Masks (Bitmask Deep Dive)

### The Mental Model

```gdscript
# collision_layer (32 bits): What broadcast channels am I transmitting on?
# collision_mask (32 bits): What broadcast channels am I listening to?

# Example: Player vs Enemy
# Player:
#   layer = 0b0001 (Channel 1: "I am a player")
#   mask  = 0b0110 (Channels 2+3: "I listen for enemies and walls")
# Enemy:
#   layer = 0b0010 (Channel 2: "I am an enemy")
#   mask  = 0b0101 (Channels 1+3: "I listen for players and walls")
```

### Bitmask Helpers

```gdscript
# ✅ GOOD: Use helper functions for clarity
func setup_player_collision() -> void:
    # I am layer 1
    set_collision_layer_value(1, true)
    
    # I detect layers 2 (enemies) and 3 (world)
    set_collision_mask_value(2, true)
    set_collision_mask_value(3, true)

# ✅ GOOD: Bit shift for programmatic layer math
func enable_layers(base_layer: int, count: int) -> void:
    var mask := 0
    for i in range(count):
        mask |= (1 << (base_layer + i - 1))
    collision_mask = mask

# ❌ BAD: Hardcoded bitmasks without documentation
collision_mask = 0b110110  # What does this mean?!
```

### Common Patterns

```gdscript
# Pattern: Projectile that hits enemies but ignores other projectiles
# projectile.gd
extends Area2D

func _ready() -> void:
    set_collision_layer_value(4, true)   # Layer 4: "Projectiles"
    set_collision_mask_value(2, true)    # Mask Layer 2: "Enemies"
    # Result: Projectiles don't collide with each other

# Pattern: One-way platform (player can jump through from below)
# platform.gd
extends StaticBody2D

@export var one_way := true

func _ready() -> void:
    set_collision_layer_value(3, true)   # Layer 3: "World"
    if one_way:
        # Use Area2D + collision exemption instead
        # (Standard one-way platforms use different technique)
        pass
```

---

## Area2D Expert Patterns

### Problem: Duplicate Triggers on Multi-CollisionShape

```gdscript
# ❌ BAD: body_entered fires MULTIPLE times if Area2D has multiple shapes
extends Area2D

func _ready() -> void:
    body_entered.connect(_on_body_entered)

func _on_body_entered(body: Node2D) -> void:
    print("Entered!")  # Fires 3x if Area h

Related in General