Quick answer: Hitbox/hurtbox mismatches happen when the collision volumes your game checks don’t align with what players see on screen. Debug them by drawing wireframe overlays on every active volume, stepping through frames one tick at a time, and logging every overlap test result with positions and frame numbers. The most common root causes are animation timing offsets, capsule-vs-mesh discrepancies, and rollback resimulation drift in netcode.

Few bugs generate more player rage than a clearly landed hit that deals no damage — or an attack that connects from two character widths away. Hitbox/hurtbox mismatches are the collision equivalent of off-by-one errors: subtle in the code, obvious in the replay clip your community posts on social media. Here’s a systematic approach to finding and fixing them before your players do.

Understanding the Terminology

A hitbox is the offensive collision volume attached to an attack. A hurtbox is the defensive volume on a character that can receive damage. In most fighting game engines these are separate layers: a punch activates a hitbox on the fist for frames 4–8 of the animation, and if that hitbox overlaps the opponent’s hurtbox during those frames, the hit registers. Mismatches occur when the hitbox and hurtbox are out of spatial or temporal alignment — the volumes never overlap on the same frame even though the visual animation shows contact.

Some engines add a third category: pushboxes that prevent characters from overlapping. Pushbox desync is a different class of bug (characters phasing through each other), but the debugging technique is identical: draw the volumes, step the frames, read the logs.

Building a Debug Overlay

The single most effective debugging tool is a real-time wireframe overlay that draws every active collision volume on screen. Color-code them: red for hitboxes, green for hurtboxes, blue for pushboxes, grey for inactive volumes. The overlay should toggle with a debug key and render on top of everything else.

// DebugHitboxRenderer.cs — Unity example
using UnityEngine;

public class DebugHitboxRenderer : MonoBehaviour
{
    public Color hitboxColor = Color.red;
    public Color hurtboxColor = Color.green;
    private bool showOverlay = false;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.F2))
            showOverlay = !showOverlay;
    }

    void OnDrawGizmos()
    {
        if (!showOverlay) return;

        foreach (var vol in GetComponentsInChildren<CollisionVolume>())
        {
            Gizmos.color = vol.type == VolumeType.Hitbox
                ? hitboxColor : hurtboxColor;
            Gizmos.matrix = vol.transform.localToWorldMatrix;
            Gizmos.DrawWireCube(vol.localCenter, vol.localSize);
        }
    }
}

In Godot, use _draw() overrides with draw_rect() or draw_circle() on a Node2D overlay. In Unreal, use DrawDebugBox or DrawDebugCapsule from the DrawDebugHelpers header. The key is making it cheap enough to leave on during QA sessions without affecting frame times.

Frame-by-Frame Stepping

Collision bugs that seem random are almost always timing bugs. You need to see exactly which simulation tick the hitbox activates and whether the hurtbox is in the right place at that tick. Build a frame-step mode into your debug tools.

# frame_stepper.gd — Godot 4 example
extends Node

var stepping_enabled := false
var advance_frame := false

func _process(delta):
    if Input.is_action_just_pressed("debug_toggle_step"):
        stepping_enabled = !stepping_enabled
        Engine.time_scale = 0.0 if stepping_enabled else 1.0

    if stepping_enabled and Input.is_action_just_pressed("debug_advance"):
        advance_frame = true

func _physics_process(delta):
    if stepping_enabled:
        if advance_frame:
            advance_frame = false
            # Allow this single physics tick
            get_tree().call_group("hitbox_debug", "log_frame_state")
        else:
            return  # Skip this tick

With stepping enabled, press a key to advance one tick. Combined with the debug overlay, you can watch a hitbox appear, grow, and disappear frame by frame. If the hitbox is active for only two frames and the hurtbox has already moved out of range by the second frame, you’ve found your mismatch.

Rollback Netcode and Phantom Hits

If your game uses rollback netcode (GGPO-style), hitbox mismatches gain an entirely new failure mode. During a rollback, the engine re-simulates several frames to correct a misprediction. If your hitbox activation logic accumulates state — for example, a flag that says “this hitbox already connected, don’t check again” — and that flag isn’t properly saved and restored during rollback, you get phantom hits (hits that register twice) or phantom misses (hits that should register but get skipped because the flag was stale).

The fix is strict: every piece of state that affects hitbox resolution must be included in your rollback snapshot. This includes the active-frame counter, the “already hit” set, and the hitbox positions. No exceptions. If you have to choose between correctness and a smaller snapshot, choose correctness.

“The scariest hitbox bugs are the ones that only appear online. You can’t reproduce them locally because the rollback path never triggers in single-player. You need a replay system that captures the rollback sequence, not just the final result.”

Capsule vs Mesh Collision Trade-offs

Many teams start with mesh-accurate collision shapes because they “look right” in the editor. This is almost always a mistake for gameplay hitboxes. Mesh colliders are expensive, unpredictable at edges, and introduce floating-point inconsistencies that vary across platforms. A capsule collider that approximates the limb is faster, cheaper to serialize for rollback, and easier to debug visually.

Reserve mesh colliders for environmental interactions where accuracy matters (a player standing on a ledge). For combat hitboxes, use capsules or boxes. Two capsules per arm, two per leg, one for the torso, one for the head — twelve primitives total give you plenty of resolution for a fighting game without the performance and determinism headaches of meshes.

When transitioning from mesh to primitive colliders, compare hit-registration rates in automated tests. Record 1,000 attack interactions with the old mesh colliders, then replay the same inputs with the new primitives. If the hit rate changes by more than two percent, adjust the primitive sizes until you match.

Structured Logging for Collision Events

Every hitbox overlap test should produce a structured log entry: attacker ID, defender ID, frame number, hitbox world position, hurtbox world position, overlap result (hit, miss, blocked), and the distance between centers. Ship these logs to your bug tracker as metadata so that when a player reports “my attack went through the enemy,” you can look up the exact frame data instead of guessing.

In Bugnet, attach this data as custom fields on the bug report. A triage engineer can filter by “hitbox miss” events, sort by distance, and immediately identify whether the issue is a timing problem (small distance, wrong frame) or a spatial problem (large distance, animation offset).

Related Issues

For physics-related desync in multiplayer contexts, see How to Set Up Structured Logging for Multiplayer Games. If your hitbox bugs surface as crashes instead of gameplay issues, check Common Game Crashes and How to Prevent Them.

If a player says “that hit should have landed,” your debug data should prove or disprove it in under a minute.