计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
lab【效率与协作】Build a Game Lab for one mechanic of a studio's game (the jump, a hit, a dash, a drift, a landing) and iterate on how it FEELS with the creator, side by side. The lab plays one short take in two builds at once, New (the working tree) and Today (the last commit), on one clock with the same seed and presses, slowed to a tenth or stepped a frame at a ti…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
A studio is the folder with studio.json. The lab is npx --no-install homie-studio lab <game> (from
@homie-rocks/studio 0.20.0): a page on this computer that plays a take (games//lab.json) in New (the
working tree, rebuilt on every save) beside Today (HEAD, or --today <ref>). Both run on the lab's clock: frame f
is exactly f/fps seconds, the dice are seeded, the presses land on the same frames, so a take plays the same every
time and the two builds can be compared frame by frame. The page checks that too: "Replays match" (or where they
differ). homie-studio lab check <game> plays the same take headless and writes numbers, a contact sheet and a still.
The game opts in with a few calls from @homie-rocks/studio/lab (every one a no-op outside the lab, so they stay in
the game). references/API.md has every call, the take format and the multiplayer rules; references/PRINCIPLES.md
has the principles of feel this skill works from, with honest sources.
df -h .: keep 10 GB free. Commit or stash what the person is working on: Today is a commit, and the lab never
touches git beyond reading it (Today is built from git's own checkout of the ref, in .studio/lab/, git-ignored).Make the lab able to see the mechanic without changing how it feels (Today must be the game as it is):
const T = lab.tunables(tuning).lab.phase('LAUNCH', 'Leaves at full speed, slows evenly'), lab.phase(null)
when it is over. Short capital names; the note says what the phase is FOR. Name today's phases honestly: a
mechanic with no anticipation has no COIL.lab.track(name, v, unit)).
Speed from the position each frame (distance / dt) is honest whatever the code does inside.lab.pose('subject', { x, y, sx, sy, a }) each frame, and the game's own onion (ghosts
every few frames), arcs (the path with a dot a frame: spacing) and a reach or hit box (lab.overlay), drawn
by lab.draw(ctx, scale) in world space.lab.camera({ game: null, close: { zoom: 2.4 }, arena: { whole: true } }); 3D: side, front, top, orbit.@homie-rocks/studio/animate): pass the lab's tunables to loadCharacter(models, url, { tune: T })
and copy its ANIM_TUNING (fade, walkSpeed, runSpeed, actSpeed, jumpStretch, landSquash, squashHz, lean, flinch,
lookAt, spring) into tunables.json's "Motion" group: the clips' blends, the squash and stretch and the lean become
sliders too. Report the character's own state (IDLE, RUN, JUMP, FALL, LAND, ATTACK, HIT) as phases when the move
is an animation. The hero-rush-3d starter's takes ("jump", "swing") show it; the animate skill has the rest.lab.stage === 'dummy'
where the round starts. Only the lab ever sets it. Keep a real-play take too.{ "at": 0.5, "key": "Space" }), the view and track
to open on. Or record one: press REC in the lab and play it in NEW (both builds get the presses).Guard allocation: report from one if (lab.on) labReport(dt) a frame. Then:
npx --no-install homie-studio lab check <game> # phases show, replays match, New = Today
git commit -m "Lab: instrument <the mechanic> (no change to how it feels)"
Open the lab (npx --no-install homie-studio lab <game>, a background task; give the person the link; never open a
browser yourself) and read the take at ¼ speed with the onion skin and arcs on. Say what you see in the terms of
references/PRINCIPLES.md: no anticipation, no impact moment (hit-stop), even spacing (no ease), a creep and a pop at
the end, no follow-through or overlap, a rigid body (no squash and stretch), a camera that never reacts.
References: describe the principle and, if it helps, a well-known game where anyone can see it, in your own words. Never copy another game's art, code, sounds or frame data, and never state another game's exact numbers unless you measured them yourself from footage the person can see or a public source you name.
Write the proposal as the phases the move will have and the number that shapes each one, for example:
HIT-STOP 70 ms (the body holds, white, squashed) → LAUNCH (leaves at 1500 px/s, stretched 30%) → SLIDE (eases into the stop over 185 px, the same at 12 fps as at 60) → SETTLE 260 ms (squash, then a wobble: overlap).
Change the game in the working tree; the lab rebuilds New on save and replays the take. Then:
npx --no-install homie-studio lab check <game> # New against Today: phases, peaks, JavaScript per frame
Read REPORT.md and look at sheet.png (the lab at each phase's start, New beside Today) before you say anything
about it. Tell the person in a few lines: which phases New has that Today did not, the numbers that moved, and what
to watch for in the lab. The person tunes with the sliders and presses Keep in code (written into
tunables.json, one line a value), or tells you, and you run npx --no-install homie-studio lab set <game> name=value.
For a rules game (src/rules.ts plus view.ts), the Lab runs the same host runtime locally in each pane,
including when the published game keeps room.offline: false. No Lab state reaches a room. Put gameplay
numbers in tunables.json; the runtime reads the sliders automatically, including public.speed for a value
under public. Keep them with homie-studio lab set <game> public.speed=7. The take's stage reaches
world.stage in the rules; use that for a target or a ledge, without importing the Lab into guarded rules.
Keep lab.phase, lab.track, poses and overlays in the view. Both panes get the same seed and fixed clock.
lab check must show actual gameplay tracks changing and the repeated take matching, not just an empty page.
A studio game is played in a room. A feel change must look the same on every screen and keep the two-browser check green:
world.emit in rules, room.on in the view; net.send only in an unchanged older game)
or a change in the snapshot (a slime's hit points dropping). Flashes, squash, sparks and numbers are drawn locally.lab.random() for particles and shake, never Math.random() for lab presentation: the world's dice must be the same in
both builds for the lab to compare them, and lab.random() is Math.random() outside the lab.Then the game's own check: npm run dev (background), npx --no-install homie-studio check <game> --url http://127.0.0.1:8787. It must pass before anything is kept.
lab check prints the game's JavaScript per frame, New against Today, on the same frames (the mean: Chrome times a
page in steps of about 0.1 ms). It is a hint. For the real cost, the perf skill measures both builds in a room, on
an emulated phone, in alternating pairs: perf.mjs baseline <game> with games// at Today (git stash the
change, or check out HEAD's copy of the folder), then the change back and perf.mjs try <game> --name "<the feel change>" --looks "<what looks different>" --plays "<what plays different>" --measure. --measure is required here:
without it try reverts anything that is not faster, and a feel change is kept for its feel, not its speed. Tell the
person the result in numbers: frames (p95), the main thread per frame, time to playable, against Today. In this
skill's own proof the bump cost 0.09 ms of main thread a frame on the emulated phone (1.48 to 1.57 ms, +4.9%, within
the noise) with frames unchanged at 16.7 ms. Never say a feel change is free from the code.
The person decides. Revert what they did not like (only those lines, in games//), keep the rest, run
lab check and the two-browser check once more, then commit with the phases in the message:
git add games/<game> && git commit -m "<Game>: the bump lands (hit-stop 70 ms), flies stretched, eases out, settles"
Record a side-by-side clip the person can share with the video skill's record, driving the lab page itself:
references/record-lab.json is a steps file (play at 1x, then ¼ with the onion skin, then step through the phases).
node <video.mjs> record lab-<game> --steps <steps> --url http://127.0.0.1:8790. Stop the lab when done:
npx --no-install homie-studio lab --stop (this studio's only; it also removes the checkouts Today was built from).
http://127.0.0.1:8790/<game>/ (run the lab as a background task).game_lab tool starts the lab and answers with
a card: a still of New beside Today at the take's busiest moment, the phases of each, the numbers, and Open.if (lab.on) around reporting).lab --stop), or open a browser for the person.