QUICKUPSCALE

PRACTICAL GUIDE

Game-engine sprite workflows

Build and verify a Godot collision polygon from sprite alpha

Start here

Derive a BitMap from alpha, generate its contour, and attach a solid CollisionPolygon2D directly under the intended StaticBody2D. Verify real physics queries against independently decoded source alpha.

Expected result:

The retained correct and restored physics matrices match all 768 source pixel centers, including eight alpha-128 centers. A stale opaque-only contour passes the obvious probes but misses exactly those eight centers.

What you need

The original CC0 32×24 PNG is a connected L shape with no holes. The project includes its editable scene, case script and deliberately stale stale_opaque.tscn resource. It keeps the image canvas intact and creates physics geometry. Godot is separate; the extracted project runs its supplied assets without Node or Python.

Tested with: Godot 4.7.2.stable.official.ed1daf0bf, Compatibility, OpenGL 4.1 / Metal 91.7 on Apple M4. Retained native viewport/physics observations and independent source-alpha checks, not a new engine run or editor-click sequence.

1. Run the original scene and negative controls

Extract the project and enter godot-workflow-reserves. Replace godot with your executable and choose a new absolute evidence directory:

godot --headless --editor --language en --path . --import
godot --path . --position 80,80 -- \
  --case=collision --out=/absolute/evidence/collision

The rendered run writes five viewport captures and observations.json, then exits. It exercises correct, stale, recovered, wrong-parent and final restored states. Headless import alone does not execute the rendered/physics result.

2. Declare alpha, alignment and solid contour policy

This fixture uses top-left alignment (centered=false), an unscaled body at the origin, threshold 0.5, simplification epsilon 0.25 and solid build mode. Its script uses:

var source := load_original("res://assets/collision.png")
var bitmap := BitMap.new()
bitmap.create_from_image_alpha(source, 0.5)
var polygons = bitmap.opaque_to_polygons(
    Rect2i(Vector2i.ZERO, source.get_size()), 0.25)
if polygons.size() != 1:
    return # This original fixture requires one connected contour.
$Body/Contour.polygon = polygons[0]

The independent source expectation contains 192 opaque centers plus eight alpha-128 centers at x=10..11/y=6..9. The adjacent alpha-127 band is excluded: 128/255 exceeds 0.5, while 127/255 does not. This expected mask comes from source alpha, not Godot’s generated BitMap.

3. Compare actual physics against the source mask

After two physics frames, the case queries every pixel center (x+0.5,y+0.5), with collision mask 1 and areas excluded. Each hit must identify /root/WorkflowReserves/CollisionMask/Body; a different body does not satisfy the check.

The six explicit probes include opaque stem (6.5,6.5), opaque foot (22.5,17.5), transparent notch (20.5,6.5), outside (1.5,1.5), alpha-128 (10.5,7.5) and alpha-127 (12.5,7.5). Correct physics hits the first two and the alpha-128 probe.

Godot direct-child collision case with the L-shaped sprite and diagnostic contour.
Retained original 960×540 native viewport with the contour directly under the physics body. The outline is diagnostic; independent source alpha and returned physics-body identities establish the collision result.

Across correct, stale and final restored 32×24 matrices, the retained 2,304 queries yield 200, 192 and 200 occupied centers. Correct/restored states have zero source-alpha mismatches. Convex decomposition can return multiple shape hits on the same body, so compare collider identities as a set.

4. Catch the plausible stale contour

The negative run disables the correct contour and activates the six-vertex stale_opaque.tscn on the same body. All four obvious opaque/transparent probes still agree, but the alpha-128 probe misses. The independent full matrix rejects exactly eight missing centers before restoration recovers them.

The yellow diagnostic outline depicts the intended mask throughout; it is not the physics oracle. Actual query markers and returned body identities distinguish the stale resource from the correct contour.

5. Keep the contour directly under its physics body

A second control reparents the unchanged correct contour under Body/Sprite, preserving its global transform and vertices. All six explicit probes then miss even though the intended outline remains in place. Moving it directly back under Body restores the intended hits and the complete source match.

Use solid build mode for interior-point expectations; hollow segment edges do not establish those hits. These results cover this known static silhouette and pixel-center probes. They do not establish holes, arbitrary silhouettes, edge-contact precision, transformed/centered sprites, animated hitboxes or dynamic-body performance.

For a new sprite, independently decode its alpha and rerun both geometry and actual physics checks at the chosen threshold. A BitMap count or drawn contour alone is insufficient.

Sources and next steps