Quick answer: Set continuous_cd = true on the RigidBody3D and ensure walls are at least velocity / tick_rate thick. For bodies exceeding 30 m/s through thin geometry, consider switching to the Jolt Physics backend.
A car physics demo, a projectile system, a falling rock the player can shoot with a missile — all suffer the same failure mode at high velocity: collisions are skipped because the body has already crossed the wall by the next physics tick. In 3D the consequences can be larger: a falling rigid body can fall through the ground entirely.
The Tunneling Math
Godot Physics 3D defaults to 60 Hz physics. A body moving 60 m/s travels 1 m per tick. A wall less than 1 m thick along the motion vector is at risk. For a rigid body with a 0.5 m diameter, walls need to be (wall_thickness + body_diameter) thicker than the per-tick distance to guarantee at least one tick of overlap. The smallest passable wall is about velocity / tick_rate + body_diameter.
Fix 1: Enable Continuous CD
The primary fix on Godot Physics 3D:
# projectile.gd
extends RigidBody3D
func _ready():
continuous_cd = true
contact_monitor = true
max_contacts_reported = 4
Continuous CD performs a swept-cast from the body’s previous position to its predicted position each step. If the cast hits something, the body is repositioned to the contact point and contact callbacks fire normally. Without it, only end-of-step overlap is tested.
Fix 2: Higher Physics Tick Rate
For vehicles, projectiles, and reactive physics, doubling the tick rate gives you double the time resolution at the cost of double the CPU. Project Settings → Physics → 3D → Physics Ticks Per Second:
physics_ticks_per_second = 120 # default 60
Bullet-physics-style games run at 240 Hz with measured stability gains, though most action games are fine at 120.
Fix 3: Jolt Physics Backend
The Godot Jolt extension provides a different physics solver with broader continuous-CD support and better handling of fast movers. Install via Asset Library or directly from the project:
# project.godot
[physics]
3d/physics_engine = "JoltPhysics3D"
Jolt uses sweep-based continuous collision detection even without an explicit flag for most body types, though you can still tune per-body. APIs are largely compatible with the built-in physics, but a handful of properties differ — check the Jolt docs for migration notes if you have an existing scene.
Fix 4: Manual Substepping for Critical Objects
If you don’t want to enable CCD globally and Jolt isn’t an option, you can implement substepping for select bodies in _integrate_forces:
extends RigidBody3D
const SUBSTEPS = 4
func _integrate_forces(state: PhysicsDirectBodyState3D):
var sub_delta = state.step / SUBSTEPS
for i in SUBSTEPS:
var from = state.transform.origin
var to = from + state.linear_velocity * sub_delta
var query = PhysicsRayQueryParameters3D.create(from, to)
var hit = state.get_space_state().intersect_ray(query)
if hit:
state.transform.origin = hit.position
state.linear_velocity = state.linear_velocity.bounce(hit.normal) * 0.5
break
state.transform.origin = to
This is hand-rolled CCD with bouncing. Each substep does a ray sweep over a smaller distance, catching collisions the engine would miss.
Verifying
Build a test scene with a thin wall (0.1 m) and launch a rigid body at increasing speed. Without CCD, you’ll find a threshold — say 6 m/s — above which the body passes through. With CCD enabled, the threshold should rise dramatically (often beyond the practical limits of your game). Log body_entered signals from the wall to confirm contacts are being detected.
“In 3D, falling-through-the-world bugs are almost always tunneling. Enable CCD on the body and raise tick rate on the project.”
For projectiles, prefer a raycast or area-sweep system over physics bodies — the engine isn’t solving anything you actually need.