
Godot Physics 3d
- 204 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Use godot-physics-3d for development tasks
About
godot-physics-3d: A skill for development. This provides functionality for development workflows.
- godot-physics-3d
Godot Physics 3d by the numbers
- 204 all-time installs (skills.sh)
- +13 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #1,915 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-physics-3dAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 204 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Use godot-physics-3d for development tasks
Files
3D Physics (Jolt/Native)
Expert guidance for high-performance 3D physics and ragdolls.
NEVER Do
- NEVER move `PhysicsBody3D` nodes in `_process()` — Use
_physics_process(). Moving bodies outside the physics step causes visual jitter and unreliable collision detection [12, 13]. - NEVER scale collision shapes directly — Scaling physics shapes causes instability, inaccurate normals, and jitter. Use the
shapeproperties (height, radius, size) instead. - NEVER modify `RigidBody3D` transforms directly — This ignores the physics solver. Use
apply_impulse(),apply_torque(), or the_integrate_forces()callback for safe manipulation [17]. - NEVER use `RigidBody3D` for platformer player controllers — RigidBody is for objects driven by physics. For refined movement, use
CharacterBody3Dwithmove_and_slide()[move_and_slide]. - NEVER leave Continuous CD (CCD) enabled for static meshes — It adds heavy CPU cost. Reserve it for high-speed small objects (bullets) to prevent them from passing through walls.
- NEVER use `PhysicsServer3D` RIDs without manual cleanup — RIDs are not garbage collected. If you create bodies via the server, you MUST call
free_rid()when done to avoid memory leaks. - NEVER use `RayCast3D` for precise ground detection on stairs — A single ray is too thin. Use
ShapeCast3Dwith a cylinder or sphere shape to detect walkable steps reliably [Stair Logic]. - NEVER rely on `VehicleBody3D` for non-racing arcade vehicles — It's a complex sim. For arcade hovercraft or simple cars, a custom
CharacterBody3Dwith Raycasts is often easier to tune. - NEVER forget to set `collision_layer` and `collision_mask` properly — If everything is on layer 1, performance will tank from redundant checks. Categorize your world.
- NEVER use `Area3D` for high-frequency blocking — Areas are for detection. For walls/barriers, use
StaticBody3Dto ensure immediate, robust containment.
---
Available Scripts
physics_server_3d_bullets.gd
Direct PhysicsServer3D RID management for thousands of high-speed 3D projectiles.
ray_query_3d_vision.gd
Expert line-of-sight and AI vision logic using low-level space state interrupts.
shapecast_3d_ground_check.gd
Robust stair and ledge detection using ShapeCast3D for 3D CharacterBody stability.
physics_ccd_3d_projectile.gd
Continuous Collision Detection configuration and sub-stepping logic for anti-tunneling.
physics_layers_3d_config.gd
Clean collision matrix architecture for 3D using named bitmask layers and masks.
custom_gravity_well_3d.gd
Planet-style gravity wells and zero-G zones implemented via priority Area3D nodes.
soft_body_3d_interaction.gd
Managing high-performance SoftBody3D flags, cloaks, and foliage attachments.
joint_3d_breakage_logic.gd
Dynamic joint stress monitoring and procedural snaps for destructible 3D objects.
kinematic_3d_stairs_logic.gd
Advanced procedural stair-stepping and snapping for professional 3D character controllers.
vehicle_simulation_tuning.gd
Tuning VehicleBody3D and VehicleWheel3D for high-speed drifting and arcade feel.
ragdoll_manager.gd
Expert manager for transitioning Skeleton3D from animation to physical simulation (death effect). Handles impulse application and cleanup.
raycast_visualizer.gd
Debug tool to visualize hit points and normals of RayCast3D in game.
Core Architecture
1. Layers & Masks (3D)
Same as 2D:
- Layer: What object IS.
- Mask: What object HITS.
2. Physical Bones (Ragdolls)
Godot uses PhysicalBone3D nodes attached to Skeleton3D bones. To setup: 1. Select Skeleton3D. 2. Click "Create Physical Skeleton" in top menu. 3. This generates PhysicalBone3D nodes.
3. Jolt Joints
Use Generic6DOFJoint3D for almost everything. It covers hinge, slider, and ball-socket needs with simpler configuration than specific nodes.
---
Ragdoll Implementation
# simple_ragdoll.gd
extends Skeleton3D
func start_ragdoll() -> void:
physical_bones_start_simulation()
func stop_ragdoll() -> void:
physical_bones_stop_simulation()4. Custom Physics Constraint Solver (intersect_ray)
For building custom constraints like hover mechanics or custom suspensions, query the physics space directly using PhysicsDirectSpaceState3D. Access the space state only during the _physics_process() callback to avoid lock errors.
class_name HoverConstraint3D extends RigidBody3D
## Custom physics constraint using low-level raycasting.
@export var hover_height: float = 2.0
@export var hover_force: float = 80.0
func _physics_process(_delta: float) -> void:
# Safely retrieve the physics space state during the physics tick.
var space_state: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state
# Define the ray vector using global coordinates.
var from_pos: Vector3 = global_position
var to_pos: Vector3 = global_position + (Vector3.DOWN * 10.0)
# Create the query parameter using the static factory method.
var query := PhysicsRayQueryParameters3D.create(from_pos, to_pos)
# Exclude this rigid body by its server RID for optimal performance.
query.exclude = [get_rid()]
# Execute the raycast query.
var result: Dictionary = space_state.intersect_ray(query)
# Resolve the custom constraint.
if result and not result.is_empty():
var hit_position: Vector3 = result.position
var distance: float = global_position.distance_to(hit_position)
if distance < hover_height:
# Apply a restorative central force to simulate a spring constraint.
var force_magnitude: float = hover_force * (hover_height - distance)
apply_central_force(Vector3.UP * force_magnitude)5. Jolt Settings (Vehicle Suspensions)
Jolt Physics is the recommended engine for high-stability 3D physics in Godot 4.x. For vehicle suspensions, tune suspension_stiffness and suspension_travel to balance stability and realism.
class_name VehicleSuspensionTuner extends VehicleBody3D
## Dynamically tunes Jolt-powered vehicle suspensions.
@export var wheels: Array[VehicleWheel3D] = []
func _ready() -> void:
# Ensure the engine is using Jolt for maximum stability.
var engine_name: String = ProjectSettings.get_setting("physics/3d/physics_engine")
if engine_name != "Jolt Physics":
push_warning("Vehicle physics requires Jolt Physics for maximum stability.")
_configure_offroad_suspension()
func _configure_offroad_suspension() -> void:
for wheel in wheels:
# Values < 50 for off-road, 50-100 for race cars.
wheel.suspension_stiffness = 35.0
wheel.suspension_travel = 0.25
wheel.damping_compression = 0.83
wheel.damping_relaxation = 0.88
wheel.wheel_friction_slip = 10.5 6. Ragdoll Blending (Revival Interpolation)
Transitioning from a ragdoll back to an animated state involves manipulating the influence property of the PhysicalBoneSimulator3D. Use a Tween to smoothly interpolate influence from 1.0 (physics) to 0.0 (animation).
class_name RagdollBlender extends Node3D
## Handles the transition between skeletal animation and physics ragdolls.
@export var bone_simulator: PhysicalBoneSimulator3D
func kill_character() -> void:
# Start physics simulation override.
bone_simulator.physical_bones_start_simulation()
bone_simulator.influence = 1.0
func revive_character() -> void:
# 1. Ensure an animation (e.g., "get_up") is playing as the target.
# 2. Smoothly transition control from Physics to Animation.
var tween: Tween = create_tween()
tween.tween_property(bone_simulator, "influence", 0.0, 1.5)\
.set_trans(Tween.TRANS_SINE)\
.set_ease(Tween.EASE_IN_OUT)
# 3. Stop simulation once the blend finishes to save CPU resources.
tween.tween_callback(bone_simulator.physical_bones_stop_simulation)Reference
- Master Skill: godot-master
# custom_gravity_well_3d.gd
# 3D Gravity wells and directional overrides via Area3D
extends Area3D
func _ready() -> void:
# Replace world gravity with point force
gravity_space_override = Area3D.SPACE_OVERRIDE_REPLACE
gravity_point = true
gravity_point_unit_distance = 10.0
gravity = 20.0 # Force attraction
# For planetary gravity (spherical):
# gravity_direction = Vector3.ZERO # Point toward center
# joint_3d_breakage_logic.gd
# Dynamic PinJoint3D/HingeJoint3D breakage under stress
extends Generic6DOFJoint3D
@export var break_force: float = 1000.0
func _physics_process(_delta: float) -> void:
# Check impulse applied to the joint
# Note: In Godot 4, finding the exact joint stress
# usually requires checking velocity deltas of the two bodies.
var body_a = get_node(node_a) as RigidBody3D
var body_b = get_node(node_b) as RigidBody3D
if body_a and body_b:
var stress = (body_a.linear_velocity - body_b.linear_velocity).length()
if stress > break_force:
queue_free() # Snap the joint
# kinematic_3d_stairs_logic.gd
# Advanced stair-climbing/snapping for CharacterBody3D [Stair Logic]
extends CharacterBody3D
@export var max_stair_height: float = 0.5
@onready var stair_ray: RayCast3D = $StairRay
func _physics_process(_delta: float) -> void:
# Custom stair-climbing is often smoother than move_and_slide's
# default floor_snap_length for fast characters.
if is_on_floor() and velocity.length() > 0:
if stair_ray.is_colliding():
var hit_point = stair_ray.get_collision_point()
var height = hit_point.y - global_position.y
if height > 0 and height <= max_stair_height:
global_position.y += height
# Correct for the vertical jump
# velocity.y = 0
# physics_ccd_3d_projectile.gd
# Managing CCD and physics steps for high-speed 3D objects
extends RigidBody3D
# PROBLEM: Sniper bullets can skip through 1m thick walls at 300m/s.
# SOLUTION: Enable CCD and use sub-stepping or a custom integration.
func _ready() -> void:
# CCD_MODE_CAST_RAY is the standard for bullets
continuous_cd = true # In 4.0+, this is a boolean or enum depending on version
# Note: In Godot 4, CCD is configured via individual body settings or Server.
# Optimization: Report only the first contact
max_contacts_reported = 1
contact_monitor = true
# physics_layers_3d_config.gd
# 3D Collision matrix architecture using bitmasks
extends Node
enum Layer {
WORLD = 1 << 0,
PLAYER = 1 << 1,
ENEMY = 1 << 2,
BULLET = 1 << 3,
INTERACTABLE = 1 << 4
}
func apply_bullet_collision(body: CollisionObject3D):
# Bullets are on BULLET layer
body.collision_layer = Layer.BULLET
# Mask for WORLD and ENEMY
body.collision_mask = Layer.WORLD | Layer.ENEMY
# physics_server_3d_bullets.gd
# High-performance 3D physics using PhysicsServer3D directly [17]
extends Node3D
# EXPERT NOTE: PhysicsServer3D is essential for 3D projectiles
# to avoid the overhead of thousands of RigidBody3D nodes.
var _bodies: Array[RID] = []
var _shape: RID
func _ready() -> void:
_shape = PhysicsServer3D.sphere_shape_create()
PhysicsServer3D.shape_set_data(_shape, 0.5)
for i in range(50):
var body = PhysicsServer3D.body_create()
PhysicsServer3D.body_set_mode(body, PhysicsServer3D.BODY_MODE_RIGID)
PhysicsServer3D.body_add_shape(body, _shape)
PhysicsServer3D.body_set_space(body, get_world_3d().space)
PhysicsServer3D.body_set_state(body, PhysicsServer3D.BODY_STATE_TRANSFORM, Transform3D(Basis(), Vector3(randf()*10, 10, randf()*10)))
_bodies.append(body)
func _exit_tree() -> void:
for body in _bodies:
PhysicsServer3D.free_rid(body)
PhysicsServer3D.free_rid(_shape)
# skills/physics-3d/scripts/ragdoll_manager.gd
extends Node
## Ragdoll Manager (Expert Pattern)
## Manages transition of character from Animated to Ragdoll state.
## Applies initial impulse for "meaty" impacts.
class_name RagdollManager
@export var skeleton: Skeleton3D
@export var character_collider: CollisionShape3D
func activate_ragdoll(impulse_dir: Vector3 = Vector3.ZERO, impulse_force: float = 0.0) -> void:
if not skeleton: return
# 1. Disable Main Collider (so it doesn't fight bones)
if character_collider:
character_collider.disabled = true
# 2. Start Simulation
skeleton.physical_bones_start_simulation()
# 3. Apply Impulse (e.g., Shotgun blast)
if impulse_force > 0.0:
# Apply to specific bone or all? usually torso (Bone 0 or near center)
# Getting physical bone by name is tricky, usually iterate children
for child in skeleton.get_children():
if child is PhysicalBone3D:
child.apply_central_impulse(impulse_dir * impulse_force)
# Break loop if only hitting one, or apply force field logic
func deactivate_ragdoll() -> void:
if not skeleton: return
skeleton.physical_bones_stop_simulation()
if character_collider:
character_collider.disabled = false
# Note: Blending back to animation pose is complex
# and requires manual bone transform interpolation.
## EXPERT USAGE:
## Call upon death. Pass 'hit_normal * -1' as impulse_dir.
# ray_query_3d_vision.gd
# Expert 3D line-of-sight using direct space state [Raycasting]
extends Node3D
func can_see_target(target: Node3D) -> bool:
var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(global_position, target.global_position)
query.collision_mask = 1 # World
query.exclude = [get_parent().get_rid() if get_parent() is CollisionObject3D else RID()]
var result = space_state.intersect_ray(query)
return result.is_empty() or result.collider == target
extends RayCast3D
## Debug tool to visualize RayCast3D hit points and normals.
## Usage: Enable 'enabled' property and 'visible'.
@export var line_color: Color = Color.RED
@export var hit_color: Color = Color.GREEN
@export var line_width: float = 2.0
@export var always_show: bool = false # Show even if not colliding
var _mesh_instance: MeshInstance3D
var _immediate_mesh: ImmediateMesh
var _material: StandardMaterial3D
func _ready() -> void:
_setup_visuals()
func _setup_visuals() -> void:
_mesh_instance = MeshInstance3D.new()
_mesh_instance.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
add_child(_mesh_instance)
_immediate_mesh = ImmediateMesh.new()
_mesh_instance.mesh = _immediate_mesh
_material = StandardMaterial3D.new()
_material.shading_mode = StandardMaterial3D.SHADING_MODE_UNSHADED
_material.vertex_color_use_as_albedo = true
_mesh_instance.set_surface_override_material(0, _material)
func _process(_delta: float) -> void:
if not is_inside_tree(): return
_immediate_mesh.clear_surfaces()
if not enabled and not always_show:
return
var start_pos := Vector3.ZERO
var end_pos := target_position
force_raycast_update()
if is_colliding():
end_pos = to_local(get_collision_point())
_draw_line(start_pos, end_pos, hit_color)
# Draw normal
var normal := get_collision_normal()
_draw_line(end_pos, end_pos + to_local(global_position + normal) - to_local(global_position), Color.AQUA)
else:
if always_show:
_draw_line(start_pos, end_pos, line_color)
func _draw_line(from: Vector3, to: Vector3, color: Color) -> void:
_immediate_mesh.surface_begin(Mesh.PRIMITIVE_LINES)
_immediate_mesh.surface_set_color(color)
_immediate_mesh.surface_add_vertex(from)
_immediate_mesh.surface_add_vertex(to)
_immediate_mesh.surface_end()
# shapecast_3d_ground_check.gd
# Robust ground detection for 3D characters using ShapeCast3D
extends ShapeCast3D
# EXPERT NOTE: ShapeCast3D is more reliable than RayCast3D for
# ground detection as it handles "step up" logic and uneven
# terrain without missing "holes" in the collision.
func _physics_process(_delta: float) -> void:
if is_colliding():
var normal = get_collision_normal(0)
var angle = rad_to_deg(acos(normal.dot(Vector3.UP)))
if angle < 45.0: # Max slope 45 deg
# Character is on walkable ground
pass
# soft_body_3d_interaction.gd
# Expert configuration for SoftBody3D (Flags, Cloaks, Foliage)
extends SoftBody3D
# EXPERT NOTE: Soft bodies are CPU heavy. Use them sparingly
# and use 'Simulation Precision' sparingly.
func attach_to_point(path: NodePath, bone: String = ""):
# Attaching a flag to a flagpole or character bone
var pin_point = 0 # Vertex index
set_point_pinned(pin_point, true)
# Logic usually involves Editor configuration, but scriptable for dynamic spawn
# vehicle_simulation_tuning.gd
# Tuning VehicleBody3D for arcade-style high-speed racing
extends VehicleBody3D
# EXPERT NOTE: VehicleBody3D is a complex Raycast-based sim.
# Tuning 'Stiffness' and 'Friction' on VehicleWheel3D is
# more important than engine force for 'feel'.
func apply_drift_physics(active: bool):
for wheel in get_children():
if wheel is VehicleWheel3D:
# Lower friction for drifting
wheel.wheel_friction_slip = 0.5 if active else 1.0
# Aggressive steering response
wheel.steering = Input.get_axis("right", "left") * 0.4