@genex-ai/cli-demo 0.84.0-dev.215 → 0.86.0-dev.217
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -7
- package/dist/index.js +2110 -1871
- package/package.json +1 -1
- package/templates/controllers/character/character-animations.ts +116 -5
- package/templates/skills/genex-ai-hud/SKILL.md +45 -27
- package/templates/skills/genex-ai-hud/references/stage1-prompt-template.md +42 -5
- package/templates/skills/genex-ai-hud/references/stage2-prompt-template.md +5 -1
- package/templates/skills/genex-ai-menu/SKILL.md +12 -5
- package/templates/skills/genex-game-director/SKILL.md +56 -15
- package/templates/skills/genex-game-director/references/design-contract.md +33 -8
- package/templates/skills/genex-game-director/references/routing-map.md +40 -28
- package/templates/skills/genex-threejs-character-controller/SKILL.md +19 -0
- package/templates/skills/genex-threejs-character-controller/references/animations.md +28 -1
- package/templates/skills/genex-threejs-creatures/SKILL.md +8 -0
- package/templates/skills/genex-threejs-game-ui/SKILL.md +74 -70
- package/templates/skills/genex-threejs-multiplayer/SKILL.md +19 -0
- package/templates/skills/genex-threejs-visual-validation/SKILL.md +9 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.86.0-dev.217",
|
|
4
4
|
"description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -324,6 +324,17 @@ export interface PlayOneShotOptions {
|
|
|
324
324
|
clamp?: boolean;
|
|
325
325
|
/** Fired when the clip finishes (skipped if a newer one-shot interrupts it). */
|
|
326
326
|
onDone?: () => void;
|
|
327
|
+
/**
|
|
328
|
+
* Movement cancels this one-shot. Every one-shot is FULL-BODY (there is no
|
|
329
|
+
* upper-body layering), so it freezes the legs — a moving character SLIDES
|
|
330
|
+
* across the ground in a static pose for the clip's whole length (a 2.9s hit
|
|
331
|
+
* reaction, a 2.7s cast). With `interruptible: true`, grounded movement
|
|
332
|
+
* intent releases the clip via {@link CharacterAnimations.clearOneShot} and
|
|
333
|
+
* locomotion takes over immediately. Use it for reactions and gestures a
|
|
334
|
+
* player can walk out of; leave it off for poses that must hold to their end
|
|
335
|
+
* (a death/knockdown). Default false.
|
|
336
|
+
*/
|
|
337
|
+
interruptible?: boolean;
|
|
327
338
|
}
|
|
328
339
|
|
|
329
340
|
type MutableAnimationStateContext = {
|
|
@@ -358,6 +369,9 @@ export class CharacterAnimations {
|
|
|
358
369
|
// When true, the active one-shot holds its final pose on finish (clamp:true —
|
|
359
370
|
// e.g. Death01) instead of crossfading back to locomotion.
|
|
360
371
|
#oneShotHoldPose = false;
|
|
372
|
+
// When true, grounded movement intent cancels the active one-shot (see
|
|
373
|
+
// PlayOneShotOptions.interruptible).
|
|
374
|
+
#oneShotInterruptible = false;
|
|
361
375
|
|
|
362
376
|
#state: CharacterAnimationState = "IDLE";
|
|
363
377
|
#prevActionName: string | null;
|
|
@@ -398,7 +412,27 @@ export class CharacterAnimations {
|
|
|
398
412
|
options: CharacterAnimationsOptions = {}
|
|
399
413
|
) {
|
|
400
414
|
this.#model = model;
|
|
401
|
-
|
|
415
|
+
// Seed the fuzzy name-matched map from the locomotion profile's own slots
|
|
416
|
+
// (explicit options.clipMap still wins). Simple boolean snapshots (remote
|
|
417
|
+
// players, bots) never reach the profile's directional resolver and fall
|
|
418
|
+
// through to this map — without the seed they bind DIFFERENT clips than the
|
|
419
|
+
// local player ("Running" vs the profile's run.forward), so the same
|
|
420
|
+
// movement renders as two different gaits on two screens. Jump states are
|
|
421
|
+
// deliberately NOT seeded: jump.full would one-shot-clamp as a loop.
|
|
422
|
+
const slots = options.locomotionProfile?.slots ?? {};
|
|
423
|
+
const profileSeed: Partial<Record<CharacterAnimationState, string>> = {};
|
|
424
|
+
const seedPairs: ReadonlyArray<[CharacterAnimationState, string]> = [
|
|
425
|
+
["IDLE", "idle.default"],
|
|
426
|
+
["WALK", "walk.forward"],
|
|
427
|
+
["RUN", "run.forward"],
|
|
428
|
+
["CROUCH_IDLE", "crouch.idle"],
|
|
429
|
+
["CROUCH_MOVE", "crouch.forward"],
|
|
430
|
+
];
|
|
431
|
+
for (const [state, slot] of seedPairs) {
|
|
432
|
+
const clip = slots[slot];
|
|
433
|
+
if (clip) profileSeed[state] = clip;
|
|
434
|
+
}
|
|
435
|
+
this.#clipMap = buildClipMap(clips, { ...profileSeed, ...options.clipMap });
|
|
402
436
|
this.#resolver = options.resolver ?? resolveAnimationState;
|
|
403
437
|
this.#onChange = options.onChange;
|
|
404
438
|
this.#locomotionProfile = options.locomotionProfile;
|
|
@@ -542,6 +576,13 @@ export class CharacterAnimations {
|
|
|
542
576
|
// Optional in the snapshot (older five-boolean remote-player objects).
|
|
543
577
|
ctx.crouchActive = snapshot.crouchActive ?? false;
|
|
544
578
|
|
|
579
|
+
// Grounded movement cancels an interruptible one-shot — otherwise the
|
|
580
|
+
// frozen full-body pose slides across the ground for the clip's whole
|
|
581
|
+
// length. clearOneShot crossfades locomotion back in with no T-pose frame.
|
|
582
|
+
if (this.#oneShotAction && this.#oneShotInterruptible && ctx.isMoving && ctx.isOnGround) {
|
|
583
|
+
this.clearOneShot();
|
|
584
|
+
}
|
|
585
|
+
|
|
545
586
|
const next = this.#resolver(ctx);
|
|
546
587
|
const stateChanged = next !== this.#state;
|
|
547
588
|
if (stateChanged) this.#state = next;
|
|
@@ -550,6 +591,19 @@ export class CharacterAnimations {
|
|
|
550
591
|
// (Jump_Start/Jump_Land) finishes and unlocks, the pending loop clip must still fade in
|
|
551
592
|
// even though the state did not change again. Internal guards make this a no-op otherwise.
|
|
552
593
|
this.#desiredMotionName = this.#resolveLocomotionClip(snapshot, next) ?? this.#clipMap[next];
|
|
594
|
+
// Rigs whose airborne slots fall back to the SAME clip as jump.start (every
|
|
595
|
+
// Meshy rig: jump.rise/jump.fall → jump.full) never re-enter #applyTransition
|
|
596
|
+
// while airborne — the name never changes — so once the one-shot finishes the
|
|
597
|
+
// character hangs FROZEN in the jump's final frame for the rest of the fall
|
|
598
|
+
// (long drops, knockback launches). Once the held action has finished
|
|
599
|
+
// (paused+clamped), retarget sustained airtime at the idle loop so the limbs
|
|
600
|
+
// keep animating while descending.
|
|
601
|
+
if ((next === "JUMP_IDLE" || next === "JUMP_FALL") && this.#desiredMotionName !== null) {
|
|
602
|
+
const held = this.#actions.get(this.#desiredMotionName);
|
|
603
|
+
if (held && held.paused) {
|
|
604
|
+
this.#desiredMotionName = this.#resolveProfileSlot("idle.default") ?? this.#clipMap.IDLE;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
553
607
|
// A full-jump profile clip may outlast a very short physics hop. Release
|
|
554
608
|
// its one-shot lock as soon as the controller lands so idle/walk can
|
|
555
609
|
// crossfade immediately instead of waiting for the authored clip to end.
|
|
@@ -643,6 +697,7 @@ export class CharacterAnimations {
|
|
|
643
697
|
// finish and the rig snaps to bind pose for a frame — the T-pose flash.
|
|
644
698
|
action.clampWhenFinished = true;
|
|
645
699
|
this.#oneShotHoldPose = options.clamp ?? false;
|
|
700
|
+
this.#oneShotInterruptible = options.interruptible ?? false;
|
|
646
701
|
action.timeScale = options.timeScale ?? 1;
|
|
647
702
|
action.reset();
|
|
648
703
|
if (current && current !== action) {
|
|
@@ -660,6 +715,42 @@ export class CharacterAnimations {
|
|
|
660
715
|
return true;
|
|
661
716
|
}
|
|
662
717
|
|
|
718
|
+
/**
|
|
719
|
+
* Cancel a one-shot and hand control back to locomotion NOW. The case that
|
|
720
|
+
* REQUIRES it: a `clamp:true` one-shot (death/knockdown, a held wind-up)
|
|
721
|
+
* holds its final frame and keeps the locomotion lock closed FOREVER by
|
|
722
|
+
* design — so a respawn or an aborted action must explicitly release it, or
|
|
723
|
+
* the rig keeps the clamped pose while the body moves around. Safe to call
|
|
724
|
+
* at any time; a no-op when nothing is held.
|
|
725
|
+
*
|
|
726
|
+
* Call it on TRANSITIONS (respawn, revive, action aborted) — never per
|
|
727
|
+
* frame, or it cancels every in-flight reaction clip.
|
|
728
|
+
*
|
|
729
|
+
* @example
|
|
730
|
+
* // on respawn, before teleporting the body:
|
|
731
|
+
* anims.clearOneShot();
|
|
732
|
+
*/
|
|
733
|
+
clearOneShot(): void {
|
|
734
|
+
if (this.#disposed) return;
|
|
735
|
+
const finished = this.#oneShotAction;
|
|
736
|
+
this.#oneShotAction = null;
|
|
737
|
+
this.#oneShotOnDone = undefined;
|
|
738
|
+
this.#oneShotHoldPose = false;
|
|
739
|
+
this.#oneShotInterruptible = false;
|
|
740
|
+
this.#canPlayNext = true;
|
|
741
|
+
// Crossfade the current state's loop in FROM the held pose — same path the
|
|
742
|
+
// "finished" handler uses, so there is no unposed/T-pose frame.
|
|
743
|
+
if (finished) this.#recoverFromOneShot(finished);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/** Clip name of the currently-playing (or clamped-held) one-shot, or null.
|
|
747
|
+
* Lets a caller cancel a SPECIFIC one-shot (e.g. a held charge wind-up)
|
|
748
|
+
* without clobbering whatever replaced it:
|
|
749
|
+
* `if (anims.currentOneShotName === WINDUP_CLIP) anims.clearOneShot();` */
|
|
750
|
+
get currentOneShotName(): string | null {
|
|
751
|
+
return this.#oneShotAction?.getClip().name ?? null;
|
|
752
|
+
}
|
|
753
|
+
|
|
663
754
|
/** Stop all actions, uncache clips, remove the mixer's 'finished' listener. */
|
|
664
755
|
dispose(): void {
|
|
665
756
|
if (this.#disposed) return;
|
|
@@ -766,9 +857,23 @@ export class CharacterAnimations {
|
|
|
766
857
|
#releaseStuckLocks(): void {
|
|
767
858
|
const prevActionName = this.#prevActionName;
|
|
768
859
|
if (prevActionName === null) return;
|
|
860
|
+
// Match BOTH bindings a jump one-shot can have: the fuzzy clipMap names and
|
|
861
|
+
// the profile slots. On profile rigs the clipMap entries are usually null
|
|
862
|
+
// (no clip is literally named "jump_start"), which made this escape hatch
|
|
863
|
+
// dead code — a stuck jump lock could never self-release. The jump.land
|
|
864
|
+
// slot is ignored when it falls back to idle.default (same guard
|
|
865
|
+
// #applyTransition uses), or an idle prevAction would spuriously match.
|
|
866
|
+
const startName = this.#clipMap.JUMP_START ?? this.#resolveProfileSlot("jump.start");
|
|
867
|
+
const profileLand = this.#resolveProfileSlot("jump.land");
|
|
868
|
+
const landName =
|
|
869
|
+
this.#clipMap.JUMP_LAND ??
|
|
870
|
+
(profileLand !== this.#resolveProfileSlot("idle.default") ? profileLand : null);
|
|
871
|
+
// A manual playOneShot() lock is NOT ours to release — even when its clip
|
|
872
|
+
// happens to share a name with the jump binding (clearOneShot owns that).
|
|
873
|
+
if (this.#oneShotAction) return;
|
|
769
874
|
if (
|
|
770
875
|
!this.#canPlayNext &&
|
|
771
|
-
prevActionName ===
|
|
876
|
+
prevActionName === startName &&
|
|
772
877
|
this.#state !== "JUMP_IDLE" &&
|
|
773
878
|
this.#state !== "JUMP_START"
|
|
774
879
|
) {
|
|
@@ -776,7 +881,7 @@ export class CharacterAnimations {
|
|
|
776
881
|
}
|
|
777
882
|
if (
|
|
778
883
|
!this.#canPlayNext &&
|
|
779
|
-
prevActionName ===
|
|
884
|
+
prevActionName === landName &&
|
|
780
885
|
this.#state !== "IDLE" &&
|
|
781
886
|
this.#state !== "JUMP_LAND"
|
|
782
887
|
) {
|
|
@@ -878,13 +983,19 @@ export class CharacterAnimations {
|
|
|
878
983
|
snapshot: CharacterStateSnapshot,
|
|
879
984
|
state: CharacterAnimationState,
|
|
880
985
|
): void {
|
|
881
|
-
|
|
986
|
+
// Gate on a numeric moveSpeed rather than the full advanced snapshot:
|
|
987
|
+
// remote players and bots drive with simple boolean snapshots plus an
|
|
988
|
+
// estimated speed (position deltas / their own velocity), and without this
|
|
989
|
+
// they play locomotion at a flat 1.0 while the local player's clips
|
|
990
|
+
// speed-match — visibly different gaits for the same movement.
|
|
991
|
+
const speed = (snapshot as Partial<AdvancedCharacterStateSnapshot>).moveSpeed;
|
|
992
|
+
if (!this.#locomotionProfile || typeof speed !== "number" || this.oneShotActive) return;
|
|
882
993
|
const band = state === "RUN" ? "run" : state === "CROUCH_MOVE" ? "crouch" : state === "WALK" ? "walk" : null;
|
|
883
994
|
if (!band || !this.#prevActionName) return;
|
|
884
995
|
const nominal = this.#locomotionProfile.nominalSpeed?.[band];
|
|
885
996
|
if (!nominal || nominal <= 0) return;
|
|
886
997
|
const limits = this.#locomotionProfile.playbackRate ?? { min: 0.75, max: 1.35 };
|
|
887
|
-
const rate = THREE.MathUtils.clamp(
|
|
998
|
+
const rate = THREE.MathUtils.clamp(speed / nominal, limits.min, limits.max);
|
|
888
999
|
this.#actions.get(this.#prevActionName)?.setEffectiveTimeScale(rate);
|
|
889
1000
|
}
|
|
890
1001
|
}
|
|
@@ -122,6 +122,15 @@ The HUD overlays a **live 3D scene**. The pixels between widgets show the game.
|
|
|
122
122
|
their opaque chrome (frame, brackets, ornament); the plate itself is
|
|
123
123
|
rebuilt at runtime in CSS matching the mockup's tint (see the three layers
|
|
124
124
|
above).
|
|
125
|
+
- **No rectangular backing plates behind bars, digits, or icons — in the
|
|
126
|
+
mockup, in sprites, or in CSS.** The Stage-1 template's widget-construction
|
|
127
|
+
paragraph forbids them at the source (widgets are shaped silhouettes drawn
|
|
128
|
+
over the scene — never delete that paragraph), and the runtime never adds
|
|
129
|
+
one back: ornament lives on the widget's own silhouette. A screen that
|
|
130
|
+
truly needs a plate (a menu sheet, an inventory panel) shapes it from the
|
|
131
|
+
art's real interior with `npx genex ui plate` — never a bare rounded
|
|
132
|
+
rectangle. Measured across 7 genres: without this rule every genre grows
|
|
133
|
+
heavy generic plates behind its bars and digits.
|
|
125
134
|
- **4–7 widgets.** Fewer doesn't read as a HUD; more clutters the screen and
|
|
126
135
|
burns generations.
|
|
127
136
|
- **Every widget needs internal contrast** — a panel fill, outline stroke, or
|
|
@@ -225,22 +234,29 @@ and [references/stage2-prompt-template.md](references/stage2-prompt-template.md)
|
|
|
225
234
|
|
|
226
235
|
**Order of work: kick Stage 1 off as your FIRST action at the UI plan
|
|
227
236
|
gate — the Stage-1 mockup IS the game concept.** There is no separate
|
|
228
|
-
UI-free concept image before it: this
|
|
229
|
-
serves as the user's style
|
|
230
|
-
(`$genex-threejs-game-ui` owns the
|
|
231
|
-
|
|
232
|
-
|
|
237
|
+
UI-free concept image before it: this ONE generation (no candidate variants
|
|
238
|
+
unless the player asks) carries scene + HUD, serves as the user's style
|
|
239
|
+
checkpoint, and anchors all later art (`$genex-threejs-game-ui` owns the
|
|
240
|
+
checkpoint choreography). **The moment the mockup lands, enqueue Stage 2 +
|
|
241
|
+
the menu still + the logotype `--no-wait` IMMEDIATELY — THEN show the
|
|
242
|
+
player the frame and ask keep/change as information, never as a gate.**
|
|
243
|
+
Silence = the concept stands; a "change" answer loops the concept with the
|
|
244
|
+
player's notes and the chain re-runs from the new frame — image-priced,
|
|
245
|
+
cheap by design, so say so in one line and do it. Only the menu VIDEO waits
|
|
246
|
+
(`$genex-ai-menu` owns its event triple). The scene half
|
|
233
247
|
of the prompt is written in TEXT from the game plan — `[GAME_SCENE]` in the
|
|
234
248
|
Stage-1 template: setting, the moment, what the player is doing, lighting.
|
|
235
249
|
**If a concept/reference image already exists — the user's own concept art, or
|
|
236
250
|
a look frame you generated and they approved — anchor Stage 1 to it with
|
|
237
|
-
`--edit <that-url>` so the HUD inherits its exact palette, materials,
|
|
238
|
-
lighting; that anchoring is what makes the final HUD actually match the
|
|
251
|
+
`--edit <that-url-or-file>` so the HUD inherits its exact palette, materials,
|
|
252
|
+
and lighting; that anchoring is what makes the final HUD actually match the
|
|
239
253
|
concept, and skipping it is why a text-only mockup drifts.** A text-only
|
|
240
|
-
Stage 1 is the fallback for when no reference exists.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
254
|
+
Stage 1 is the fallback for when no reference exists. A user-supplied
|
|
255
|
+
reference needs no upload step: `--edit` takes a local file path directly
|
|
256
|
+
(`--edit ./reference.png`, ≤4 MB inlined) — a chat attachment saved to disk
|
|
257
|
+
is a perfectly good anchor, and "I can't feed the local screenshot into the
|
|
258
|
+
art chain" is never true and never a reason to reach for another tool.
|
|
259
|
+
Then write the
|
|
244
260
|
widget layout and wiring code (placement, masked-fill scaffolding,
|
|
245
261
|
plain-CSS placeholder bars) while it renders — the CSS HUD keeps
|
|
246
262
|
the game playable until the sprites land. When a sprite lands it REPLACES
|
|
@@ -250,19 +266,14 @@ is the black-box defect. The mockup is a STYLE anchor
|
|
|
250
266
|
only: the widget set and
|
|
251
267
|
layout come from the element inventory and the game contract — a mechanic
|
|
252
268
|
the mockup invented (a lap counter, a stamina orb) does not enter the HUD,
|
|
253
|
-
and a mechanic it failed to show still does.
|
|
254
|
-
call (`--candidates 2`) and pick the better one: a re-roll costs the whole
|
|
255
|
-
serial chain, a second candidate costs nothing extra in wall-clock. If the
|
|
256
|
-
user's concept-loop feedback rejects the style after Stage 2+ ran,
|
|
257
|
-
re-run from Stage 1 with their notes — cheaper than it sounds (one image
|
|
258
|
-
is the whole concept now), so say so in one line and do it.
|
|
269
|
+
and a mechanic it failed to show still does.
|
|
259
270
|
|
|
260
271
|
```bash
|
|
261
272
|
# Stage 1 — the game CONCEPT: full HUD composited over the game's own scene.
|
|
262
|
-
# TEXT-described; add `--edit <concept-url>` to anchor it to an
|
|
263
|
-
# concept/reference image so the HUD inherits its exact style.
|
|
264
|
-
#
|
|
265
|
-
npx genex image "<filled stage-1 prompt>" --size 2560x1440 --quality high
|
|
273
|
+
# TEXT-described; add `--edit <concept-url-or-local-file>` to anchor it to an
|
|
274
|
+
# existing concept/reference image so the HUD inherits its exact style.
|
|
275
|
+
# ONE concept image; save its URL:
|
|
276
|
+
npx genex image "<filled stage-1 prompt>" --size 2560x1440 --quality high
|
|
266
277
|
|
|
267
278
|
# Stage 2 — deconstruct the mockup into an asset sheet on white:
|
|
268
279
|
npx genex image "<filled stage-2 prompt>" --edit <mockup-url> --quality high
|
|
@@ -319,6 +330,12 @@ To fix ONE sprite, don't re-run the pipeline:
|
|
|
319
330
|
(the canvas sliced it — regenerate the sheet with more margin around every
|
|
320
331
|
element) or looks like a fragment (extreme aspect / near-empty box — try
|
|
321
332
|
`--dilate` to glue split pieces).
|
|
333
|
+
- **Stacked meters cluster.** Meters that sit adjacent/stacked in the mockup
|
|
334
|
+
(HP directly over stamina) tend to come back as one clustered crop on the
|
|
335
|
+
sheet — when your mockup stacks meters, ask the Stage-2 sheet for extra
|
|
336
|
+
whitespace between the STACKED meter cells specifically (measured on a
|
|
337
|
+
live run: the tools hard-refuse the resulting neighbor-in-crop defect, so
|
|
338
|
+
spacing up front saves a sheet re-roll).
|
|
322
339
|
- **gpt-image-2 silently squashes past 3:1** — never request a canvas with
|
|
323
340
|
aspect beyond 3:1. For thin strips (a wide bar frame), generate inside a
|
|
324
341
|
≤3:1 canvas with margins, then `genex ui trim` down to the art.
|
|
@@ -619,10 +636,10 @@ separate concept spend), a couple of minutes each at high quality — budget an
|
|
|
619
636
|
hour end to end, not five minutes. That fits comfortably inside the image rate
|
|
620
637
|
limit; local `genex ui` steps are free and instant. Two rules keep the clock
|
|
621
638
|
honest: the sprite pipeline runs WHILE you build (kick Stage 1 first, code
|
|
622
|
-
against CSS placeholders, swap sprites in as stages land
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
639
|
+
against CSS placeholders, swap sprites in as stages land), and the chain
|
|
640
|
+
never parks behind the keep/change answer — Stage 2, the menu still, and the
|
|
641
|
+
logotype enqueue the moment the mockup lands (the order-of-work rule above);
|
|
642
|
+
a later "change" loops the concept at image prices.
|
|
626
643
|
|
|
627
644
|
## Publish checklist
|
|
628
645
|
|
|
@@ -641,8 +658,9 @@ after a bad single costs the whole chain).
|
|
|
641
658
|
- `npx genex image` — `--size <WxH>` exact pixels (multiples of 16, each side
|
|
642
659
|
≤ 3840, aspect at most 3:1); `--quality <low|medium|high>` (high for the
|
|
643
660
|
mockup/sheet, medium for single sprites); `--candidates <2|3|4>` several
|
|
644
|
-
variants in ONE call (
|
|
645
|
-
|
|
661
|
+
variants in ONE call (only when the player asks for variants — the
|
|
662
|
+
doctrine is ONE concept); `--edit <url|file>` image-to-image edit of an
|
|
663
|
+
R2 URL or a local image file (≤4 MB, inlined); `--clean <url>`
|
|
646
664
|
background removal only; `--remove-bg` chains removal after a
|
|
647
665
|
generation/edit; `--bg-mode <sprite|glyph|sheet>` picks the removal model
|
|
648
666
|
(`glyph` for digits/closed shapes; `--clean` defaults to `sheet`; `matte`
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Stage 1 prompt template — the game concept: full-HUD mockup
|
|
2
2
|
|
|
3
|
-
Fill the
|
|
4
|
-
`npx genex image "<filled prompt>" --size 2560x1440 --quality high
|
|
3
|
+
Fill the placeholders, then pass the whole text as the prompt to
|
|
4
|
+
`npx genex image "<filled prompt>" --size 2560x1440 --quality high`.
|
|
5
|
+
ONE concept image — no candidate variants unless the player asks for them.
|
|
5
6
|
This image IS the game concept — the user's style checkpoint and the anchor
|
|
6
7
|
for all later art — so the scene half deserves the same care as the HUD half.
|
|
7
8
|
|
|
@@ -26,11 +27,35 @@ Placeholders:
|
|
|
26
27
|
rival truck closing on the left, late-afternoon glare"
|
|
27
28
|
- `[STYLE_BRIEF]` — your full 3–5 sentence style brief with the named 4–5 hue
|
|
28
29
|
palette. Do NOT append the per-sprite cutout sentence here — this is a full
|
|
29
|
-
frame, not a cutout.
|
|
30
|
+
frame, not a cutout. **End the brief with a named register anchor**: "in
|
|
31
|
+
the register of <2–3 real games>" from the table below. Anchors set the
|
|
32
|
+
REGISTER (stroke weight, ornament budget, composure) — never copy their
|
|
33
|
+
assets, and never show their names in-game. Pick 2–3; genres between rows
|
|
34
|
+
blend the neighbors.
|
|
35
|
+
|
|
36
|
+
| Genre register | Anchor games (2–3) |
|
|
37
|
+
|---|---|
|
|
38
|
+
| Gothic / soulslike | Bloodborne, Elden Ring |
|
|
39
|
+
| Clean sci-fi / arena shooter | Destiny 2, Titanfall 2 |
|
|
40
|
+
| Arcade racing / rally | Forza Horizon, vintage Baja rally decals |
|
|
41
|
+
| Cozy / farming / life sim | Stardew Valley, Spiritfarer |
|
|
42
|
+
| Analog survival horror | Resident Evil 7, Signalis |
|
|
43
|
+
| Retro pixel | Shovel Knight |
|
|
44
|
+
| Ornate high fantasy RPG | Diablo IV |
|
|
45
|
+
|
|
46
|
+
- `[N]` — the exact count of elements in `[ELEMENT_LIST]`, spelled out in
|
|
47
|
+
the ONLY-these line. The bans in that line are load-bearing: without them
|
|
48
|
+
the model completes the genre's canonical HUD past your list (invented
|
|
49
|
+
kill feeds, leaderboards, timers, extra slots — measured in 5/7 genres).
|
|
30
50
|
- `[ELEMENT_LIST]` — a bullet list of your 4–7 chosen widgets with one-line
|
|
31
51
|
descriptions. List only asset-backed widgets; pure-geometry elements
|
|
32
52
|
(crosshairs, tick rails, plain shapes) are built in code and stay off the
|
|
33
|
-
list.
|
|
53
|
+
list. **Name each element as the shaped object it is** — "bare glowing
|
|
54
|
+
digits with a pip row", "an etched line silhouette", "stencil digits held
|
|
55
|
+
by two rivet brackets". Never "panel" or "plate" unless a physical plate
|
|
56
|
+
IS the art (a diegetic device, a pinned note). **State exact counts**
|
|
57
|
+
("exactly one tool slot", "exactly three gem sockets") — the model honors
|
|
58
|
+
them.
|
|
34
59
|
|
|
35
60
|
## The template
|
|
36
61
|
|
|
@@ -39,10 +64,12 @@ A screenshot of a complete game HUD for [GENRE_BRIEF]. The HUD is composited ove
|
|
|
39
64
|
|
|
40
65
|
Visual style: [STYLE_BRIEF]
|
|
41
66
|
|
|
42
|
-
The HUD includes these elements,
|
|
67
|
+
The HUD includes ONLY these [N] elements and nothing else — do not add any other UI: no kill feed, no leaderboard, no map, no timer, no chat, no crosshair, no extra slots, no duplicates. Arrange them naturally as a real game would lay them out (you decide the layout — do not force a grid, place each element where it makes the HUD readable and combat-ready):
|
|
43
68
|
|
|
44
69
|
[ELEMENT_LIST]
|
|
45
70
|
|
|
71
|
+
Widget construction rules: every widget is a shaped object with its own silhouette — an ornamented frame or emblem drawn directly over the game scene. NO rectangular backing panels behind bars, digits, or icons; no dark filler boxes; the game scene stays visible right up to each widget's frame edge. All ornament and material character lives ON the frame outline itself. Keep the frames detailed and characterful — confident AAA game UI, not a sterile minimal overlay. Size widgets like a shipped game: the HUD hugs the screen edges and no single widget exceeds about one eighth of the frame width. Every meter channel interior reads visibly darker than both its fill and its surrounding frame.
|
|
72
|
+
|
|
46
73
|
This is a real in-game screenshot. Sharp detail on every UI element. No motion blur on the HUD. The HUD is clear and combat-readable. No watermarks. No external annotations.
|
|
47
74
|
|
|
48
75
|
CRITICAL — flat HUD framing: draw every HUD element flat and head-on, parallel to the screen plane, like a 2D overlay painted directly onto the display (orthographic / screen-space UI). The HUD must NOT be tilted, angled, skewed, rotated in 3D, shown in perspective, or made to recede into depth — no isometric interface, no vanishing point on the panels, no 3D-extruded or floating-at-an-angle widgets, no curved/wrapped screen. ONLY the game scene behind the HUD may show 3D depth and perspective; the HUD layer itself is a flat 2D plane with square-on, axis-aligned edges, so each widget can be cleanly cut out as a flat sprite.
|
|
@@ -53,6 +80,16 @@ not deconstruct cleanly (angled panel edges have no clean silhouette, so
|
|
|
53
80
|
Stage 2 produces sliced, skewed cutouts). If the mockup comes back tilted,
|
|
54
81
|
regenerate it before proceeding; do not try to salvage it downstream.
|
|
55
82
|
|
|
83
|
+
**Never delete the widget-construction paragraph either** — it is what keeps
|
|
84
|
+
the mockup from coming back as generic rectangles: without it, every genre
|
|
85
|
+
grows heavy rectangular backing plates behind its bars and digits, and the
|
|
86
|
+
lane's whole value (shaped, characterful chrome) is lost. The channel-contrast
|
|
87
|
+
sentence in it is deliberate phrasing: channels read "visibly darker than
|
|
88
|
+
both their fill and their frame" — a RELATIVE rule that works on light
|
|
89
|
+
palettes too (an absolute "dark" trough fought cozy/pale briefs). Stage 2's
|
|
90
|
+
EMPTY-state law (dark empty tracks on the sheet) is separate and stays
|
|
91
|
+
absolute.
|
|
92
|
+
|
|
56
93
|
One more Stage-1 rule that pays off at mask time: **meter channels must be
|
|
57
94
|
continuous** — never place a label or ornament in the MIDDLE of a fill
|
|
58
95
|
channel (it splits the mask and the fill into fragments); labels sit above or
|
|
@@ -25,7 +25,11 @@ Fill `[ASSET_LIST]`, then pass the whole text as the prompt to
|
|
|
25
25
|
- **requests variant cells for discrete icon counters** — hearts, ammo
|
|
26
26
|
pips, stars ship as repeated sprites, not masks, so their item asks for
|
|
27
27
|
the states as SEPARATE same-size cells ("the heart icon: one full, one
|
|
28
|
-
half, one empty cell") instead of an annotated pair
|
|
28
|
+
half, one empty cell") instead of an annotated pair;
|
|
29
|
+
- **asks for extra whitespace between meters that were STACKED in the
|
|
30
|
+
mockup** ("place the health-bar cells and the stamina-bar cells in
|
|
31
|
+
separate rows with generous spacing") — adjacent meters otherwise cluster
|
|
32
|
+
into one crop and the extraction tools refuse the pair.
|
|
29
33
|
|
|
30
34
|
## The template
|
|
31
35
|
|
|
@@ -180,11 +180,18 @@ cycle watched AS RENDERED. A metadata probe (ffprobe) can't see a seam, a
|
|
|
180
180
|
panel covering the video, or a video that never plays — only watching can.
|
|
181
181
|
|
|
182
182
|
**Work async — the menu must never block the game.** The still is
|
|
183
|
-
`--edit`-anchored to the concept mockup, so it's style-dependent:
|
|
184
|
-
the moment the
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
`--edit`-anchored to the concept mockup, so it's style-dependent: enqueue it
|
|
184
|
+
`--no-wait` **the moment the concept mockup LANDS** — it rides the same
|
|
185
|
+
immediate chain as the HUD Stage-2 sheet and the logotype; the user's
|
|
186
|
+
keep/change answer never gates it (a later "change" just re-edits it against
|
|
187
|
+
the new frame, image-priced). **Only the VIDEO — the expensive item — waits,
|
|
188
|
+
and its trigger is an event, never a clock: fire it at the FIRST of (a) the
|
|
189
|
+
user's yes to the concept, (b) the next `genex preview` push after the still
|
|
190
|
+
has landed (a shipped milestone with the user silent = the pick stands), or
|
|
191
|
+
(c) style art being the only work left. Never fire it while a user objection
|
|
192
|
+
is open** ("change the colors" blocks it until the loop resolves). Ship the
|
|
193
|
+
CSS menu (buttons + title over the frame IMAGE as a static backdrop) and
|
|
194
|
+
swap the `<video>` in when `genex wait` prints the URL. The frame image doubles as the
|
|
188
195
|
**loading screen background** — it exists minutes before the video does (see
|
|
189
196
|
`$genex-threejs-game-ui`'s loader spec). Both assets live in Genex storage
|
|
190
197
|
(R2) — permanent, public, CORS-open; you load them straight from the printed
|
|
@@ -10,6 +10,12 @@ tech demo that boots. You are the director; the other Genex skills are your
|
|
|
10
10
|
specialists. Load only the skills that change the result; never the whole
|
|
11
11
|
pack by default.
|
|
12
12
|
|
|
13
|
+
**After ANY context compaction or session resume**, re-read the game's
|
|
14
|
+
`AGENTS.md` (the Genex build contract block), `DESIGN.md`, and the skill for
|
|
15
|
+
the stage you are executing — never keep building from memory alone. A
|
|
16
|
+
compaction that eats the conversation does not release you from the
|
|
17
|
+
pipeline; those two files are how it comes back.
|
|
18
|
+
|
|
13
19
|
## 1. Check what you can do (once, before planning)
|
|
14
20
|
|
|
15
21
|
Look at your own tool list and note the answers — the rest of this workflow
|
|
@@ -27,6 +33,13 @@ uses them:
|
|
|
27
33
|
Never claim a capability you didn't find, and never stall because one is
|
|
28
34
|
missing.
|
|
29
35
|
|
|
36
|
+
One more thing to note while you're looking: your platform may bundle its own
|
|
37
|
+
image / video / site generation workflows. **They are not part of any Genex
|
|
38
|
+
lane.** All generated art, audio, video, characters, and UI come from `genex`
|
|
39
|
+
commands, unless the player explicitly asks for another tool by name — and a
|
|
40
|
+
local reference image is never a reason to switch tools: `genex image --edit`
|
|
41
|
+
and `--inpaint` take a local file path directly.
|
|
42
|
+
|
|
30
43
|
## 2. Scope check — what is this?
|
|
31
44
|
|
|
32
45
|
- **A new game** → the full flow: contract (§3), teams menu (§4), build order
|
|
@@ -58,7 +71,10 @@ for the user. Ask only what genuinely forks the build:
|
|
|
58
71
|
- any real ambiguity in the request itself.
|
|
59
72
|
|
|
60
73
|
Never ask about SDKs, engines, renderers, file layout, or anything
|
|
61
|
-
technical — those are your decisions.
|
|
74
|
+
technical — those are your decisions. **The silence fallback applies only
|
|
75
|
+
AFTER the questions have been posted in chat.** A request that already names
|
|
76
|
+
the game does not skip the interview — then it's the confirm-pitch round.
|
|
77
|
+
Asking is never optional; waiting is: if the player is silent or has no way
|
|
62
78
|
to answer, proceed on your own stated assumptions and write each one into
|
|
63
79
|
DESIGN.md → Decisions as "assumed — player didn't answer"; the build never
|
|
64
80
|
stalls on the interview.
|
|
@@ -132,7 +148,7 @@ game that needs concrete objects or surfaces, decide a small core set from the
|
|
|
132
148
|
game IDEA — and from the Content lines when there are any (locations and the
|
|
133
149
|
enemy roster name the set) — and put it in the Assets table up front. This set
|
|
134
150
|
is concept-INDEPENDENT (prompted from the idea, not the concept image, and it
|
|
135
|
-
mostly survives a style change), so it
|
|
151
|
+
mostly survives a style change), so it never waits on the concept at all. Each
|
|
136
152
|
`npx genex` job is an independent ~1-minute render: launch them concurrently
|
|
137
153
|
in the background (`--no-wait`), scaffold the scene while they run, and wire
|
|
138
154
|
each in as it lands, with a procedural placeholder until then:
|
|
@@ -164,11 +180,18 @@ The mandatory rows, in order, each with its one "done when" line:
|
|
|
164
180
|
shows; the tier is what keeps a phone boot alive.
|
|
165
181
|
3. **UI plan gate** — `$genex-threejs-game-ui`, every game: the screen
|
|
166
182
|
inventory, one shared style brief, 2–3 AAA references, the menu archetype,
|
|
167
|
-
then
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
183
|
+
then ONE concept image with its full HUD already on it (no candidate
|
|
184
|
+
variants unless the player asks). **The moment it lands: decide the HUD
|
|
185
|
+
lane (the lane beat below), enqueue the Stage-2 sheet + the menu still +
|
|
186
|
+
the logotype `--no-wait` IMMEDIATELY, and only THEN show the player the
|
|
187
|
+
frame and ask keep-or-change with your question tool — as information,
|
|
188
|
+
never as a gate.** Silence = the concept stands; a "change" answer loops
|
|
189
|
+
the concept with the player's notes and the chain re-runs from the new
|
|
190
|
+
frame (image-priced — cheap by design). Only the menu VIDEO waits, for
|
|
191
|
+
the FIRST of: the player's yes · the next `genex preview` after the menu
|
|
192
|
+
still landed · style work being the only work left — and it never fires
|
|
193
|
+
while a player objection is open. Done when: the sheet, still, and
|
|
194
|
+
logotype are enqueued and the frame is in front of the player.
|
|
172
195
|
4. **Content contract when the request names plural content** — quests,
|
|
173
196
|
enemies, bosses, locations, spells, items, or a content genre (an RPG, an
|
|
174
197
|
adventure, an open world, a story game) — `$genex-threejs-game-content`.
|
|
@@ -188,10 +211,26 @@ The mandatory rows, in order, each with its one "done when" line:
|
|
|
188
211
|
declares its `genex.matchmaking` block before preview. Done when: the
|
|
189
212
|
model, start rule, and late-join behavior are stated in DESIGN.md and the
|
|
190
213
|
netcode feel gate ran before handoff.
|
|
191
|
-
7. **Ship the
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
214
|
+
7. **Ship the playable v0 and preview it.** The scaffold prompt owns the
|
|
215
|
+
player-facing milestones and links — don't restate them; obey them. Done
|
|
216
|
+
when: the v0 loop is genuinely playable and the player has their draft
|
|
217
|
+
page link.
|
|
218
|
+
|
|
219
|
+
**The HUD lane — the concept decides it, and DESIGN.md records it.** You
|
|
220
|
+
make this call as the art director the moment the mockup lands: ornate /
|
|
221
|
+
painterly / material widget chrome (carved bone, etched metal, glowing
|
|
222
|
+
runes, brushed gold) → **sprites** — the `$genex-ai-hud` pipeline is
|
|
223
|
+
mandatory. Chrome that is flat geometry + typography, where a CSS rebuild
|
|
224
|
+
would be screenshot-indistinguishable from the mockup → **CSS allowed**,
|
|
225
|
+
styled from the brief. The litmus test: would a screenshot of the CSS
|
|
226
|
+
rebuild pass for the mockup at a glance? Unsure or ambiguous → sprites.
|
|
227
|
+
Record it as one DESIGN.md line — `HUD lane: sprites (…)` or
|
|
228
|
+
`HUD lane: CSS (…, one-line justification)` — the preview preflight checks
|
|
229
|
+
for it. In BOTH lanes: micro-text (damage numbers, timers, ammo digits)
|
|
230
|
+
stays HTML text in the brief's font, and no rectangular backing plates
|
|
231
|
+
behind bars, digits, or icons — ever (a truly needed shaped plate comes
|
|
232
|
+
from `npx genex ui plate`). Only the player may decline the generated HUD,
|
|
233
|
+
and the player's explicit lane request wins in both directions.
|
|
195
234
|
|
|
196
235
|
Two rules for every game that moves (decide both before building, state them
|
|
197
236
|
in DESIGN.md):
|
|
@@ -227,10 +266,12 @@ agent: you stay the director.
|
|
|
227
266
|
rule is anti-collision, never a reason to serialize work.
|
|
228
267
|
- You stay the integrator and the only writer of shared files (boot, main
|
|
229
268
|
loop, netcode). Workers never spawn workers — one level deep, always.
|
|
230
|
-
- Concept-DEPENDENT rows (the HUD chain, style-matched art)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
269
|
+
- Concept-DEPENDENT rows (the HUD chain, style-matched art) launch the
|
|
270
|
+
moment the concept LANDS — the Stage-2 chain enqueues immediately (§5.3);
|
|
271
|
+
only the menu video waits for its event triple. Concept-INDEPENDENT rows
|
|
272
|
+
(world/terrain, content data, enemies, asset wiring) launch immediately
|
|
273
|
+
either way. Typing a big game alone, line by line, is how sessions run
|
|
274
|
+
out before the world exists.
|
|
234
275
|
- Give each worker everything by path: the `DESIGN.md` path, its Modules row,
|
|
235
276
|
and the skill files it needs (skills live in this project —
|
|
236
277
|
`.claude/skills/<name>/SKILL.md`, `.codex/skills/…`, or `.cursor/skills/…`,
|