Files
feuerwerk/todo-notes/frame-edge-bleeding.md
T
rootandClaude Opus 5 e147dd6d21 Add a four-direction walk demo that seeds its own sprite sheet
tools/seed-actor.mjs builds the project Actor demo: it uploads
tools/assets/Actor1.png, slices the 576x384 sheet into the 96 cells of a
12x8 grid (48x48 each, no remainder) and wears the character in the top
left corner. Walking is four ping-pong animations of three frames, which
is the original RPG-Maker walk cycle; standing is the middle frame of the
row, not an animation.

The graph pastes walk- or idle- in front of the direction faced and wears
the result, so eight cases need no branch tree - the List node only holds
numbers. Which way it looks is the key pressed last, corrected whenever
only one axis is still held, or letting go of one key of a diagonal would
walk backwards. Axis input already normalizes its vector.

tools/lib/projects.mjs gains uploadTexture, since data/textures/ is
git-ignored and a seed that ships its picture has to send it. The harness
gains w/a/s/d and keyDown/keyUp halves, because holdKey cannot express two
presses that overlap - which is what a diagonal is.

tools/smoke-actor.mjs plays it in 19 checks and reads the drawn pixels at
the camera edge, where the bounds hold him walking on the spot: three
distinct frames walking, one standing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 12:13:13 +02:00

1.4 KiB
Raw Blame History

Ein Sprite, das ein Frame aus einem Sheet zeigt, zeichnet am Rand eine Pixelzeile, die zum Nachbarframe gehört. Sichtbar im Projekt Actor demo als heller Streifen über dem Kopf.

Gemessen (__gm.pixelAt, Spielansicht): Beim Stehen nach unten (Sheet-Zeile 0, deren oberste Pixelzeile in der Datei komplett leer ist) liegt dort #394349 – bläulich. Das passt zur untersten Sheet-Zeile (Schuhe von Character 6, (87,87,154)) über den grünen Hintergrund gemischt, die Kante bricht also auf die Gegenseite um. Beim Laufen nach rechts (Zeile 2) ist der Streifen stattdessen hellgrau – die Füße aus Zeile 1 direkt darüber.

Ursache: frameFillMatrix (src/viewport/textureFill.ts) bildet die ganze Textur auf die Shape ab, weil Pixi-v8-Graphics-Fills Texture.frame ignorieren. Eine Fill-Matrix kann keine Grenze ausdrücken, also sampelt die Randzeile in den Nachbarn, sobald die Skalierung nicht ganzzahlig ist (die Spielansicht streckt 640×360 auf etwa 735×409, Faktor ~1,13).

Nicht die Ursache: die Frame-Rechtecke. Die sind exakt ganzzahlig; den fraktionalen Fall behandelt normalizeFrameRect bereits.

Abzuklären: halber Pixel Einzug in der Matrix (kostet einen halben Pixel Bildinhalt, trifft jedes Frame) oder ein echtes Sprite mit new Texture({source, frame}) statt des Graphics-Fills (Pixi klemmt dann selbst, ändert aber den Zeichenweg für alle Sprites).