Quick answer: A body with multiple collision shapes can trigger one signal per shape. Track entered bodies in a Set and ignore duplicates, or merge the body’s shapes.
A pickup’s Area2D awards coins on body_entered. A player with two collision shapes (body + feet) triggers it twice — double coins.
Why It Fires Twice
body_entered fires when the overlap relationship changes. If the entering body has more than one CollisionShape2D, Godot may report entry once per shape that crosses the boundary.
Dedupe with a Set
var _inside := {}
func _on_body_entered(body):
if _inside.has(body):
return
_inside[body] = true
award_coins(body)
func _on_body_exited(body):
_inside.erase(body)
Track which bodies are inside; ignore repeat entries. Clear on exit.
Or Use body_shape_entered
If you genuinely need per-shape granularity, connect body_shape_entered instead — it’s explicitly per-shape, so the doubled call is expected and you handle it deliberately.
Simplify the Body
If the player doesn’t need separate body/feet shapes, merge them into one CollisionShape2D. One shape = one entry signal.
Verifying
Walk the player into the pickup. Coins award exactly once. Re-entering after exiting awards again (correct). Multi-shape bodies don’t double-trigger.
“Multi-shape bodies fire per-shape. Dedupe with a Set, or merge shapes.”
The Set-tracking pattern is also how you handle ‘is anything in this trigger right now?’ — one pattern, two uses.