Quick answer: RigidBody2D → continuous_cd = CCD_MODE_CAST_RAY. Or raise physics_ticks_per_second. For hitscan, sweep a Raycast manually each tick.
Bullet at 1500 px/s. 60 Hz tick. 25 px gap per step. Hits anything thinner than 25 px? Sometimes. Anything thinner than 12 px? Almost never.
The Symptom
Some bullets damage; identical bullets at the same target miss without any visible reason.
The Fix
# Bullet
extends RigidBody2D
func _ready():
continuous_cd = CCD_MODE_CAST_RAY
linear_velocity = Vector2.RIGHT * 1500
# Or per-project: physics tick rate
# project.godot → physics → common → physics_ticks_per_second = 120
# Hitscan alternative
func _physics_process(_d):
var ray = PhysicsRayQueryParameters2D.create(prev_pos, position)
var hit = get_world_2d().direct_space_state.intersect_ray(ray)
if hit:
hit.collider.take_damage()
queue_free()
prev_pos = position
CCD adds a sweep cost; use only on fast movers. Tick-rate bump affects every body, expensive at scale. Hitscan is cheapest if you don’t need ballistic bullets.
Verifying
Fire bullets at a thin (5 px) wall. Without CCD: most pass through. With CCD: every shot registers.
“CCD on fast bodies. Or sweep manually. Hits land.”
Related Issues
For client prediction, see prediction. For tween signal, see tween signal.
CCD on. Tunnels close. Hits land.