Files
agent-tools/hitbox-tool/README.md
claude f8924d89f7 hitbox-tool: per-frame collision boxes from sprite sheet alpha -> JSON
- parses the spritec _WxH_CxR naming convention incl. auto-detection of
  integer-upscaled sheets (256x64 named 8x8_4x1 -> 64x64 cells)
- tight alpha bbox per frame with threshold/shrink/pad tuning, row-major
  indices, empty-frame flags; boxes relative to frame origin
- show command draws frames + box outline in the terminal for verification
- go tests: scanning, threshold, shrink/pad clamping, name parsing,
  scale inference, JSON roundtrip

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmdG9GqfSWCzts7AkDwRDh
2026-07-14 07:12:05 +02:00

67 lines
2.0 KiB
Markdown

# hitbox-tool (`hitbox`)
Scans **sprite sheet PNGs** and writes per-frame **collision boxes as
JSON**, computed from the alpha channel. Closes the loop with
[`pixel-sprite-maker`](../pixel-sprite-maker/): render a sheet with
`spritec`, scan it with `hitbox`, and your game gets both graphics and
hitboxes without a human drawing rectangles. Go, zero dependencies,
single static binary.
## Build
```bash
# Arch/Garuda: sudo pacman -S go
cd hitbox-tool
go build -o build/hitbox . # or the VS Code task "build hitbox-tool"
go test ./...
```
## Usage
```bash
# spritec-named sheets need no flags — layout is parsed from the name,
# and integer upscales are auto-detected (a _8x8_4x1 sheet that is
# 256x64 px was rendered at --scale 8, so cells are 64x64):
hitbox scan walk_8x8_4x1.png -o walk.hitbox.json
hitbox scan boss.png --cell 32x32 -o boss.json # explicit frame size
hitbox scan portrait.png # whole image = one frame, JSON to stdout
hitbox show walk_8x8_4x1.png --frame 2 # verify visually in the terminal
# tuning:
--threshold 128 # ignore faint pixels (alpha < 128)
--shrink 1 # tighter, more forgiving hitboxes (n px per side)
--pad 2 # generous hitboxes (e.g. pickups)
```
## Output
```json
{
"image": "walk_8x8_4x1.png",
"cellW": 8, "cellH": 8, "cols": 4, "rows": 1,
"alphaThreshold": 1,
"frames": [
{ "index": 0, "col": 0, "row": 0, "empty": false,
"box": { "x": 1, "y": 0, "w": 5, "h": 8 } },
{ "index": 1, "col": 1, "row": 0, "empty": true, "box": null }
]
}
```
- Boxes are **relative to each frame's top-left corner**.
- Frame order is row-major (`index = row * cols + col`), matching
spritec sheets.
- Cells with no solid pixels get `"empty": true`.
In game code:
```
hit = px >= frameX + box.x && px < frameX + box.x + box.w
&& py >= frameY + box.y && py < frameY + box.y + box.h
```
`hitbox show` draws each frame with `#` for solid pixels and `+` for the
box outline, so an agent can verify the result without an image viewer.