Coding Quests
The Scroll Library
Tutorials

Godot 4 Hitbox and Hurtbox Tutorial (2D Combat)

June 16, 20266 min read

Once your characters move, the next thing they want to do is hit each other. The clean way to do combat in Godot is the hitbox and hurtbox pattern. It sounds like two names for the same thing. It isn't, and the distinction is what makes the whole system work.

Hitbox vs hurtbox

A hitbox deals damage. It's the swing of a sword, the blast radius of a grenade, the spikes on a trap. It's attached to the thing doing the hurting.

A hurtbox receives damage. It's the area of a character that can be hit. It's attached to the thing that can get hurt.

When a hitbox overlaps a hurtbox, damage happens. Keeping them separate means an enemy's attack can hit the player without the enemy accidentally damaging itself, and you can have a character that deals damage in one spot while being vulnerable in another.

Both are just Area2D nodes. The difference is entirely in how you wire them up.

Set up collision layers

This is the part people skip, and then nothing works. Go to Project Settings, Layer Names, 2D Physics, and name a few layers so you don't lose track. Something like:

  • Layer 2: player_hurtbox
  • Layer 3: enemy_hurtbox
  • Layer 4: player_hitbox
  • Layer 5: enemy_hitbox

The rule: a hitbox sits on its own layer and masks the hurtbox layer it should hit. So the player's hitbox is on player_hitbox and masks enemy_hurtbox. It can only ever detect enemy hurtboxes. That's how you stop friendly fire without writing a single if check.

The hurtbox

The hurtbox is the simple one. It's an Area2D with a CollisionShape2D, and it forwards hits to a health component:

GDScript
extends Area2D
class_name Hurtbox
@export var health: HealthComponent
func take_hit(damage: int) -> void:
if health:
health.take_damage(damage)

The hitbox

The hitbox carries a damage value and watches for hurtboxes entering it:

GDScript
extends Area2D
class_name Hitbox
@export var damage: int = 10
func _ready() -> void:
area_entered.connect(_on_area_entered)
func _on_area_entered(area: Area2D) -> void:
if area is Hurtbox:
area.take_hit(damage)

That's the entire system. When an enemy hurtbox enters the player's hitbox, area_entered fires, we confirm it's a hurtbox, and we deal damage. The health component (which you can build with the health and damage guide) takes it from there and tells the UI to update through signals.

Turning the hitbox on and off

A sword shouldn't deal damage when it's sheathed. Enable the hitbox's collision only during the active frames of an attack. The easiest way is an AnimationPlayer track that toggles the CollisionShape2D's disabled property, or do it in code:

GDScript
func attack() -> void:
$Hitbox/CollisionShape2D.disabled = false
await get_tree().create_timer(0.2).timeout
$Hitbox/CollisionShape2D.disabled = true

Use set_deferred("disabled", ...) if Godot warns you about changing collision state mid-physics-step. That warning trips up a lot of people, and deferring the change is the fix.

Build a real combat system

Hitboxes and hurtboxes are the foundation, but real combat needs hit reactions, knockback, invincibility frames, and attack animations driving the timing. Our 2D Combat quest builds all of that into a working top-down fighter, including player death and respawn. Once the pattern clicks, every melee weapon, projectile, and trap in your game uses the same two nodes.

FAQ

What's the difference between a hitbox and a hurtbox?

A hitbox deals damage and is attached to whatever is attacking, like a sword swing. A hurtbox receives damage and is attached to whatever can be hit, like a character's body. Damage happens when a hitbox overlaps a hurtbox. Separating them prevents attackers from hurting themselves.

Should hitboxes be Area2D or PhysicsBody2D?

Area2D. You want to detect overlaps and deal damage, not push objects around or block movement. Area2D's area_entered signal is exactly the overlap detection combat needs, without the physics collision of a body node.

How do collision layers and masks work for combat?

A node's layer is what it is; its mask is what it scans for. Put a hitbox on its own layer and set its mask to only the hurtbox layer it should damage. That way the player's attacks detect enemy hurtboxes and nothing else, which kills friendly fire without any code.

godottutorialcombat2d

Reading is the map. The quest is the territory.

Build this for real in the 2D Combat Quest.

Start the Quest
Written by Coding Quests

We teach Godot 4 by making you build complete systems: inventories, save systems, souls-like controllers, enemy AI. The scrolls are free. The quests are where it sticks.