A frame's collision outline can be generated from its pixels (#161)
game-maker / gate (push) Successful in 2m13s
game-maker / suite (push) Successful in 7m42s

Drawing an outline by hand for every frame of a sheet is slow, and the
outline mostly follows the opaque part of the image anyway. The texture
tab now traces it: one click, a corner budget, and every Frame
collision collider and tile using that frame blocks with the result.

- "Generate collision..." sits in the texture tab header and is off
  while the texture has no frames.
- The dialog has Max points (default 8, 3 to 64), Solid from ... %
  alpha (default 50) and All frames of the texture. A live line says
  what Generate will write, and Generate stays off when nothing is
  solid enough.
- traceCollision walks each island of solid pixels along the pixel
  edges, then drops the least important corners until at most N are
  left and no stair step remains. A slope becomes one straight edge.
- An outline never crosses itself. Where the image would force that,
  the island's convex hull is taken instead.
- Crumbs under 1 % of the frame area are dropped unless they are the
  only island. Holes are not reproduced.
- Existing outlines are replaced. One frame or all frames is a single
  undo step.
- TextureLibrary hands out the sheet's RGBA data, and a pure function
  cuts a frame's region from it.

Findings:
- The wedge in the e2e fixture gets 4 corners, not 3. Its tip is one
  pixel wide in the image, and that real edge stays while corners are
  still free.

Tests:
- ./check.sh: 131 files, 1941 tests, CHECK EXIT 0 (was 130 / 1931).
- 10 new unit tests in tests/core/trace-collision.test.ts, including
  150 random shapes that must all be valid and within the budget.
- GM_STEPS=42, freshly seeded: ALL PASSED, 20 checks (was 16).
- Three counter-probes bite: no hull fallback, no stair threshold,
  dialog ignoring the alpha threshold.
- Check sum 1202 in README is computed (1198 + 4), CI measures it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5 committed 2026-09-21 08:00:30 +02:00
1 parent f4ec23d3d3
commit 2d79230b55
10 files changed
+705 -5

No files matched your search

+27
View File
@@ -150,6 +150,33 @@ export class SetFrameCollisionCommand implements Command {
}
}
/**
* Replaces the outlines of several frames at once – what "Generate collision…" writes (#161), one
* undo step whether it covered one frame or the whole sheet.
*/
export class SetFramesCollisionCommand implements Command {
readonly label = 'Generate collision';
private previous = new Map<string, Vec2[][]>();
constructor(
private readonly assetId: string,
private readonly outlines: ReadonlyMap<string, readonly (readonly Vec2[])[]>,
) {}
execute(project: Project): void {
const asset = requireTexture(project, this.assetId);
if (this.outlines.size === 0) throw new CommandError('No frame to generate a collision outline for');
const frames = [...this.outlines.keys()].map((id) => requireFrame(asset, id));
this.previous = new Map(frames.map((frame) => [frame.id, frameCollision(frame)]));
for (const frame of frames) setFrameCollision(frame, this.outlines.get(frame.id)!);
}
undo(project: Project): void {
const asset = requireTexture(project, this.assetId);
for (const [id, outlines] of this.previous) setFrameCollision(requireFrame(asset, id), outlines);
}
}
export class RenameFrameCommand implements Command {
readonly label = 'Rename frame';
private previous = '';