From 25a82c5be29146ed39452a329f0315a8ca891643 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 21 Jun 2026 01:43:13 +0000 Subject: [PATCH] examples: apply the transition-card lesson to the slingshot (+ demo cleanup) Reviewed the other demo stacks against the lessons codified since they were written. The one clear lesson-gap was the slingshot: it had the platformer's "visible build seam" (and worse - it hid its overlay before tearing down), so every level change dismantled the old tower in view. - Slingshot: add a dedicated opaque transition cover (sgCard + sgCardText, kSgUIVersion 1->2) shown BEFORE each rebuild (all four transition callers: retry, next-level, win-replay, the 1-3 level jumps) and faded out after via a generation-guarded send-driven blendLevel ramp - the slingshot twin of the platformer's pfCardShow/pfCardReveal, minus the illustrated art. Kept separate from the translucent sgShade banner; the initial openCard build is left to the menu (a gSgCardActive gate). - Demo: remove the dead newStaticBar factory (defined, never called). The other stacks needed no change, and two agent-flagged items were false positives I verified and dropped: the contraption banner "10 Hz redundant relayout" actually shows 0.1s tenths (genuinely 10 Hz), and the demo's newPoly "wrong handle" already returns the control ref. The contraption per-frame loop is otherwise playbook-clean; spike-gamekit is self-declared throwaway scaffolding whose per-frame costs are intentional. Example-side only; no Kit touch, embedded Kit in sync. Static gates clean. The slingshot cover needs an OXT pass to confirm + tune the hold/fade timing. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01HkN9P6YodA65ZuhDDuuQks --- CHANGELOG.md | 21 +++++ examples/box2dxt-demo.livecodescript | 17 ---- examples/box2dxt-slingshot.livecodescript | 110 ++++++++++++++++++++-- 3 files changed, 121 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf5ba61..ab01474 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,27 @@ The native shim's ABI is tracked separately by `b2Version()` (currently `4`). ### Added +- **Slingshot: a transition COVER between levels (carrying the platformer's + transition-card lesson across).** A review of the other demo stacks found the + slingshot had the same "visible build seam" the platformer just fixed - and worse: + every level change ran `b2kClear`/`b2kTeardown`/`sgWipeStage` *on screen* before + `lock screen`, and each transition path *hid* its translucent banner overlay right + before tearing down, so the player watched the old tower dismantle against a bare + card. Added a dedicated **opaque cover** (`sgCard` + `sgCardText`, built once in + `buildSgUI`, `kSgUIVersion` 1 -> 2) raised above everything: `sgCardShow` covers + with a "LEVEL n / 3" title *before* the teardown (in all four transition callers - + retry, next-level, win-replay, the 1-3 level jumps), the rebuild paints behind it, + and `sgCardReveal` holds a beat then fades it out via a generation-guarded + `send`-driven `blendLevel` ramp - the slingshot twin of the platformer's + `pfCardShow`/`pfCardReveal`, minus the illustrated art (a plain tinted cover is + enough). Kept separate from the translucent `sgShade` banner so neither juggles the + other's blendLevel; the initial `openCard` build is left to the menu (a + `gSgCardActive` gate). Example-side only; gates clean. **Needs an OXT pass** to + confirm the cover masks the rebuild and to tune the hold/fade timing. The same + review also **removed the dead `newStaticBar` factory** from the demo (defined, + never called) and confirmed the other stacks need no change: the contraption builder + is already playbook-clean, and spike-gamekit is intentional throwaway scaffolding. + - **Platformer: ILLUSTRATED biome cards (real game art on the transition card).** The level cards are no longer a solid tint + sparse text: each is now a little composed scene built from the already-loaded atlases (zero new asset files). Behind the title diff --git a/examples/box2dxt-demo.livecodescript b/examples/box2dxt-demo.livecodescript index 0eb0111..576a3cc 100644 --- a/examples/box2dxt-demo.livecodescript +++ b/examples/box2dxt-demo.livecodescript @@ -6336,23 +6336,6 @@ function newPoly pX, pY, pR, pColor return tRef end newPoly -function newStaticBar pX, pY, pW, pH, pColor - local tName, tRef - put "demo_static_" & the milliseconds & "_" & random(99999) into tName - create graphic tName - put the long id of graphic tName into tRef - set the style of tRef to "rectangle" - set the filled of tRef to true - set the backgroundColor of tRef to pColor - set the foregroundColor of tRef to "20,20,24" - set the lineSize of tRef to 1 - set the width of tRef to pW - set the height of tRef to pH - set the loc of tRef to round(pX) & "," & round(pY) - b2kAddStatic tRef - return tRef -end newStaticBar - -- ===================================================================== -- UI -- ===================================================================== diff --git a/examples/box2dxt-slingshot.livecodescript b/examples/box2dxt-slingshot.livecodescript index 6811c2f..137050a 100644 --- a/examples/box2dxt-slingshot.livecodescript +++ b/examples/box2dxt-slingshot.livecodescript @@ -74,8 +74,15 @@ local gBlkN, gBlk, gBlkX, gBlkY, gBlkScored -- blocks + build spots local gRingUntil -- pop-ring pool timers (0 = ring free) local gDotsOn -- aim dots currently shown (skip redundant sets) local gHudLast, gHudNextMS, gBuilding - -constant kSgUIVersion = "1" +-- the TRANSITION COVER: an opaque card (sgCard) + a title (sgCardText) that hides +-- each level teardown/rebuild so the player never watches a tower dismantle, then +-- fades out. The platformer's pfCardShade/pfCardText pattern, minus the illustrated +-- art - a plain tinted cover is enough here. gSgCardActive gates the reveal so the +-- initial openCard build (which the menu covers) is left alone; gSgCardGen +-- invalidates a stale fade send; gSgCardFade is the ramp (0 opaque .. 100 clear). +local gSgCardActive, gSgCardGen, gSgCardFade + +constant kSgUIVersion = "2" -- added the transition cover (sgCard / sgCardText) constant kPocketX = 170 -- the sling pocket (screen px) constant kPocketY = 444 constant kPullMax = 96 -- tether radius: full pull = full power @@ -111,6 +118,8 @@ command buildSgUI if there is a field "sgHud" then delete field "sgHud" if there is a graphic "sgShade" then delete graphic "sgShade" if there is a field "sgBigText" then delete field "sgBigText" + if there is a graphic "sgCard" then delete graphic "sgCard" + if there is a field "sgCardText" then delete field "sgCardText" set the width of this stack to 1024 set the height of this stack to 640 set the loc of this stack to the screenLoc @@ -152,6 +161,27 @@ command buildSgUI set the opaque of it to false set the showBorder of it to false set the textColor of it to "245,245,250" + -- the transition COVER (opaque) + its title - hidden; shown by sgCardShow before + -- each rebuild, faded out by sgCardReveal. Distinct from the translucent sgShade + -- banner so neither has to juggle the other's blendLevel. + create graphic "sgCard" + set the style of it to "rectangle" + set the rect of it to 0, 0, 1024, 640 + set the filled of it to true + set the backgroundColor of it to "20,24,36" + set the blendLevel of it to 0 + set the visible of it to false + create field "sgCardText" + set the rect of it to 112, 250, 912, 392 + set the lockText of it to true + set the traversalOn of it to false + set the textAlign of it to "center" + set the textSize of it to 30 + set the textStyle of it to "bold" + set the opaque of it to false + set the showBorder of it to false + set the textColor of it to "245,245,250" + set the visible of it to false set the uSgUIVersionTag of this stack to kSgUIVersion end buildSgUI @@ -163,8 +193,69 @@ command sgChromeFront if there is a field "sgTitle" then set the layer of field "sgTitle" to tN if there is a field "sgHelp" then set the layer of field "sgHelp" to tN if there is a field "sgHud" then set the layer of field "sgHud" to tN + -- the transition cover sits ABOVE all other chrome while it is covering: the + -- build just created controls over it, so re-raise it here (last) to keep it on + -- top until the reveal fade hides it. Skipped when it is hidden. + if there is a graphic "sgCard" and the visible of graphic "sgCard" is true then + set the layer of graphic "sgCard" to tN + if there is a field "sgCardText" then set the layer of field "sgCardText" to tN + end if end sgChromeFront +-- ===== the TRANSITION COVER (the platformer's transition-card lesson) ===== +-- A level title for the cover. +function sgLevelTitle pLevel + return "L E V E L " & pLevel & " / 3" +end sgLevelTitle + +-- COVER the screen NOW, before a rebuild: hide the translucent banner, raise the +-- opaque card + title above everything, show it, and force one repaint so it is on +-- screen before the teardown/build that follows paints underneath it. +command sgCardShow pTitle + local tTop + add 1 to gSgCardGen -- invalidate any in-flight fade ramp + put true into gSgCardActive + if there is a graphic "sgShade" then set the visible of graphic "sgShade" to false + if there is a field "sgBigText" then set the visible of field "sgBigText" to false + if there is no graphic "sgCard" then exit sgCardShow + set the blendLevel of graphic "sgCard" to 0 + if there is a field "sgCardText" then + set the text of field "sgCardText" to pTitle + set the blendLevel of field "sgCardText" to 0 + end if + put the number of controls of this card into tTop + set the layer of graphic "sgCard" to tTop + if there is a field "sgCardText" then set the layer of field "sgCardText" to tTop + lock screen + set the visible of graphic "sgCard" to true + if there is a field "sgCardText" then set the visible of field "sgCardText" to true + unlock screen +end sgCardShow + +-- REVEAL the built level: hold the opaque card a beat, then fade it out via a +-- generation-guarded self-send (so a fresh cover invalidates a stale ramp). +command sgCardReveal + add 1 to gSgCardGen + put 0 into gSgCardFade + send ("sgCardFadeStep" && gSgCardGen) to me in 600 milliseconds +end sgCardReveal + +command sgCardFadeStep pGen + if pGen is not gSgCardGen then exit sgCardFadeStep + if there is no graphic "sgCard" then exit sgCardFadeStep + add 12 to gSgCardFade + if gSgCardFade >= 100 then + set the visible of graphic "sgCard" to false + if there is a field "sgCardText" then set the visible of field "sgCardText" to false + set the blendLevel of graphic "sgCard" to 0 -- reset for the next cover + put false into gSgCardActive + exit sgCardFadeStep + end if + set the blendLevel of graphic "sgCard" to gSgCardFade + if there is a field "sgCardText" then set the blendLevel of field "sgCardText" to gSgCardFade + send ("sgCardFadeStep" && gSgCardGen) to me in 45 milliseconds +end sgCardFadeStep + -- ===================================================================== -- The state machine: menu -> play -> (clear ->) ... -> won -> (again) -- ===================================================================== @@ -452,6 +543,9 @@ command sgBuildLevel pLevel b2kStart end if put false into gBuilding + -- fade the cover off the freshly-built level (only when a transition raised it; + -- the initial openCard build has no cover - the menu takes over there) + if gSgCardActive is true then sgCardReveal end sgBuildLevel -- Remove every level control (all are "sg_" prefixed; chrome is "sgX"). @@ -548,8 +642,7 @@ end sgShotDone command sgRetry put gScoreAt into gScore -- the level never happened - set the visible of graphic "sgShade" to false - set the visible of field "sgBigText" to false + sgCardShow sgLevelTitle(gLevel) sgBuildLevel gLevel put "play" into gMode end sgRetry @@ -607,8 +700,7 @@ command sgNextLevel sgShowWin exit sgNextLevel end if - set the visible of graphic "sgShade" to false - set the visible of field "sgBigText" to false + sgCardShow sgLevelTitle(gLevel + 1) sgBuildLevel gLevel + 1 put "play" into gMode end sgNextLevel @@ -734,8 +826,7 @@ on mouseUp end if if gMode is "won" then put 0 into gScore - set the visible of graphic "sgShade" to false - set the visible of field "sgBigText" to false + sgCardShow sgLevelTitle(1) sgBuildLevel 1 put "play" into gMode exit mouseUp @@ -755,8 +846,7 @@ on rawKeyDown pKeyCode exit rawKeyDown end if if pKeyCode >= 49 and pKeyCode <= 51 and gMode is not "menu" then - set the visible of graphic "sgShade" to false - set the visible of field "sgBigText" to false + sgCardShow sgLevelTitle(pKeyCode - 48) sgBuildLevel pKeyCode - 48 put "play" into gMode exit rawKeyDown