- 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
67 lines
2.0 KiB
Markdown
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.
|