vl4dt/godot-skills

godot-physics

Physics systems for Godot 4.x: collision layers/masks, rigid body dynamics, character controllers (CharacterBody2D/3D), area nodes for triggers, physics interpolation, bulk queries, and performance optimization.

Vedi sorgente
Documento Skill originale

Contenuto dal repository con titoli, esempi, codice, tabelle, link e immagini preservati.

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).

gdscript
# 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_PICKUPS

Layer Design Best Practices

LayerBitUse Case
10x1Player characters
20x2Enemies and NPCs
40x4Projectiles and weapons
80x8Environment / terrain
160x10Pickups and collectibles
320x20Triggers (Area-only)

Typed Collision Queries

gdscript
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

gdscript
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_force

Physics Material

gdscript
var bouncy_material := PhysicsMaterial.new()
bouncy_material.bounce = 0.8
bouncy_material.friction = 0.2
collision_shape.shape.material = bouncy_material

Rigid Body with Forces

gdscript
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

gdscript
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

gdscript
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

gdscript
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

gdscript
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

gdscript
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 NavigationServer3D for pathfinding queries
  • Use small-radius Area3D nodes for proximity checks
  • Avoid querying the entire world every frame

Godot 4.7 Physics Notes

  • Physics interpolation is more robust with improved accuracy
  • Use AreaLight3D for dynamic light casting on visible surfaces
  • Test physics performance with Perfetto tracing on mobile targets

Quick Reference: Movement Methods

MethodUse CaseReturns
move_and_slide()Standard character movementSlideFlags
move_and_collide()When you need collision infoCollisionData or null
move_and_snap()Platformer with snap-to-floorSlideFlags
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.

References

dallo stesso repository

Altri Skills

Tutti gli Skills