From cabcebc4c182baed783913e8ee64ca3115790c32 Mon Sep 17 00:00:00 2001 From: elham-saboori Date: Wed, 28 Oct 2026 14:47:36 +0000 Subject: [PATCH] chore: publish 2 skills (craft-game-floors, ui-ugui) --- skills/craft-game-floors/SECURITY.md | 23 +++ skills/craft-game-floors/SKILL.md | 134 ++++++++++++++++++ .../references/art-direction.md | 85 +++++++++++ .../references/camera-rigs.md | 51 +++++++ .../references/character-motion.md | 35 +++++ .../references/game-audio.md | 47 ++++++ .../craft-game-floors/references/game-feel.md | 68 +++++++++ .../references/game-shell.md | 45 ++++++ .../references/live-floor.md | 35 +++++ .../references/placeholder-assets.md | 35 +++++ .../references/scene-lighting.md | 52 +++++++ .../references/toon-shading.md | 34 +++++ skills/craft-game-floors/references/vfx.md | 40 ++++++ .../references/visual-scorecard.md | 43 ++++++ skills/ui-ugui/SKILL.md | 2 +- 15 files changed, 728 insertions(+), 1 deletion(-) create mode 100644 skills/craft-game-floors/SECURITY.md create mode 100644 skills/craft-game-floors/SKILL.md create mode 100644 skills/craft-game-floors/references/art-direction.md create mode 100644 skills/craft-game-floors/references/camera-rigs.md create mode 100644 skills/craft-game-floors/references/character-motion.md create mode 100644 skills/craft-game-floors/references/game-audio.md create mode 100644 skills/craft-game-floors/references/game-feel.md create mode 100644 skills/craft-game-floors/references/game-shell.md create mode 100644 skills/craft-game-floors/references/live-floor.md create mode 100644 skills/craft-game-floors/references/placeholder-assets.md create mode 100644 skills/craft-game-floors/references/scene-lighting.md create mode 100644 skills/craft-game-floors/references/toon-shading.md create mode 100644 skills/craft-game-floors/references/vfx.md create mode 100644 skills/craft-game-floors/references/visual-scorecard.md diff --git a/skills/craft-game-floors/SECURITY.md b/skills/craft-game-floors/SECURITY.md new file mode 100644 index 0000000..a60c588 --- /dev/null +++ b/skills/craft-game-floors/SECURITY.md @@ -0,0 +1,23 @@ +# Security notes: craft-game-floors + +This skill has the agent run C# inside the user's open Unity Editor (applying the craft floors: +materials, lighting, UI, audio, captures) and, for the live floor, wire Unity Gaming Services +calls in the user's own project. Automated skill scanners flag these as powerful capabilities. +They are intentional, and limited by the safeguards below. + +## Accepted risks + +| Risk | Capability | Why it is accepted | +|---|---|---| +| `SEC_POWER_CAP` | Runs C# in the user's open Editor | It only reaches the Editor the user already has open, on their own machine, as that user, behind the Pipeline capability gate. No code fetched from a remote source is run — the agent runs C# written from this skill's own references, which also require a restore point before project changes. | +| `SEC_CLOUD` | Wires Cloud Save / Leaderboards / Remote Config client calls (the live floor) | Only against the project's own cloud id, only when a live identity probe succeeds, and declining any floor is one word. Provisioning that needs credentials or the Editor UI is named as a human step, never automated. | + +## Mitigations + +- **Restore point first.** The skill requires a rollback point (a clean commit, or one it creates) + before changing the project. +- **Floors are declinable.** Every floor reverses on a word; declines are recorded, not argued. +- **Only the user's own Editor and project.** Everything runs on this machine as this user. +- **No remote code.** All executed C# comes from this skill's references. +- **Mechanics route to their owners.** Mixer routing, UI construction, backend integration, and + import settings go to their dedicated skills rather than being improvised here. diff --git a/skills/craft-game-floors/SKILL.md b/skills/craft-game-floors/SKILL.md new file mode 100644 index 0000000..d6d41aa --- /dev/null +++ b/skills/craft-game-floors/SKILL.md @@ -0,0 +1,134 @@ +--- +name: craft-game-floors +description: "Makes a Unity game look and feel finished: art style, lighting, juice, sound, menus, saves and look scoring. Use when building a new game's first playable version, or when a game looks like programmer art, feels flat or silent, or needs polish." +allowed-tools: + - Bash + - Read + - Write + - Edit +--- + +# Craft Game Floors + +A functional game that looks and feels like programmer art reads as a failure to the person who +asked for it. This skill is the craft layer: **floors** — disciplines where every game needs a +minimum and the engine's defaults are unacceptable — each carried by a reference below with +opinionated values, worked examples to imitate, and a verify step. The floors are the heart of the +**paved** path (Unity's default scaffolding for agent-built games: assumptions declared up front, +reversible on a word); in this skill, "paved" simply means these floors are always on unless the +user declines one. + +**Before starting any floor or diagnosis, open and read the matching reference below and the Unity +docs pages it links** — your engine knowledge may be out of date, wrong, or for a different +version. Name the files and pages you read in your report. **Do only what's asked. Don't change +unrelated assets or files. Avoid long explanations.** + +Two ways in, same floors: + +- **During project creation** — the `new-unity-project` skill loads this one and carries the + floors through the build in the order below. +- **Mid-project, on any craft ask** ("fix the look", "add sound", "it feels flat", "make a title + screen", "score it") — jump straight to the matching reference, after the mid-flow check below. + +**Scope by the ask**: a request that authorizes changes ("fix", "add", "polish") gets the floor +applied; an assessment-only ask ("score it", "what should I fix?") gets the scored board and +recommendations, with no project modifications until the user says go. + +## Entering mid-flow + +The references assume an **art bible** (style statement + fixed palette) and, where audio meets +UI, the **mixer handshake** (exposed params `MusicVol`/`SFXVol`). On an existing project, run +these in order before the asked-for floor: + +1. **Restore point** — confirm a clean commit to roll back to, or create one, before any change. +2. **Render pipeline** — read `GraphicsSettings.currentRenderPipeline`: the look floors' verifies + are written for URP; on Built-in or HDRP, apply the opinions, name the pipeline gap, and skip + the URP-typed checks. +3. **Art bible** — look for a `Palette` type or an art-bible note (CLAUDE.md, docs); if none + exists, write the three-line bible from the game's current look first — retro-tinting later is + the expensive order. +4. **Identity (live floor only)** — verify player identity with a live probe (services + initialized and a PlayerId present), never by assuming an earlier setup step ran. +5. **Scope the done gate** to the surface you touched: a title-screen session scores the shell + and look axes, not the whole board. + +**A floor fills an engine default, never a developer's choice.** If the project already has its +own controller, camera, shader, palette or style, keep it, apply only the floor's opinions that +don't conflict, and name the difference in the report. Write an art bible only from choices the +developer has made, and ask before adding one mid-project. + +## The floors — numbered in build order, read each at its moment + +1. [art-direction](references/art-direction.md) — before the first material, light, or UI element +2. [scene-lighting](references/scene-lighting.md) — with the visual baseline (3D) +3. [toon-shading](references/toon-shading.md) — when the art bible picks a cel/toon style (3D) +4. [camera-rigs](references/camera-rigs.md) — first gameplay camera (has a 2D section) +5. [placeholder-assets](references/placeholder-assets.md) — first missing asset +6. [character-motion](references/character-motion.md) — first moving character (3D-leaning) +7. *(the playable loop lands here — loop before content, one system per pass)* +8. [game-feel](references/game-feel.md) — the feel pass, after the loop +9. [vfx](references/vfx.md) — with the feel pass (has a 2D section) +10. [game-audio](references/game-audio.md) — before done +11. [game-shell](references/game-shell.md) — before done (2D backdrop delta). Before the first TextMeshPro text, import TMP Essentials (see the reference's failure table). +12. [live-floor](references/live-floor.md) — with the shell (services before gameplay code) +13. [visual-scorecard](references/visual-scorecard.md) — **the done gate, always last; run it once a playable loop exists.** During project creation, stop at the lit-baseline capture and skip the gate. + +Mid-project asks enter at the matching number and pull earlier floors only as the mid-flow check +requires (the bible, the pipeline, the identity probe). + +**Resolving `` in the references' doc links** — prefer WebFetch of the exact page over +WebSearch: for `docs.unity.com/en-us/engine/` manual and script-reference pages, use the +project's Editor stream (e.g. `6000.3`) from `ProjectSettings/ProjectVersion.txt`, and the same +stream for the remaining `docs.unity3d.com//Documentation` pages; for +`docs.unity3d.com/Packages/@` pages, use the installed package's major.minor +from `Packages/manifest.json` or the `Library/PackageCache` folder name; the +`docs.unity.com/en-us/.md` services pages are unversioned. + +Routing follows the game's 2D/3D nature; **the 2D look floor (sprite art direction, 2D lights, +sprite placeholders) is not yet covered** — on a 2D game, say so at those beats rather than +applying 3D instructions. The scorecard's 2D anchor set still applies. + +## The boundary (what this skill is not) + +This skill owns **opinionated creation and floors** — which values, in what order, to what bar. +Catalog neighbours own **mechanics on existing setups**: volume/post mechanics → +`urp-postprocessing` · routing sources into existing mixer groups → `audio-setup-mixers` · audio +import/memory → `optimize-audio` · UI framework choice and construction → `ui` · font assets → +`optimize-text-mesh-pro` · backend services → `build-live-game` · pixel-perfect 2D → +`2d-pixel-perfect`. When an ask is purely mechanical, route there; when it's "make it good," it's +this skill, which then delegates the mechanics. If this index wants to grow, the new thing is a +catalog skill routed by the prompt, not another floor — a floor must apply to *every* game. + +## The done gate + +A craft session ends with [visual-scorecard](references/visual-scorecard.md)'s board — scored +frames, not a green compile. In order: + +1. **Score** all six axes per the scorecard. All six at 3 or better is the floor. +2. **Fix pass** — anything below 3 gets its fix before done (assessment-only asks: scores and + recommendations, no fixes). If a fix doesn't work, reread the relevant reference and its + linked documentation before trying variations. +3. **Console** — confirm zero console errors. +4. **Play check** — enter Play mode and check the touched behavior with `Debug.Log` lines you + added for the purpose. If the fix pass or this check fails, reread the relevant reference and + its linked documentation before trying variations. + +## Final report + +State: which floors ran and which references were read · the board (or the scoped axes) before and +after the fix pass · every incidental side effect — packages installed, files or settings edited, +assets imported — and any follow-up the user needs · any floor skipped or gap named (the 2D look +floor, a declined install, a missing neighbour skill), so the user knows what deeper guidance was +skipped. + +## Common mistakes + +- **Polishing without a bible** — fixing one object's look against no palette; write the + three-line bible first, even mid-project. +- **Replacing a developer's choice with a floor recipe** — an existing custom controller, + camera, shader or palette stays; the fix pass never swaps it for the floor's default. +- **Treating a floor as a feature** — floors are defaults; the user declines one with a word, and + the decline is recorded in the report, never argued. +- **Running the mechanics from memory** when a neighbour skill owns them — route and delegate. +- **Skipping the done gate** because the change was small — scope it down instead. +- **Applying 3D look instructions to a 2D game** — name the gap instead. diff --git a/skills/craft-game-floors/references/art-direction.md b/skills/craft-game-floors/references/art-direction.md new file mode 100644 index 0000000..0eb4cbe --- /dev/null +++ b/skills/craft-game-floors/references/art-direction.md @@ -0,0 +1,85 @@ +# Art Direction + +> Craft reference for the `craft-game-floors` skill. **Load:** before the first material, light, or UI element exists — in the creation flow, that is the start of the build (the paved default path); in an ad-hoc session, load it first whenever no art bible or `Palette` source exists yet, because every other floor depends on this one. + +## The art bible — three lines, written first + +Before the first material, write and print: + +1. **Style statement** — one sentence ("Cel-shaded storybook interior: soft toon bands, chunky outlines, warm afternoon light"). +2. **Palette** — 5–6 hex values + one accent, each named for its job (floor, walls, character-primary, character-secondary, props, shadow, accent/XP). Derive from the prompt's mood; keep saturation in one family. +3. **Mood** — the light source story ("late sun through windows, cool shadow bounce"). + +## Three worked bibles — imitate, then vary + +Copy the closest one and re-derive the hexes from the prompt's own nouns; never ship these verbatim twice. + +**Hearthside** (cozy interior) + +- Style: "Cel-shaded storybook café: soft toon bands, chunky outlines, everything slightly rounded." +- Palette: floor `#8A6F4D` honeyed oak · walls `#E8D9BE` cream plaster · character-primary `#C96F4A` terracotta · character-secondary `#F4E7CE` oat · props `#5E7B5A` sage · shadow `#6B7A8F` cool slate · accent `#F2B134` amber. +- Mood: late sun through west windows; cool blue-gray shadow bounce; lamps warm the corners. + +**Neon Ricochet** (bright arcade) + +- Style: "Flat-shaded neon arcade: saturated shapes on near-black courts; everything that scores, glows." +- Palette: floor `#141A2E` ink navy · bounds `#253057` midnight · character-primary `#19D3F0` electric cyan · character-secondary `#F037A5` hot magenta · props `#7A5CF0` violet · shadow `#0B0F1E` abyss · accent `#F7E04B` laser yellow. +- Mood: no sun — the pieces are the light source; dim cool key, emissives and glow carry the frame. + +**Derelict Signal** (moody sci-fi) + +- Style: "Grounded sci-fi interior: desaturated steel broken by one signal color, hard light through grates." +- Palette: floor `#3A3F46` deck gunmetal · walls `#2A2E35` hull plate · character-primary `#C8CCD4` worn ceramic · character-secondary `#5A6472` slate · props `#4E5A50` olive drab · shadow `#1C2026` void blue · accent `#FF6A2B` signal orange. +- Mood: one hard cool key through ceiling grates; the accent reads as emergency lighting; shadows stay deep but blue, never black. + +## Palette as the single source + +Author one `Palette.cs` (static Color fields from the hex values) or a ScriptableObject. **Every** color in the project reads from it: materials, fog, light tints, particles, UI, text. The test is absolute: `Color.gray`, default Lit material, and default skybox never appear in a capture. + +## Style is a system, not a shader on the hero + +Whatever the style is, it covers *everything in frame*: character, walls, props, floor, sky, UI. The proven failure mode is a cel-shaded character in a default-lit room — the game reads as broken exactly at the seam. When the bible picks a toon pass, the pass applies frame-wide. + +## Primitive characters that still read + +Agent-built characters are assembled from primitives. Rules that make them charming instead of sad: + +- **Silhouette first** — the character must be identifiable filled flat black. Exaggerate the identifying features (ears, tail, snout) ~30% past realistic. +- **Taper and tilt** — scale limb primitives asymmetrically (0.8× at the far end) and tilt joints a few degrees; nothing organic is axis-aligned. +- **Two-tone minimum** — body primary + belly/face secondary from the palette, plus dark accents (paw tips, ear interiors). +- **Nothing is statuary** — an idle character breathes: a 2–3 line sine bob on the body and a slow tail sway. Cheap, transformative. + +## Worked character: Scout, the fox courier + +A complete build to imitate. Parent chest, head, legs, and tail to the body; snout, nose, and ears to the head. Scales are local (x, y, z) against a body ~1 unit long; rotations in degrees; palette roles come from the active bible. + +| # | Part | Primitive | Scale | Placement and tilt | Palette role | +|---|---|---|---|---|---| +| 1 | Body (root) | Sphere | 0.50, 0.42, 0.62 | egg lying along +Z | character-primary | +| 2 | Chest | Sphere | 0.34, 0.30, 0.30 | embedded front-under of body | character-secondary | +| 3 | Head | Sphere | 0.32, 0.30, 0.32 | above body front; pitch down 6 | character-primary | +| 4 | Snout | Sphere | 0.14, 0.10, 0.20 | head front; pitch up 4 | character-secondary | +| 5 | Nose | Cube | 0.05 uniform | snout tip; yaw 45 (diamond) | shadow | +| 6 | Ears ×2 | Cube | 0.10, 0.18, 0.04 | head top; roll ±12 outward | primary; inner face shadow | +| 7 | Legs ×4 | Capsule | 0.09, 0.16, 0.09 | under body corners; tilt 3–5 off vertical | character-primary | +| 8 | Paw tips ×4 | Sphere | 0.072 uniform | leg ends (the 0.8× taper) | shadow | +| 9 | Tail | Capsule | 0.12, 0.30, 0.12 | body rear; pitch back 55 | character-primary | +| 10 | Tail tip | Sphere | 0.10 uniform | tail end | character-secondary | + +Idle: sine-bob the body ±2% of its height at ~0.6 Hz; sway the tail ±8° at ~0.3 Hz. The 0.18-tall ears are the ~30% silhouette exaggeration — keep it. + +## Environment floor + +No bare planes. Every room gets: floor material distinct from walls, baseboard/trim geometry at wall joins, 2–3 props scaled against the character, and one light-story element (window, lamp). + +## Verify + +Machine check first, capture second: + +- Eval (via `unity-cli`): walk every `Renderer` and assert no `sharedMaterial` named `Default-Material`, and that `RenderSettings.skybox` is a palette-derived material, not the template sky. Any hit names its object. **Scope the fixes by the ask**: hits on the surface the request authorized get fixed before capturing (a full-look pass authorizes everything; a title-screen ask authorizes the title backdrop); hits outside that scope are never edited uninvited — report them as follow-up recommendations instead. +- Grep the project scripts for literal `Color.` constants outside the `Palette` source; a hit means the single source is being bypassed. +- Capture the character beside a doorway: scale and palette read together. A gray or default-lit surface in the frame fails the bible, not the capture. + +## Expansion scope + +Reference palettes per genre/mood beyond these three, texture and detail passes, integration with generated 2D/3D assets, style-transfer onto imported models, per-platform color calibration. diff --git a/skills/craft-game-floors/references/camera-rigs.md b/skills/craft-game-floors/references/camera-rigs.md new file mode 100644 index 0000000..b37208c --- /dev/null +++ b/skills/craft-game-floors/references/camera-rigs.md @@ -0,0 +1,51 @@ +# Camera Rigs + +> Craft reference for the `craft-game-floors` skill. Relative links in this file resolve to the skill directory. **Load:** whenever a gameplay camera is needed — never hand-roll one. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The opinion + +The camera is half of game feel and the first thing a hand-rolled implementation gets wrong (a hand-rolled follow camera has shipped pointing at the outside of the house it was meant to film). Builds under these floors use **Cinemachine** (`com.unity.cinemachine` — on the paved package list, this skill's default install set, for any gameplay camera, 2D rigs included; install it if absent) and one of the recipes below. A hard-parented or static camera never ships. + +**On new installs, take the current major — never 2.x.** On Unity 6 editors Cinemachine ships as a core package whose major the editor picks; where a version can be pinned, pin the 3.x line or newer (e.g. `com.unity.cinemachine@3.1`). 2.x is a different API surface (`CinemachineVirtualCamera` / `CinemachineCollider` vs the `CinemachineCamera` / `CinemachineDeoccluder` family from 3.x on), and package-install examples elsewhere still pin 2.x versions — every recipe here is written against the 3.x-family types and will not compile on 2.x. + +**Existing projects: check the installed major first** (`Packages/manifest.json`), before any package change. A project already on Cinemachine **2.x** keeps it — upgrading the package breaks existing camera scripts across the API break, and a restore point recovers but does not make them compatible. On 2.x, apply this file's *opinions* (framing, damping, dead zones, FOV, shake-via-impulse) through the 2.x equivalents (`CinemachineVirtualCamera`, its Body/Aim stages, `CinemachineImpulseSource`), skip the 3.x-typed recipes and verifies, and name the version gap in the report. Migrating 2.x → 3.x is never a side effect of a craft fix: it happens only as an explicitly scoped ask, agreed with the user, following the [Cinemachine upgrade guide](https://docs.unity3d.com/Packages/com.unity.cinemachine@/manual/CinemachineUpgradeFrom2.html) and verified end-to-end afterward. + +## Third-person follow (the 3D default) + +[`CinemachineCamera`](https://docs.unity3d.com/Packages/com.unity.cinemachine@/manual/CinemachineCamera.html) with: + +- **Third Person Follow** body — camera distance ~4–6 for interiors, shoulder offset (~0.5 right, ~1.2 up) so the character sits at the lower-third intersection, not dead center. +- **Damping** ~0.5–1.0 s on position — the camera *settles*, never teleports. +- **The rig inherits the follow target's yaw.** On a character that turns toward its movement, the camera whips to face whatever the character faces — hedges, walls, the void. Raise rotation damping, or follow a yaw-damped proxy transform instead of the character root. +- **[Deoccluder](https://docs.unity3d.com/Packages/com.unity.cinemachine@/manual/CinemachineDeoccluder.html) — interiors default OFF.** The interior recipe: skip the deoccluder, frame high enough that walls can't eat the lens; add one only for exteriors/orbit cams. Its tuning knobs go by different names across CM3 minor versions — verify the fields on the *installed* version before touching them — and its failure modes are in the table below. +- **FOV 50–60** for interiors (default 40 feels like a telescope indoors; 90 feels like a fisheye). +- Slight **look-ahead** in movement direction when the game is about exploring toward things. + +## Orbit / inspect + +[`CinemachineOrbitalFollow`](https://docs.unity3d.com/Packages/com.unity.cinemachine@/manual/CinemachineOrbitalFollow.html) with three-ring setup for player-rotatable cameras; recenter after ~2 s idle. Use for creature/vehicle games where the subject is the point. + +## 2D follow + +`CinemachineCamera` + **Position Composer**: dead zone ~0.1×0.1 (breathing room for small motion), look-ahead 0.5–1 s along velocity, damping asymmetric (loose horizontal, tight vertical for platformers). Pixel-art projects: pair with `2d-pixel-perfect`. + +## Feel hooks + +Screenshake and impact kicks route through **Cinemachine Impulse** (source on the event, listener on the camera) — the feel pass owns when they fire and how hard. Never move the camera transform directly for shake. + +## Failure modes (deoccluder) + +| Symptom | Root cause | Fix | +|---|---|---| +| interior camera collapses onto the character (a configured 4.2 m distance measured at 0.7 m) | default deoccluder settings resolving against room geometry | disable the deoccluder indoors; reframe higher | +| Editor stack overflow in Play mode (seen on 6000.4–6000.6) | the deoccluder's obstacle resolution recursing when an obstacle crosses the camera–target line | remove or disable the deoccluder on that camera | +| deoccluder silently does nothing | Group Framing active on the same camera | pick one — they don't compose | +| camera flips on slopes/loops | the deoccluder resolves against world-up while the target's local up differs | disable it for that rig | + +## Verify + +Capture during movement (via `unity-cli`): player on the lower-third line, no wall in front of the lens, horizon level. Then the measured check, by eval rather than eyeball: read the rig's configured camera distance off the Third Person Follow component and compare it to the live `(camera.transform.position − target.position).magnitude` — if the live value is under ~80% of the configured one, something is fighting the rig; disable the deoccluder first and re-measure. Walk the player into a corner and repeat both the capture and the distance read. + +## Expansion scope + +State-driven camera sets (gameplay/menu/photo), blend tuning, target groups for multiplayer framing (note: the deoccluder and Group Framing don't compose), split-screen, cutscene dolly tracks, mobile gyro/touch orbit. diff --git a/skills/craft-game-floors/references/character-motion.md b/skills/craft-game-floors/references/character-motion.md new file mode 100644 index 0000000..7c6137f --- /dev/null +++ b/skills/craft-game-floors/references/character-motion.md @@ -0,0 +1,35 @@ +# Character Motion + +> Craft reference for the `craft-game-floors` skill. Relative links in this file resolve to the skill directory. **Load:** when a character glides instead of walks, freezes instead of idles, or was assembled from primitives with no animation source. [art-direction](art-direction.md) owns silhouette and proportion; this reference makes the character *move* like a character. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The opinion + +A character that translates across the floor in a fixed pose reads as a chess piece. Agent-built characters rarely have rigs — so the paved answer (this skill's default, rig or no rig) is **procedural motion**: a handful of sine waves and springs, driven by velocity, layered on the primitive hierarchy. It is cheap, tunable live via `unity-cli` eval, and the single biggest step from "tech demo" to "game". + +## The layers (one ~80-line component) + +Drive everything from normalized speed (`speed / maxSpeed`) so it scales: + +- **Gait bob** — body y-offset: `sin(t · stepFreq · 2π) · bobAmp · speedN`, stepFreq ~2.5 Hz at full speed. Add a half-frequency roll (z-rotation, ~2°) so weight shifts side to side. +- **Limb swing** — legs/arms rotate on x via the same sine, opposite limbs phase-shifted by π. Even cylinders read as walking when they alternate. +- **Lean** — pitch the body into acceleration (~8° at full accel) and bank into turns (~5°). Compute from velocity delta; damp it. +- **Squash & stretch** — jump: stretch y 1.1 on rise; land: squash y 0.85 / xz 1.1 for 0.1 s, ease out with the feel pass's Ease helper. +- **Secondary motion** — tail, ears, antennae follow with delay: each segment lags its parent's rotation by 0.05–0.1 s (a one-line exponential damp is enough). This is where charm lives. +- **Head look** — head yaws toward movement direction or the nearest point of interest, clamped ~60°, damped. +- **Settle, never freeze** — on stop, blend gait amplitude to zero over 0.3 s and hand off to the idle breathe/sway (from [art-direction](art-direction.md)); a hard stop at full pose is the giveaway. + +## Physics note (paid for live, docs-confirmed) + +The [collider interaction rules](https://docs.unity.com/en-us/engine//manual/physics-section/physics-overview/collision-section/collider-interactions/create-trigger.md) govern when trigger events fire; the trap this floor pays for: a NavMesh-driven character with its CharacterController disabled has **no collider at all**, so pickups' `OnTriggerEnter` silently never fires. Keep a kinematic Rigidbody + trigger SphereCollider on the character root so interaction survives whichever component is steering. NavMesh surfaces, agents, and links themselves are `initialize-ai-navigation`'s job — this note only keeps the character touchable while an agent drives it. + +## Tuning + +Expose bobAmp, stepFreq, lean, squash on the component; tune live in Play mode via eval → capture → eval (no recompiles — the proven inner loop). Caveat: a timed-out `eval` may still have executed — mutations are not idempotent-safe; put anything long through `run_script`. + +## Verify + +Capture three frames during movement at intervals that don't alias the gait — at 2.5 Hz, samples one second apart land after exactly five half-cycles and can repeat the pose, so sample at ~0.7 s and ~1.3 s offsets (or any interval that isn't a multiple of the configured step period). The pose must differ visibly in all three. Run the machine form of the same check alongside: eval-sample the body's `localPosition.y` and one limb's `localRotation` at the three timestamps — all three samples must differ. Then stop the character and sample twice 0.5 s apart: the values must still differ (breathing, not frozen); if they match, the settle blend never handed off to idle — check the amplitude blend-out. Finish with one interaction check: drive the character over a pickup and assert its `OnTriggerEnter` fired (counter or log line) — a silent pickup means the kinematic Rigidbody or trigger collider is missing from the root. + +## Expansion scope + +Animator blend trees and state machines, humanoid retargeting onto imported/generated rigs, foot IK and slope adaptation, animation events driving feel/audio hooks, per-genre locomotion profiles. diff --git a/skills/craft-game-floors/references/game-audio.md b/skills/craft-game-floors/references/game-audio.md new file mode 100644 index 0000000..b2a6ff6 --- /dev/null +++ b/skills/craft-game-floors/references/game-audio.md @@ -0,0 +1,47 @@ +# Game Audio + +> Craft reference for the `craft-game-floors` skill. Relative links in this file resolve to the skill directory. **Load:** during the feel pass, or whenever a game has zero or unrouted audio. Routing sources into *existing* mixer groups lives in `audio-setup-mixers`; import/memory settings in `optimize-audio`; everything else about the mixer is owned here. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The floor rule + +**Zero audio reads as broken, not minimal.** The paved minimum — this skill's always-on floor, in every build it touches: pickup/confirm blip · milestone sting · ambient bed · UI click. Silence is never the default; "no audio assets" is not an excuse because placeholders can be synthesized. + +## Synthesized placeholders (no assets, no downloads) + +Generate PCM in code — either write WAVs from an editor script or build clips at runtime with `AudioClip.Create`. Recipes that sound intentional rather than like a test tone: + +- **Pickup blip**: sine, 880→1320 Hz pitch envelope over 90 ms, fast decay. Vary pitch ±8% per play so repeats don't grate. +- **Milestone sting**: three ascending notes of a major triad (e.g. 523/659/784 Hz), 120 ms each with slight overlap, soft square or sine. +- **Ambient bed**: 4–8 s loop of low-passed noise (brown noise ≈ integrate white noise, clamp) at low volume — "room tone" — optionally a slow sine swell underneath. +- **UI click**: 20 ms filtered noise burst. + +Always apply a 5 ms fade-in/out envelope to every clip — raw edges click. + +## The mixer (owned here) + +One AudioMixer: `Master → Music, SFX, Ambient`. Every AudioSource routes to a group — nothing on Master directly. + +- **Creation is editor-UI work** in the [Audio Mixer window](https://docs.unity.com/en-us/engine//manual/audio/mixer/overview.md) — there is no public scripting API for creating mixers or groups; the mechanics live under that manual section. Once the groups exist, `audio-setup-mixers` routes every source into the right one — that skill *routes only*: it does not create mixers or groups, set volumes, expose parameters, or configure ducking. Those are this file's recipes. +- **Exposed parameters.** Expose each group's Volume to script (mechanics on the Audio Mixer manual page above) and name the parameters **MusicVol** and **SFXVol** — the shell's settings sliders bind to these names. Set from script with [`mixer.SetFloat("MusicVol", dB)`](https://docs.unity.com/en-us/engine//script-reference/unityengine/audio/audiomixer/setfloat.md) — its call-timing constraint and its snapshot interaction are documented on that page; the ducking rule below exists because of that interaction. +- **Ducking — never through the parameter the sliders own.** Once a settings slider calls + `SetFloat("MusicVol", …)`, snapshots stop controlling that parameter, so snapshot ducking of the + same Music volume silently dies the first time the user touches the slider. Separate the two: + expose MusicVol/SFXVol for the sliders on the groups' own Volume, and duck through a different + mechanism — a Duck Volume effect on the Music group fed by a Send from SFX (sidechain; threshold + high enough that blips don't pump the music), or a snapshot that lowers a *separate* + duck-dedicated attenuation the sliders never touch. The sidechain route is the floor default + because it coexists with the sliders by construction. + +**Headless floor:** mixer *authoring* needs editor UI, so a fully headless session routes volumes through one static gain layer (Music/SFX floats every source multiplies in) and leaves mixer creation as the first human-session upgrade — the settings sliders bind either way. + +## Placement + +2D (no spatialization) for UI and milestone sounds; spatialized 3D (`spatialBlend = 1`, sensible min/max distance) for world pickups and ambient emitters. Force short 3D clips to mono (`optimize-audio`). + +## Verify + +Play-mode check via `unity-cli` eval, branched by implementation. **Mixer build:** trigger a pickup and a milestone, assert the sources played (`AudioSource.isPlaying` true during the clip, or `time` advanced), and assert the list of sources with a null `outputAudioMixerGroup` is empty — a null group means route that source. **Headless gain-layer build:** `outputAudioMixerGroup` is legitimately null everywhere; instead assert every source's volume multiplies through the gain layer (set the Music float to 0, assert the music source's effective volume followed; restore it). Either branch: a source that never plays means the clip was never generated — confirm the synth script ran and the clip's `length` is > 0. Caveat: a timed-out `eval` may still have executed — mutations are not idempotent-safe; put anything long through `run_script`. + +## Expansion scope + +Generated audio via asset services, adaptive/vertical music layers, footstep surface systems, audio LOD and voice management, loudness normalization to platform targets. diff --git a/skills/craft-game-floors/references/game-feel.md b/skills/craft-game-floors/references/game-feel.md new file mode 100644 index 0000000..4a178f5 --- /dev/null +++ b/skills/craft-game-floors/references/game-feel.md @@ -0,0 +1,68 @@ +# Game Feel + +> Craft reference for the `craft-game-floors` skill. Relative links in this file resolve to the skill directory. **Load:** in the feel pass after the core loop works, and whenever actions feel flat, snappy-less, or unacknowledged. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The floor rule + +**Every player-visible state change gets a visible + audible answer within 100 ms.** A pickup that just disappears, a bar that snaps, a level-up that's only text — each reads as broken, not minimal. + +## The utility (author once, no external deps) + +One static `Ease` class — three curves plus a coroutine tween helper — is enough that nobody reaches for a package. The tween runs on **unscaled time by default** so pause fades and UI keep moving at `Time.timeScale = 0`; pass `unscaled: false` for gameplay tweens that should freeze under pause or hit-stop. + +```csharp +public static class Ease +{ + // The three curves the recipes name — OutCubic, OutBack (classic 1.70158 overshoot) and + // OutElastic — use the standard formulas from an easing reference such as https://easings.net. + + // The opinionated part: tweens run on UNSCALED time by default, so shell and UI motion + // survives timeScale 0; gameplay tweens that should freeze under pause pass unscaled: false. + public static IEnumerator Tween( + float duration, System.Action apply, System.Func ease, bool unscaled = true) + { + for (float t = 0f; t < 1f; t += (unscaled ? Time.unscaledDeltaTime : Time.deltaTime) / duration) + { + apply(ease(t)); + yield return null; + } + + apply(1f); + } +} +``` + +## The recipes + +- **Pickup**: scale-pop the collector or a proxy (1 → 1.25 → 1, 0.15 s, OutBack), 8–12 palette-colored particles, a pickup blip (the audio floor's synthesized recipe), floating "+XP" text rising ~0.8 units and fading over 0.6 s. +- **Numbers animate, never snap**: XP/health bars tween fill over 0.3 s OutCubic; counters count up. (Set the uGUI `Image` sprite — `Type.Filled` silently no-ops with a null sprite; found live.) +- **Milestone (level-up, win)**: a *moment*, never just text — the worked sequence below is the template. +- **Movement life**: idle breathing bob (sine, ~2 s period), slight lean into movement direction, landing squash (y 0.85 for 0.1 s). See [art-direction](art-direction.md) for why nothing is statuary. +- **Impacts (action games)**: screenshake via Cinemachine Impulse ([camera-rigs](camera-rigs.md)), hit-stop via [`Time.timeScale`](https://docs.unity.com/en-us/engine//script-reference/unityengine/time/timescale.md) `= 0.05` for 50–80 ms of *unscaled* time — always through one manager so effects never fight. + +## The worked milestone: level-up + +Exact values; retime to taste but keep the shape (flash → sting → banner → payoff → hold): + +| When | What | Values | +|---|---|---| +| 0 ms | Flash | fullscreen overlay in the palette accent, alpha 0.6 → 0 over 100 ms, OutCubic, unscaled | +| 0 ms | Sting | the audio floor's three-note ascending sting starts with the flash | +| 50 ms | Banner in | "LEVEL 7" scales 0 → 1 over 350 ms OutBack — overshoots to ~1.1 around 250 ms | +| 50 ms | Burst | 30–50 confetti particles, cone up, 2–3 palette colors | +| 400 ms | Payoff | XP bar refills to the new floor over 0.3 s OutCubic; the level counter counts up | +| 1,500 ms | Banner out | scales 1 → 0 over 200 ms OutCubic; overlay and particles are gone by now | + +Input stays live throughout — the moment is a layer over play, never a lock. Run every tween in it unscaled so a level-up landing on the pause edge can't freeze mid-flash. + +## Tunables stay visible + +Expose the numbers (pop scale, tween durations, shake amplitude) on one component or a debug overlay so the iterate-by-looking loop (`unity-cli` capture → eval → capture) can tune them live without recompiles. Caveat: a timed-out `eval` may still have executed — mutations are not idempotent-safe; put anything long through `run_script`. + +## Verify + +Via `unity-cli` eval in Play mode: trigger one pickup and one milestone, then assert — the popped transform (the collector or its proxy — whichever the pickup recipe animated) returned to its **pre-pop baseline scale** within 0.5 s (record the baseline before triggering; characters often have non-unit root scales, so never assert against literal 1), the XP bar's `fillAmount` sampled twice 0.1 s apart mid-tween gives two different values (animating, not snapping), and the floating text object exists during the moment and is destroyed within ~1 s. Capture the busiest instant with the **screen** source so the feedback is in the frame. On any miss, the fix is wiring, not taste — confirm the gameplay event actually calls the feel layer. + +## Expansion scope + +Curve-preset library, UI transition system, controller rumble, frame-event-driven animation feedback, per-genre feel profiles (platformer vs. shooter vs. cozy). diff --git a/skills/craft-game-floors/references/game-shell.md b/skills/craft-game-floors/references/game-shell.md new file mode 100644 index 0000000..cc027c2 --- /dev/null +++ b/skills/craft-game-floors/references/game-shell.md @@ -0,0 +1,45 @@ +# Game Shell + +> Craft reference for the `craft-game-floors` skill. **Load:** once the core loop works, before the feel pass concludes — and in any ad-hoc session, whenever the game boots straight into gameplay: such a build isn't done. UI construction mechanics live in the `ui` skill family; this reference owns which screens exist and how they behave. Relative links in this file resolve to the skill directory. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The opinion + +A game that boots directly into gameplay reads as a demo. The shell is formulaic — which is exactly why an agent should never skip it: it's an hour of work that changes what the artifact *is*. + +## The minimum shell (four states, one state machine) + +One `GameState` enum (`Title, Playing, Paused, Ended`) on one manager; every screen is a palette-styled panel (see [art-direction](art-direction.md) — default uGUI skin never ships; if no art bible or Palette source exists yet, run that reference first — it is the root dependency). On a fresh URP template, import TMP Essential Resources before the first text element ([TMP font assets](https://docs.unity3d.com/Packages/com.unity.ugui@/manual/TextMeshPro/index.html)); the exact failure and fix are in the table below. + +- **Title** — the worked treatment below. **Backdrop is the live game scene**, gameplay HUD hidden. Free production value; no separate art, and the style uniformity is automatic. +- **Pause** — `Time.timeScale = 0` + dimmed overlay; Resume / Restart / Quit. Audio ducks −6 dB (mixer snapshot, [game-audio](game-audio.md)). Feel/tween code must run on *unscaled* time while paused ([game-feel](game-feel.md)'s Tween has an unscaled path). +- **Settings** — Music and SFX sliders bound to the mixer parameters MusicVol and SFXVol (the audio floor's exposed names, [game-audio](game-audio.md)). Probe before binding: `mixer.GetFloat("MusicVol", out _)` must return true; if there is no mixer, bind the audio floor's static gain layer instead — and if there is no audio at all, run that floor first. That's the floor; more is expansion scope. +- **Ended** — win/lose banner as a *moment* (milestone recipe from [game-feel](game-feel.md)), the run's numbers (score/level/time), Restart + Title. Restart must produce a clean run — re-enter via scene reload or a real reset path, never leftover state. + +## Worked title treatment (imitate this, then vary) + +- **Layout**: game name centered at ~60% screen height, the shell's largest text; prompt line centered at ~25% height, small, letter-spaced uppercase. Nothing else on screen — no placeholder menus. +- **Palette roles**: name in the palette's accent with a soft shadow in the palette's darkest value; prompt in the neutral light tone at ~70% alpha; any scrim behind text is the darkest palette value at 60–80% alpha, never pure black. +- **Backdrop camera (3D)**: slow idle orbit of the play space, roughly 4–6°/s, framing the character or centerpiece off-center (rule of thirds); or a locked wide shot if the scene reads better still. +- **2D delta**: the backdrop is a slow parallax pan, not an orbit — pan across the level at ~0.5 units/s with background layers moving at fractional speeds. +- **Prompt text**: "press any key" (or the real bind), alpha pulsing 0.5↔1.0 over ~1.6 s on unscaled time — motion is what marks it interactive. + +## Transitions + +Every state change crosses a 0.3 s fade (one fullscreen black-from-palette overlay, alpha tween on unscaled time). Hard cuts between screens read as broken. + +## Failure modes (verify by capture, not compile) + +| Exact error / symptom | Root cause | Fix | +|---|---|---| +| `Value cannot be null (source)` on the first `TextMeshProUGUI` | fresh URP templates ship without TMP Essential Resources, so the default font asset is null | import the essentials first — editor-side `TMPro.TMP_PackageResourceImporter.ImportResources(true, false, false)` — then recreate the element | +| UITK text input renders at zero width despite an authored width | the default theme's flex-basis collapses it | set `flex-basis` explicitly on the input | +| UITK panel renders dark with no warning | an unresolved `var(--token)` in the stylesheet | define or correct the token | +| UITK styles silently absent | a `.tss` that `@import`s a `.uss` written in the same batch can drop it | author the `.uss` in its own pass, or inline the styles | + +## Verify + +Drive the full loop headlessly via `unity-cli` (eval state changes + capture each screen): boot → Title frame, pause → Paused frame, end → Ended frame, restart → clean second run. "Clean" is asserted, not eyeballed: eval-read the `GameState` enum at each step to confirm the transition landed, re-read the run counters after restart (score/XP/level back at initial values), and read the Editor log for a zero console-error count. Caveat when driving via `eval`: a timed-out eval may still have executed — mutations are not idempotent-safe; long work goes through `run_script`. + +## Expansion scope + +Save slots and persistence, options that persist (audio, quality, language via `localization`), input rebinding UI, accessibility (text size, contrast, screen-shake toggle), controller/focus navigation, credits and legal screens. diff --git a/skills/craft-game-floors/references/live-floor.md b/skills/craft-game-floors/references/live-floor.md new file mode 100644 index 0000000..1ecb74b --- /dev/null +++ b/skills/craft-game-floors/references/live-floor.md @@ -0,0 +1,35 @@ +# Live Floor + +> Craft reference for the `craft-game-floors` skill. **Load:** when wiring services — after player identity exists, verified with a standalone probe (`UnityServices.State == ServicesInitializationState.Initialized` and `AuthenticationService.Instance.IsSignedIn` with a non-empty `PlayerId`), never by assuming an earlier step ran. In the creation flow the canonical moment is [game-shell](game-shell.md) time, after the flow's auth setup. Service mechanics live in `build-live-game`; this reference owns which slice, in what shape, wired when. Relative links in this file resolve to the skill directory. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The opinion + +A build without persistence, comparison, or an off-switch is a demo with a backend. The live floor is the smallest slice of UGS that makes the artifact a *live game*: the save the player never sees, the rank the end screen shows, and the lever you can pull at 2am. All three are free-tier, invisible or nearly so, need zero genre awareness, and compose directly on the anonymous PlayerId — which is why identity precedes this floor. + +**Identity precondition.** Every piece below keys on the PlayerId from the project's auth setup — run the probe in the load line above rather than trusting any prior session's record. If the probe fails (cloud link failed, sign-in skipped, or never set up), skip the floor and state its cost plainly: no persistence beyond PlayerPrefs, no rank, no remote lever — reduced scope, never UGS calls against a project with no cloud id. **Anonymous identity is device-local**: [`SignInAnonymouslyAsync`](https://docs.unity.com/en-us/authentication/use-anon-sign-in.md) creates a fresh account where no cached credentials exist — a reinstall can clear that cache, so the save is only as durable as the device's sign-in cache and never follows the player to a second device. Say "persists on this device while you stay signed in" in any user-facing claim, and name account linking (the graduation below) as what unlocks cross-device. + +## 1 · Cloud Save — progression persists by default + +`PlayerPrefs` is the default-gray of save systems: device-bound, gone on reinstall. Floor shape: one [Cloud Save](https://docs.unity.com/en-us/cloud-save.md) slot (`progress`) holding the progression state the game already tracks (level, XP, collected, settings), written on milestone and on quit, read at boot. **Deliberate deviation:** `build-live-game`'s validation rule routes sensitive writes (XP, scores, currency) through Cloud Code; the floor is client-authoritative *on purpose* — zero server code at creation time — and the server-authoritative shape in Expansion scope is the graduation, not an alternative reading. Offline-safe: cache locally, sync when the service answers. + +## 2 · Leaderboard — derived from the game's progression design, never from genre + +The game's own design names its milestone/score moment — the point where the loop calls progress (a level completed, resources banked, a run scored). **That event is the leaderboard's oracle**: if the progression design defines a score-shaped milestone, create one [leaderboard](https://docs.unity.com/en-us/leaderboards.md) from it, submit on that milestone, and show rank on the game shell's end screen ([game-shell](game-shell.md)'s stats block gains one line). No score-shaped milestone in the design, no leaderboard — the rule stays genre-blind. Anonymous players get generated display names ("Otter-7f2a" style), which defers the name-moderation question past the floor. + +## 3 · The kill switch — one Remote Config flag + +`liveops_enabled` (bool, default true) in [Remote Config](https://docs.unity.com/en-us/remote-config.md) gates the live features: save sync, leaderboard submit/read. Fetched at boot **and re-fetched on a timer or at natural seams** (scene load, app focus) — a boot-only cache means the 2am toggle can't reach sessions already running, which defeats the lever. One flag, not a config schema — it exists so a live problem is a toggle, not a hotfix. + +## Wiring order + +Identity → *(the loop and feel passes)* → **live floor wires with the shell**: rank belongs on the end screen and save-on-quit belongs to the shell's state machine, so this reference applies at [game-shell](game-shell.md) time, not before. Verify before done, each check naming its call: write the save and read it back (`CloudSaveService.Instance.Data.Player` save then load — the loaded value must equal the written one); submit a score and read the rank back (`LeaderboardsService.Instance` add-score then get-player-score — rank non-null); flip `liveops_enabled` in Remote Config's web console, wait past the re-fetch interval (or force a re-fetch), and assert the gated calls stop — a flip can only reach a running session through the re-fetch above, so this check also proves the re-fetch works. The flag flip is the one human-gated check — in a headless session, assert instead by eval that every live call path reads the flag first and that the re-fetch path exists. + +**Provisioning floor (the honest headless line):** creating the leaderboard and Remote Config resources needs the Deployment window or a service-account key — a human gate this floor never consumes. A fully headless run wires the code paths, verifies the Cloud Save round-trip for real, and lets rank and the flag degrade gracefully ("Rank: unavailable"; flag defaults on) — naming leaderboard and Remote Config provisioning as the first human-session deploys, the same pattern as the audio floor's mixer authoring. + +## What the floor deliberately excludes + +Cloud Code (infrastructure, not an outcome), **Economy** (check [its documentation](https://docs.unity.com/en-us/economy.md) for availability before ever choosing it — never default a new game onto a service whose docs say it cannot accept one; its documented replacement for new projects is a server-authoritative Cloud Code + Cloud Save + Remote Config setup, exactly the graduation shape below), matchmaking/multiplayer, Vivox, achievements/battle passes. All prompt-routed via `build-live-game` when asked for. + +## Expansion scope — the server-authoritative shape + +When the leaderboard starts mattering or money enters, the floor graduates to `build-live-game`'s validated shape: a Cloud Code module owns every write (save becomes a server ledger, scores are validated server-side, the client is never trusted). Scar tissue that graduates with it: pin the Cloud Code package to the stable line, never the experimental major; keep the kill switch in the release-config template so no build ships without the lever. diff --git a/skills/craft-game-floors/references/placeholder-assets.md b/skills/craft-game-floors/references/placeholder-assets.md new file mode 100644 index 0000000..5be2e58 --- /dev/null +++ b/skills/craft-game-floors/references/placeholder-assets.md @@ -0,0 +1,35 @@ +# Placeholder Assets + +> Craft reference for the `craft-game-floors` skill. **Load:** whenever the build needs an asset it doesn't have, before reaching for external content. Silhouette/proportion rules live in [art-direction](art-direction.md); the audio floor reference owns placeholder sound. Sibling links resolve to the skill directory. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The doctrine: primitive but proud + +A placeholder that follows the art bible is *style*; a placeholder that ignores it is *missing content*. The player can't tell a deliberate flat-shaded cube-chair from programmer art unless the color, proportion, or seam gives it away. This reference removes the giveaways. Nothing in frame is ever magenta, default-gray, or default-font. + +## Meshes + +- **Compound primitives first** — characters and props assembled from tapered, tilted primitives per [art-direction](art-direction.md)'s rules (its worked fox build is the template to imitate), palette-tinted per part via material or vertex color. +- **[ProBuilder](https://docs.unity3d.com/Packages/com.unity.probuilder@/manual/index.html) for architecture** — add `com.unity.probuilder` when rooms need trim, baseboards, window frames, door reveals, or furniture beyond stacked cubes; the linked manual carries the authoring mechanics (editor-script mesh API included). Through `unity-cli`, short edits fit `eval`; anything longer goes through `run_script` — a timed-out `eval` may still have executed, so mesh mutations through it are not safe to blindly retry. +- **Bevel cheat** — a slight scale-inset second material band or ProBuilder bevel on edges catches the toon ramp and reads as manufactured, not extruded. +- **Scaled-primitive colliders lie** — a non-uniformly scaled primitive keeps its original collider shape limits: a squashed cylinder's capsule collider clamps its height to twice its radius and becomes an invisible dome that walls characters off. Strip or replace colliders on squashed primitives (a BoxCollider, or a fresh mesh collider). + +## Textures (sparingly — toon styles are mostly flat) + +Generate `Texture2D` in code when a surface needs interest: stripes, checkers, dot grids, low-octave value noise — always palette-tinted, always subtle (10–15% value variation). A rug, a picture frame's art, a window's sky card. Point-filter small textures for crisp toon looks. + +## UI icons and type + +- **Icons**: draw simple geometric glyphs (paw, star, gear, speaker) into a `Texture2D`/Sprite from code — circles, capsules, and rotated rects compose almost any HUD icon at 64px. +- **Type**: the default font is a giveaway. Style TMP's default aggressively — bold weight, +2–5% character spacing, palette-colored with a darker palette outline (0.15–0.2) — so it reads as chosen. Never ship untinted white Arial-look text. Font-asset mechanics (weights, outline and underlay setup, fallback atlases, SDF settings) live in `optimize-text-mesh-pro`; this reference owns only the styling opinion. + +## The escalation ladder + +1. Primitives + palette (free, always first) → 2. ProBuilder + procedural textures (minutes) → 3. **the generator slot** — external asset generation and store content are a product decision above this reference's pay grade; when that lands, it replaces rungs, not the doctrine. + +## Verify + +The done gate's palette-discipline axis is the hard check, run as a machine scan before any capture (via `unity-cli` eval): walk every `Renderer` for a `sharedMaterial` named `Default-Material`, every TMP component for the stock `LiberationSans SDF` font asset left unstyled, and `RenderSettings.skybox` for the template sky — all three lists must come back empty, and any hit names its object. Then the taste check: screenshot a prop close-up — if it reads as "unfinished" rather than "stylized", the fix is proportion or color, not more geometry. + +## Expansion scope + +First-party asset generation (text-to-3D, textures, sprites), Asset Store pull-and-restyle flows, style-transfer onto imported models, LOD/optimization of generated content — the strategic half this reference deliberately leaves open. diff --git a/skills/craft-game-floors/references/scene-lighting.md b/skills/craft-game-floors/references/scene-lighting.md new file mode 100644 index 0000000..7ff2f0c --- /dev/null +++ b/skills/craft-game-floors/references/scene-lighting.md @@ -0,0 +1,52 @@ +# Scene Lighting + +> Craft reference for the `craft-game-floors` skill. **Load:** immediately after the first scene exists in any 3D build — or on first touching a 3D scene still lit by the template's defaults. Post-effect mechanics live in `urp-postprocessing` — this reference says *which* values; that skill says *how*. Sibling links resolve to the skill directory. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The opinion + +Light tells the scene's story. The un-opinionated default — one white directional at noon over flat gray ambient — is why agent-built games look like documents. Every 3D scene built under these floors (the paved default — and any scene this skill touches) gets the baseline below before gameplay authoring continues. The values in this reference supersede the project baseline's and `urp-postprocessing`'s own starting points; that skill remains the mechanics and debugging reference for the Volume framework itself. + +## The key light + +- One directional, **warm-tinted from the palette** ([art-direction](art-direction.md); toward `#FFF2DC`, never pure white), intensity ~1.1–1.3. +- Elevation 35–50°, azimuth angled across the play axis so faces get light/shadow variation (never straight down, never behind camera). +- Soft shadows on; shadow distance sized to the playable space, not the pipeline default (URP ships 50 m; Built-in 150 m). + +## The ambient answer (the half everyone skips) + +Environment lighting → **Gradient**: sky = cool tint from the palette, equator = neutral mid, ground = dark warm. This is what keeps shadow sides alive instead of dead gray. For toon styles the ambient is doing the "bounce" the ramp can't. + +## The global Volume (via `urp-postprocessing`) + +One global [Volume](https://docs.unity.com/en-us/engine//manual/post-processing-and-full-screen-effects/urp/volumes-landing-page.md) asset, always — and **first, check the camera**: a script-created camera's `UniversalAdditionalCameraData` defaults `renderPostProcessing` to false (template cameras ship with it on), and the whole Volume silently does nothing until it is enabled. + +**[Bloom](https://docs.unity.com/en-us/engine//manual/post-processing-and-full-screen-effects/urp/effect-list/bloom.md) needs HDR here** — with SDR's 0–1 colors the threshold values below never fire (the skill's own trap note; the Bloom page doesn't state it). Confirm HDR on the active URP asset (and not overridden off on the camera); a build that must stay SDR runs Threshold 0.9 with Intensity halved. + +Baseline starting values: + +| Override | Start | Why | +|---|---|---| +| Tonemapping | ACES (Neutral for flat toon looks) | free depth and rolloff | +| Bloom | intensity 0.25–0.4, threshold 1.1 (HDR on) | glow on accents/pickups only | +| Vignette | 0.2–0.25 | frames the action | +| Color Adjustments | saturation +8, contrast +5 | counters engine-default flatness | + +Interiors: consider palette-tinted fog for depth. Skip AO on toon styles (fights the bands); consider it on stylized-lit ones. + +## Per-mood variants (worked) + +Starting points keyed to the worked bibles in [art-direction](art-direction.md) — tune from the matching row, never from zero. + +| Mood | Key light | Tonemapping | Bloom | Vignette | Color Adjustments | Extras | +|---|---|---|---|---|---|---| +| Cozy interior | warm `#FFE8C8`, 1.15, elev 38° | ACES | 0.3 @ 1.1 | 0.25 | sat +8, contrast +5 | White Balance temp +10; faint palette-tinted fog | +| Bright arcade | cool dim `#CFE6FF`, 0.6, elev 60° | Neutral | 0.8 @ 0.9 | 0.18 | sat +15, contrast +10 | emissive materials carry the light; ambient from the bounds color | +| Moody sci-fi | cool hard `#BFD2E6`, 1.3, elev 55° | ACES | 0.35 @ 1.2 | 0.3 | sat −5, contrast +12 | White Balance temp −8; fog tinted from the shadow value | + +## Verify + +**This verify is URP-typed** — establish the pipeline first (`GraphicsSettings.currentRenderPipeline`; the router's mid-flow check does this). On Built-in or HDRP, apply this file's light-story opinions (key, ambient, mood) but skip the URP checks and name the pipeline gap in the report — `UniversalAdditionalCameraData` and the URP Volume types don't exist there, and `urp-postprocessing` stops by design when URP is inactive. Under URP, machine checks first (via `unity-cli` eval): the camera's `UniversalAdditionalCameraData.renderPostProcessing` reads true, and the global Volume's profile reports all four overrides present and active. Then capture with the Volume enabled and disabled and diff the two PNGs — a mean per-channel delta that rounds to zero means the settings are too timid; raise until the diff is unmistakable, then pull back one notch. Keep each tuning eval short: a timed-out `eval` may still have executed, so long sweeps go through `run_script` instead. + +## Expansion scope + +Baked GI / light-probe workflows, time-of-day rigs, per-scene Volume blending, mobile/web budget variants, HDRP parity. diff --git a/skills/craft-game-floors/references/toon-shading.md b/skills/craft-game-floors/references/toon-shading.md new file mode 100644 index 0000000..44d16d0 --- /dev/null +++ b/skills/craft-game-floors/references/toon-shading.md @@ -0,0 +1,34 @@ +# Toon Shading + +> Craft reference for the `craft-game-floors` skill. **Load:** when the art bible picks a cel/toon/anime style — and if no art bible exists yet, author one first via the look floor ([art-direction](art-direction.md)). Sibling links resolve to the skill directory. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The opinion + +One toon shader family for the **whole frame**. Characters, walls, props, floor — all of it. A cel-shaded hero in a PBR room is the canonical broken look. + +## The ramp (URP, Unity 6) + +Two routes; pick by what's in the project: + +- **Shader Graph route** — the lighting lives in a [Custom Function node](https://docs.unity3d.com/Packages/com.unity.shadergraph@/manual/Custom-Function-Node.html), because Shader Graph exposes no main-light data as built-in nodes. Inside the function, call `GetMainLight()` and quantize `NdotL` into 2–3 hard bands via `smoothstep` with a narrow edge (0.02–0.05) so bands stay crisp but antialiased. Two requirements, mechanics on the linked pages: + - Give the function a preview-safe branch per the Custom Function node page above (previews have no light data). + - For cast shadows, pass `GetMainLight` a shadow coord and declare the three main-light shadow [keywords](https://docs.unity3d.com/Packages/com.unity.shadergraph@/manual/Keywords.html) (`_MAIN_LIGHT_SHADOWS`, `_MAIN_LIGHT_SHADOWS_CASCADE`, `_SHADOWS_SOFT` — Boolean, Global, Multi Compile), or `shadowAttenuation` reads 1 everywhere. + - `shader-graph-create-custom-node` turns finished HLSL into a reflected function node (it needs `com.unity.shadergraph` 17.5 or newer); the graph wiring above stays this reference's recipe. +- **Hand-written HLSL route** — a single ~120-line URP shader covers it. Same quantized-NdotL core; add `_BaseColor`, `_ShadowTint`, `_Bands`. + +Either way: **shadow bands are palette-tinted, never black** — sample the art bible's shadow color (usually a cooler, darker sibling of the base) — and main-light shadow attenuation multiplies into the band input on both routes so cast shadows band too. + +## Outlines + +- **Inverted hull** (default): a second pass/material extruding vertices along normals in clip space, front-face culled. Width in screen space (~2 px equivalent) so it doesn't collapse with distance; outline color = palette's darkest value, not black. +- **Renderer Feature** (whole-frame route): a fullscreen edge pass on depth+normals when you want environment edges too. Prefer this when the environment is mostly flat walls — hull outlines on giant planes look wrong. The feature is Render Graph code: run `validate-urp-render-graph-renderer-feature` over it before trusting it. + +## The supporting cast + +- **Skybox**: gradient from the palette (simple shader or tinted procedural) — the default blue procedural sky instantly breaks the style. +- **Fog**: if used, tinted from the palette. +- **Verify** (via `unity-cli`): machine scan first — eval over every renderer in frame asserting `sharedMaterial.shader.name` belongs to the toon family, with zero hits on `Universal Render Pipeline/Lit` (any hit names the object to convert) — then capture: bands visible on a curved surface, outlines uniform at two capture distances. + +## Expansion scope + +Ramp textures for painterly grades, rim/specular toon terms, face-shadow maps for characters, 2D-lit variant, VFX Graph toon integration, per-object outline masks. diff --git a/skills/craft-game-floors/references/vfx.md b/skills/craft-game-floors/references/vfx.md new file mode 100644 index 0000000..a18b710 --- /dev/null +++ b/skills/craft-game-floors/references/vfx.md @@ -0,0 +1,40 @@ +# VFX + +> Craft reference for the `craft-game-floors` skill. Relative links in this file resolve to the skill directory. **Load:** during the feel pass and whenever an effect is needed — [game-feel](game-feel.md) says *when* effects fire; this reference says *what* they look like. Colors come from [art-direction](art-direction.md)'s palette; glow interplay from [scene-lighting](scene-lighting.md)'s bloom threshold. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The rules (every effect, no exceptions) + +- **Palette only** — start/gradient colors sampled from the art bible; a white default particle breaks style as hard as a gray material. +- **Short-lived** — 0.3–0.8 s lifetimes for event effects; size-over-lifetime shrinks to zero so nothing pops out of existence. +- **Readable at gameplay distance** — size and count tuned against the real camera ([camera-rigs](camera-rigs.md)), verified by capture, not in the inspector. +- **Pooled** — never Instantiate/Destroy per event. Reuse one ParticleSystem via `Stop`/`Clear`/`Play` (or one system with `Emit()`); when effects need real instances, pool them with [`UnityEngine.Pool.ObjectPool`](https://docs.unity3d.com//Documentation/ScriptReference/Pool.ObjectPool_1.html) — built into the engine, zero dependency. Construct it with a createFunc plus actionOnGet/actionOnRelease so reset-on-release is baked in; take with `Get()`, return with `Release(item)`. +- **Nothing loops without a job** — a looping effect must be telling the player something (a shrine glows because it's interactive). + +## The recipes (URP, built-in ParticleSystem) + +- **Pickup burst** — 8–12 particles, sphere emission, radial start speed 2–3, slight gravity, palette accent → transparent gradient. Pair with the pop + blip ([game-feel](game-feel.md)). +- **Trail** — for moving collectibles and guides (the butterfly case): a small emitter with rate-over-distance, 0.5 s lifetime, shrinking. Sells motion better than the mover itself. +- **Ambient motes** — the interior seller: slow-drifting dust in the key light's direction, 10–20 alive, barely-visible palette-light color, large box emitter above the play space. This one effect makes a room feel inhabited. +- **Milestone confetti** — 30–50 particles, cone up, gravity on, 2–3 palette colors, 1.2 s — reserved for the full-screen moments so they stay special. +- **Movement puffs** — tiny 3–4 particle poofs on footfall/landing, ground-colored, 0.3 s. + +Material — the paved default: `Sprites/Default` (or URP particle-unlit explicitly set to Transparent) tinted from the palette; the failure table below has the two traps. Additive/glow sparingly and only above the bloom threshold on purpose — one glowing thing per frame is an accent; five is soup. + +## 2D delta + +In a 2D scene, particles sort through the renderer like every sprite: set Sorting Layer and Order in Layer on the [`ParticleSystemRenderer`](https://docs.unity.com/en-us/engine//script-reference/unityengine/particlesystemrenderer.md) (both inherited from `Renderer`) so effects draw above the sprites they comment on and below UI. Sorting Layer beats camera distance — an effect on the wrong layer hides behind the background no matter its Z. + +## Failure modes + +| Symptom | Root cause | Fix | +|---|---|---| +| particles render fully opaque despite an alpha tint | URP particle-unlit ignores alpha while Surface Type is Opaque | use `Sprites/Default`, or set the material's Surface Type to Transparent (or enable Alpha Clipping) | +| console floods with curve errors while animating velocity over lifetime | the x/y/z curves are in mixed modes (two set, one left default) | set **all three axes** in the same curve mode | + +## Verify + +Capture mid-effect at the gameplay camera (the done gate's "feedback visibility" axis needs the effect *in* the frame) — and time the capture by eval, not luck: assert the system's `particleCount > 0` at the capture moment. Pooling check: fire the effect ten times and assert the ParticleSystem instance count stayed constant before and after (a growing count means an Instantiate leaked in). Check the ambient motes read in a still — if they only register in motion, raise size slightly, never count. + +## Expansion scope + +VFX Graph for GPU-scale effects, shader-driven dissolves/impacts, decals, screen-space effects (speed lines, damage vignettes), effect LOD and budgets per platform. diff --git a/skills/craft-game-floors/references/visual-scorecard.md b/skills/craft-game-floors/references/visual-scorecard.md new file mode 100644 index 0000000..6919a65 --- /dev/null +++ b/skills/craft-game-floors/references/visual-scorecard.md @@ -0,0 +1,43 @@ +# Visual Scorecard + +> Craft reference for the `craft-game-floors` skill. **Load:** at the end of every build session and after any visual change worth verifying. A single-floor touch-up owes only that floor's own Verify section; the full board below is the build session's done gate. Capture mechanics live in `unity-cli` (editor_play → capture_game_view → eval). Relative links in this file resolve to the skill directory. Confirm engine behavior against the linked documentation for the installed version before acting on it. + +## The rule + +**The first capture is a draft, never the deliverable.** A build session under these floors (any paved build) ends with scored frames, not a green compile. "It runs" and "it's done" are different claims; this gate is the difference. + +## Procedure + +1. **Record the Editor's starting state first** — whether Play mode is already running (a user-owned session is never restarted or stopped: score inside it or ask), and the current auto-tick and `runInBackground` values, so both can be restored after the gate. Then prove liveness with this four-call loop before trusting any capture: `editor set_auto_tick on` · script eval [`Application.runInBackground`](https://docs.unity.com/en-us/engine//script-reference/unityengine/application/runinbackground.md) `= true` · `editor.playmode start` · a job wait on `Time.frameCount` with op `changed`. The command names here are the installed pipeline package's — confirm them with `unity command` before running. Run the loop every time: capture-on-demand renders frozen games as alive, and an unfocused Editor can freeze play at frame 1–2 while status still reports playing. **After the gate, restore the recorded auto-tick and `runInBackground` values and exit Play if the gate started it.** +2. Render a warm-up frame and discard it before every scored capture — the first render after a rebuild comes back blank (async shader compile). +3. Capture **three frames**: the standard gameplay angle · a character close-up · the busiest UI moment (mid-pickup or milestone, so feedback is *in* the frame). **UI in captures:** game-view capture can skip ScreenSpaceOverlay canvases entirely while the canvas is provably alive — for any frame that must show UI, either use a screen-level capture source where the installed pipeline package offers one (confirm its capture flags — names and `--view` options vary by version) or put the HUD on a ScreenSpaceCamera canvas, which renders in every capture path. Verify UI presence in the frame, not in the hierarchy. **Driving the game for captures:** simulated key events may not reach Input System polling in editor play mode — when a verification drive needs movement, compile a small debug-input hook into the controller rather than fighting the input simulator. **Where captures land:** `capture game_view` writes to `Assets/Captures/` — add `Assets/Captures/` to .gitignore before the first capture. +4. Score each axis 1–5. The palette-discipline instant fail is machine-checkable first: eval-scan every renderer for `Default-Material`, TMP for the stock font **left at default settings** (a deliberately styled stock font passes — placeholder-assets prescribes exactly that), **every ParticleSystem's startColor and every HUD panel Image for untinted white/gray** (the two elements that most often slip the palette while passing the material scan; a white sprite tint that deliberately preserves the sprite's own colors also passes — the scan flags defaults, not choices), before scoring — so axis 1's fail never rests on eyesight alone. **If the request authorized changes**, anything below 3 gets a fix, and spend at least one fix pass even on a passing board; **on an assessment-only ask** ("score it"), report the board and the recommended fixes and change nothing until the user says go. When tuning live via `eval`: a timed-out eval may still have executed — mutations are not idempotent-safe; long work goes through `run_script`. + +## The six axes + +| Axis | The question | Instant fail | +|---|---|---| +| Palette discipline | Does every color trace to the art bible? | default-gray material, skybox, or UI anywhere | +| Style uniformity | One style across character, environment, sky, UI? | style breaks at the character's edge | +| Lighting depth | Warm/cool separation? Shadow sides alive? | flat white directional look | +| Silhouette | Is the character identifiable filled black? Grade from the gameplay camera angle as well as a side view. | reads as "capsules" | +| UI intentionality | HUD on the palette, aligned, styled text? | default uGUI skin visible | +| Feedback visibility | Does the busy frame *show* the game answering the player? | a pickup frame with nothing happening | + +## The 2D anchor set + +For a 2D build, grade the same six questions through their 2D equivalents: palette discipline = every sprite tint traces to the bible (no default-white sprites); style uniformity = one consistent pixel density across all sprites, no mixed-resolution art; silhouette = the character reads at gameplay zoom, not just in the source asset; lighting depth = outline discipline instead — either every gameplay-critical sprite carries the outline treatment or none does; UI intentionality and feedback visibility grade unchanged. Where a 2D floor is missing, name the gap in the report rather than grading against a 3D axis the build cannot meet. + +## Calibration anchors + +- **5** — a promo still from a stylized indie (Monument Valley, A Short Hike): total palette and style discipline. +- **3** — a polished jam entry: simple assets, but deliberate light, consistent style, styled UI. **The paved floor — the minimum any build under these floors ships at.** +- **1** — untextured primitives under one white directional with default-gray UI: the documented starting point this gate exists to abolish. + +## Report + +Print the board in one fixed grammar — one line per axis, `axis: /5 — ` — so a later session can parse it, then what the fix pass changed, and attach the final captures. The scored frames are the session's proof-of-done artifact. + +## Expansion scope + +Baseline captures + visual regression across sessions, resolution/aspect sweeps, colorblind-safety check, bot-driven capture routes, per-genre anchor packs. diff --git a/skills/ui-ugui/SKILL.md b/skills/ui-ugui/SKILL.md index 9c5e114..52ebaf7 100644 --- a/skills/ui-ugui/SKILL.md +++ b/skills/ui-ugui/SKILL.md @@ -40,7 +40,7 @@ Determine what the user is asking for: - Other namespaces in the project can cause ambiguous type errors **Verify before modifying:** -- Always check what currently exists before making changes +- Always check what exists before making changes - Confirm parent objects exist before adding children - Verify components are present before modifying properties - Never assume hierarchy state — query it first