Saltmire
Practical Godot 4 tutorials — game feel, saving, and shipping polish fast.

Godot 4 Area2D not detecting hits? 6 reasons area_entered isn't firing

You connected area_entered, you can see the two areas overlapping in the editor, and nothing happens. This is the single most common Godot 4 combat bug, and it's almost always one of these six things. Work down the list — they're ordered by how often they're the culprit.

1. monitoring is off (or the wrong area is monitoring)

An Area2D only detects others when monitoring = true. And detection has a direction: the scanner needs monitoring; the thing being found needs monitorable. If both areas monitor each other you get double hits; if neither does, you get silence.

# the attacker scans, doesn't need to be found:
hitbox.monitoring = true
hitbox.monitorable = false
# the target is found, doesn't scan:
hurtbox.monitoring = false
hurtbox.monitorable = true

2. Layers and masks don't line up (the classic)

collision_layer is what you are; collision_mask is what you look for. For A to detect B, A's mask must include B's layer. Same number in both layer fields with empty masks = they never see each other.

# hurtbox exists on layer 3:
hurtbox.collision_layer = 0
hurtbox.set_collision_layer_value(3, true)
hurtbox.collision_mask = 0
# hitbox looks for layer 3:
hitbox.collision_layer = 0
hitbox.set_collision_mask_value(3, true)

Check this in code, not just the inspector — a parent scene or tween can overwrite layer bits at runtime and the panel won't show it.

3. There's no enabled CollisionShape2D

An Area2D with no shape (or a shape whose disabled is true, or whose shape resource is null) has nothing to collide with. It fails silently — no error, no warning.

var cs := hitbox.get_node("CollisionShape2D")
assert(cs.shape != null and not cs.disabled, "hitbox shape missing/disabled")

A frequent trap: calling cs.disabled = true to "turn off" an attack and forgetting to flip it back before the next swing.

4. The signal is connected to the wrong node

area_entered fires on the area that's doing the monitoring. If you connected the signal on the hurtbox but the hurtbox has monitoring = false (correct, per #1), the callback never runs. Connect area_entered on the monitoring area, or use body_entered if the other side is a CharacterBody2D/StaticBody2D rather than an Area2D.

hitbox.area_entered.connect(_on_hit)   # hitbox monitors, so it emits

Mixing up area_entered (Area↔Area) and body_entered (Area↔Body) is its own silent no-op — pick the one that matches what the other node actually is.

5. The hit started already overlapping (the melee trap)

area_entered only fires the frame two areas begin overlapping. If your hitbox turns on while it's already sitting on top of the target — extremely common for a melee swing that activates mid-contact — the signal never fires and the hit is lost. Scan what's already inside when you activate:

func activate() -> void:
    monitoring = true
    for area in get_overlapping_areas():
        _on_hit(area)

Note get_overlapping_areas() is only accurate after at least one physics frame of monitoring — if you enable and scan in the same frame, call await get_tree().physics_frame first.

6. Physics is paused or the node is disabled

If the area's process_mode is PROCESS_MODE_DISABLED, or the tree is paused and the node doesn't have PROCESS_MODE_ALWAYS, monitoring quietly stops. This bites during hit-stop / slow-motion, when you drop Engine.time_scale or pause the tree for feel.

# keep the hitbox live through a pause used for hit-stop:
hitbox.process_mode = Node.PROCESS_MODE_ALWAYS

The fast way to never hit this again

Every bug above comes from the same place: Area2D gives you the raw primitive and makes you wire the rules — direction, layers, shapes, overlap-on-activate, pause behavior — by hand, with no errors when you get it wrong. If you'd rather drop in two nodes that already handle all six, that's exactly what Saltmire Hitbox is: Hitbox2D / Hurtbox2D with zero layer setup, correct monitoring out of the box, and the melee overlap case handled for you.

Built this the long way once too many times. Saltmire Hitbox does it as a drop-in tool: https://saltmire.itch.io/saltmire-hitbox