diff --git a/HANDOFF.md b/HANDOFF.md index 93fd126..cfb2c58 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -467,6 +467,23 @@ the title screen. `menuMusic` passes `48`. Re-encode the file and re-measure: frames at 48 kHz — an un-round master, and the decoder returns 20.4787, so 7.9 ms of padding is trimmed). Both beds are AAC now; nothing in `assets/audio/` is still a multi-megabyte WAV. +**A SPRITE THAT LOOKS "SHRUNK" IS AN ANCHOR BUG, NOT AN ART BUG.** `_fine.py` scales the whole +generated sheet so that `--anchor`'s frame is exactly 224 px, and `drawChar` then draws every sprite +at `img.height / SPRITE_H`. So if the anchor frame is one where a PROP goes above the head, 224 px +has to cover the prop too and every body on that sheet comes out a head short. `opponent_play` was +`[o_toss, o_smash, o_swing1, o_swing2]` with `anchor: 0` — anchored on the TOSS, arm and ball +stretched overhead — which is why the opponent visibly shrank whenever he served or swung +(o_swing1 came out 162 px against a 224 px standing pose). **Every batch must lead with a plain +standing figure via `ref`, which `_fine.py` anchors on and `_regen.py` then discards** — the shape +the player's own batches already used. Correctly anchored, a toss frame is TALLER than 224 (278 px +here) and the body renders at full size. Quick audit: a standing frame should be ~224, a raised-arm +frame >224, and nothing should be well under it. + +**The opponent wears a RED headband.** A prompt that said "white headband" silently repainted it on +six frames, and it only showed up against the untouched `o_toss`/`o_idle1`. When regenerating part +of a character, name his colours in the prompt AND check the regenerated frames against an untouched +one — `check: "same"`/`"loose"` skips `colour_match`, so nothing else will catch a palette drift. + **The opponent was running with empty hands.** `o_run1`/`o_run2` had no racket in either fist, and the cause was the batch prompt: `opponent_run` never said the word "racket" and its reference sheet was built from the racket-less frames it was replacing, so the generator had nothing to copy one diff --git a/assets/_regen_batches.json b/assets/_regen_batches.json index 0b550a3..9dc110a 100644 --- a/assets/_regen_batches.json +++ b/assets/_regen_batches.json @@ -143,14 +143,16 @@ "opponent_run": { "sprites": [ "o_run1", - "o_run2" + "o_run2", + "o_run3", + "o_run4" ], "ref_only": [ "o_idle1" ], "anchor": 0, "check": "same", - "subject": "The reference is ONE tennis opponent in a RED polo shirt, dark grey shorts and a white headband, seen from the FRONT, gripping a tennis racket in his RIGHT hand. Draw TWO frames of that SAME man, same size, same clothes, same face, same front-facing view, RUNNING across the court towards the camera as two stages of one stride: the first with his left leg forward and his right leg trailing, the second with the legs reversed. In BOTH frames he is STILL HOLDING THE TENNIS RACKET in his right hand, the same racket as the reference \u2014 head, strings, throat and grip all drawn, carried out to his side at about waist height as a runner carries it. His other arm swings free. The racket must be clearly visible and fully inside the canvas in both frames; neither hand is ever empty." + "subject": "The reference is ONE tennis opponent in a RED polo shirt, dark grey shorts and a RED HEADBAND, seen from the front, gripping a tennis racket in his right hand. Draw FOUR frames of that same man RUNNING towards the camera. THE FOUR FRAMES MUST BE FOUR DIFFERENT BODY POSITIONS \u2014 no two frames alike, do not repeat a pose: (1) his RIGHT knee lifted high in front of him, left leg stretched out straight behind, left arm forward; (2) his right foot planted flat on the ground under him taking his weight, left knee just beginning to swing forward, both arms low; (3) his LEFT knee lifted high in front of him, right leg stretched out straight behind, right arm forward; (4) his left foot planted flat on the ground under him taking his weight, right knee just beginning to swing forward, both arms low. Every figure is the SAME man as the reference and the SAME size: red polo shirt, dark grey shorts, RED HEADBAND, blond hair, seen from the FRONT. Same face, same colours, same proportions, standing the same height. IN EVERY ONE OF THE FOUR FRAMES he is HOLDING THE TENNIS RACKET in his right hand \u2014 the full racket, with its oval head, its strings, its throat and its grip all drawn, exactly like the reference's racket, carried close in front of his chest. NO FRAME MAY HAVE AN EMPTY RIGHT HAND and no frame may show a racket with the head missing. The whole figure and the whole racket sit fully inside the canvas with clear space around them. The canvas must contain EXACTLY FOUR figures and no others. Do NOT redraw the standing reference pose as an extra figure." }, "ump_boy": { "sprites": [ @@ -391,5 +393,47 @@ "anchor": 0, "check": "same", "subject": "The reference is ONE tennis player in a white shirt, blue shorts and a white headband, seen from behind, serving with the racket high in his right hand. Draw FOUR frames of that same man. Every figure is seen strictly FROM BEHIND \u2014 the back of his head and the back of his shirt towards you, NO face, NO eyes, NO nose, NO mouth. Every figure is drawn from exactly the same viewpoint: same camera height, same distance, straight on, standing on the same level ground line, with no perspective distortion, no foreshortening, no worm's-eye or bird's-eye angle and no three-quarter turn. Same size, same body, same style, same BRIGHT WHITE shirt and BRIGHT BLUE shorts as the reference \u2014 do not wash the colours out. He is RIGHT-HANDED and the racket NEVER changes hands: in every single frame the racket is in his RIGHT hand, which \u2014 because he is drawn from behind \u2014 is on the RIGHT-HAND SIDE OF THE IMAGE, exactly as in the reference. His empty left arm is on the LEFT-HAND SIDE OF THE IMAGE. Draw NO ball anywhere in any frame \u2014 the game draws the ball. Frame 1: he STANDS upright and relaxed at the baseline, arms down, the racket hanging at his right side. Frame 2: he leans forward a little and his empty LEFT hand reaches down and out to the LEFT-HAND SIDE OF THE IMAGE, palm down, as if patting a ball on the ground beside his left foot; the racket stays in his right hand at his right side. Frame 3 IS THE SAME DRAWING AS FRAME 2 \u2014 identical stance, identical legs, identical head, identical racket, identical everything \u2014 and the ONLY difference is that his left elbow is bent a little more so that left hand has risen about one hand's width higher, as if the ball had bounced back up into it. Frame 4: the ball toss \u2014 he leans back and throws that same empty LEFT arm up high above him on the LEFT-HAND SIDE OF THE IMAGE, having just released the ball, while the racket stays in his right hand down at his right side. Output exactly four figures in one row, evenly spaced and far apart." + }, + "opponent_swing": { + "sprites": [ + "o_swing1", + "o_swing2" + ], + "ref": "o_idle1", + "anchor": 0, + "check": "loose", + "subject": "Three frames of the same tennis opponent, drawn as coarse low-resolution pixel art. FIRST a plain standing frame for scale, exactly like the reference. THEN two frames of a FOREHAND: the racket drawn back across his body, then the follow-through carried through to the other side. Every figure is the SAME man as the reference and the SAME size: RED polo shirt, dark grey shorts, RED cloth headband, blond hair, seen from the FRONT. Same face, same colours, same proportions. His headband is RED, the same red as his shirt \u2014 never white, cream or grey. In both swing frames he stays UPRIGHT at his full standing height \u2014 head at the same level as the standing frame, back straight, knees only slightly bent. He does not crouch, squat or lunge low. The whole racket is drawn and fully inside the canvas." + }, + "opponent_idle": { + "sprites": [ + "o_idle1", + "o_idle2" + ], + "ref_only": [ + "o_idle1" + ], + "anchor": 0, + "check": "same", + "subject": "The reference is ONE tennis opponent standing and waiting, gripping a tennis racket in his right hand. Draw TWO frames of that same man waiting to receive, standing at the SAME FULL HEIGHT as the reference: the two frames are one relaxed weight shift, first with his weight on his left foot, then on his right, racket held ready in front of him in both. Every figure is the SAME man as the reference and the SAME size: RED polo shirt, dark grey shorts, RED headband, blond hair, seen from the FRONT across the net. Same face, same colours, same proportions. CRITICAL: the ENTIRE racket \u2014 head, strings, throat and grip \u2014 is fully inside the canvas in both frames, with clear empty space between the racket and every edge. Nothing is cut off at an edge. He stands upright and does not crouch. His headband is RED, the same red as his shirt \u2014 it is NEVER white, cream or grey." + }, + "opponent_toss": { + "sprites": [ + "o_toss", + "o_smash" + ], + "ref": "o_idle1", + "anchor": 0, + "check": "loose", + "subject": "Three frames of the same tennis opponent, drawn as coarse low-resolution pixel art. FIRST a plain standing frame for scale, exactly like the reference. THEN the ball TOSS \u2014 standing tall, his free hand stretched straight up above his head having just released the ball, racket back and low. THEN the SMASH \u2014 at full stretch, the racket swung up overhead to hit. Every figure is the SAME man as the reference and the SAME size: RED polo shirt, dark grey shorts, RED cloth headband, blond hair, seen from the FRONT. Same face, same colours, same proportions. His headband is RED, the same red as his shirt \u2014 never white, cream or grey. In the toss and smash frames his FEET STAY ON THE GROUND LINE and his body stays the same size as the standing frame; only his arms reach above his head. The whole racket is drawn and fully inside the canvas. His shorts are DARK CHARCOAL GREY, almost black \u2014 the same dark grey as the reference figure standing beside them, definitely NOT pale grey, light grey or blue. His shoes are white. Match the reference palette exactly in every frame." + }, + "opponent_bounce": { + "sprites": [ + "o_bounce", + "o_bounce2" + ], + "ref": "o_idle1", + "anchor": 0, + "check": "loose", + "subject": "Three frames of the same tennis opponent, drawn as coarse low-resolution pixel art. FIRST a plain standing frame for scale, exactly like the reference. THEN TWO frames of him standing at the baseline BOUNCING A BALL on the ground before serving, with NO ball drawn. Every figure is the SAME man as the reference and the SAME size: RED polo shirt, dark grey shorts, RED cloth headband, blond hair, seen from the FRONT. Same face, same colours, same proportions. His headband is RED, the same red as his shirt \u2014 never white, cream or grey. In both bouncing frames his racket is held out to the side in his RIGHT hand \u2014 which is on the LEFT-HAND SIDE OF THE IMAGE \u2014 and his other arm is stretched DOWN AND OUT to the RIGHT-HAND SIDE OF THE IMAGE, well clear of his body, with the palm open and facing the ground as if patting a ball. CRITICAL: the two bouncing frames are otherwise the IDENTICAL drawing \u2014 same stance, same legs, same head, same racket, same size \u2014 and the ONLY difference between them is the height of that patting hand: in the first it is low, near knee height; in the second it is higher, near his waist. Draw NO ball in any frame." } } \ No newline at end of file diff --git a/assets/o_bounce.png b/assets/o_bounce.png index 05dec4d..38c2f54 100644 Binary files a/assets/o_bounce.png and b/assets/o_bounce.png differ diff --git a/assets/o_bounce2.png b/assets/o_bounce2.png index 18cd029..875bd7e 100644 Binary files a/assets/o_bounce2.png and b/assets/o_bounce2.png differ diff --git a/assets/o_idle1.png b/assets/o_idle1.png index 42d97a3..f6bc819 100644 Binary files a/assets/o_idle1.png and b/assets/o_idle1.png differ diff --git a/assets/o_idle2.png b/assets/o_idle2.png index 8719046..aa750ec 100644 Binary files a/assets/o_idle2.png and b/assets/o_idle2.png differ diff --git a/assets/o_run1.png b/assets/o_run1.png index 02f8d55..77e808e 100644 Binary files a/assets/o_run1.png and b/assets/o_run1.png differ diff --git a/assets/o_run2.png b/assets/o_run2.png index 962518b..cf122fd 100644 Binary files a/assets/o_run2.png and b/assets/o_run2.png differ diff --git a/assets/o_run3.png b/assets/o_run3.png new file mode 100644 index 0000000..d929975 Binary files /dev/null and b/assets/o_run3.png differ diff --git a/assets/o_run4.png b/assets/o_run4.png new file mode 100644 index 0000000..845125f Binary files /dev/null and b/assets/o_run4.png differ diff --git a/assets/o_smash.png b/assets/o_smash.png index 8708fca..7720210 100644 Binary files a/assets/o_smash.png and b/assets/o_smash.png differ diff --git a/assets/o_swing1.png b/assets/o_swing1.png index 2de3ead..8c93ce1 100644 Binary files a/assets/o_swing1.png and b/assets/o_swing1.png differ diff --git a/assets/o_swing2.png b/assets/o_swing2.png index 6a3a5f4..cfd523d 100644 Binary files a/assets/o_swing2.png and b/assets/o_swing2.png differ diff --git a/assets/o_toss.png b/assets/o_toss.png index 4094a8d..a160c54 100644 Binary files a/assets/o_toss.png and b/assets/o_toss.png differ diff --git a/config.js b/config.js index 6e26cd4..664ee85 100644 --- a/config.js +++ b/config.js @@ -372,7 +372,11 @@ export const POSE_TIME = 0.38; // Measured off the sprite, not guessed: the player is RIGHT-handed, so he pats the ball with his // LEFT hand — the left-hand side of the image — whose palm sits ~0.69 m above his feet and ~0.36 m // off his centre line. The ball tops out just under that hand instead of beside it. -export const BOUNCE_IDLE = { dx: 0.36, apex: 0.68, tempo: 3.6 }; +// dx/apex are per-man because the two sprites reach differently, and a single pair put the +// opponent's ball out by his racket instead of under his palm. Both are FEEL KNOBS measured off the +// drawn palm by eye — a skin-detector guess at the hand lands on a knee, so tune these by looking. +export const BOUNCE_IDLE = { dx: 0.36, apex: 0.68, tempo: 3.6, // the player, drawn from behind + oDx: 0.45, oApex: 0.55 }; // the opponent, drawn from the front // generated sprite sheets: the idle pose is this many pixels tall; all scales derive from it export const SPRITE_H = 224; // 2x remaster (was 112). RES is 3, so the player at the baseline diff --git a/index.html b/index.html index 2afed4f..a00b11c 100644 --- a/index.html +++ b/index.html @@ -117,6 +117,6 @@