按源仓库内容呈现,保留标题、案例、代码、表格、链接以及原文引用的演示图片。
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.

