@genex-ai/cli-demo 0.88.0-dev.219 → 0.91.0-dev.228
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 +4 -1
- package/dist/index.js +317 -67
- package/package.json +1 -1
- package/templates/controllers/NOTICE.md +6 -4
- package/templates/controllers/character/player-character.ts +217 -0
- package/templates/skills/genex-ai-character/SKILL.md +54 -8
- package/templates/skills/genex-ai-image/SKILL.md +5 -1
- package/templates/skills/genex-ai-menu/SKILL.md +14 -6
- package/templates/skills/genex-ai-skybox/SKILL.md +19 -6
- package/templates/skills/genex-game-director/SKILL.md +29 -11
- package/templates/skills/genex-game-director/references/routing-map.md +3 -3
- package/templates/skills/genex-threejs-character-controller/SKILL.md +103 -51
- package/templates/skills/genex-threejs-character-controller/references/animations.md +62 -50
- package/templates/skills/genex-threejs-embed-auth/SKILL.md +5 -3
- package/templates/skills/genex-threejs-multiplayer/SKILL.md +10 -8
- package/templates/skills/genex-threejs-multiplayer/references/host-physics.md +8 -7
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// The player's body, resolved once: the game's OWN generated character when
|
|
3
|
+
// this game has one, the visiting player's profile avatar when it does not.
|
|
4
|
+
//
|
|
5
|
+
// WHY THIS EXISTS: before it, the two lanes were different code — different
|
|
6
|
+
// loader, different CharacterAnimations arguments, and a per-frame call that
|
|
7
|
+
// appeared and disappeared. So the boot path written at hour 0 (the avatar,
|
|
8
|
+
// the only lane that works before a character exists) had to be REWRITTEN at
|
|
9
|
+
// hour 2 when the generated character landed, by an agent low on context with
|
|
10
|
+
// ten things half-done. It wasn't, and games shipped wearing the platform's
|
|
11
|
+
// stock avatar instead of the character they were designed around. ONE shape
|
|
12
|
+
// for both lanes means the character arriving later is a file drop, not an
|
|
13
|
+
// edit — `genex controller character --character <id>` writes the manifest and
|
|
14
|
+
// the next reload picks it up.
|
|
15
|
+
//
|
|
16
|
+
// The generated character is the default for any game where a human body
|
|
17
|
+
// appears on screen (first-person included — remotes, a look-down body, a
|
|
18
|
+
// shadow, a menu portrait). The avatar is the FALLBACK: a declared temporary
|
|
19
|
+
// body while the character renders, or the stand-in when generation genuinely
|
|
20
|
+
// could not happen (out of credits, failed, unverified).
|
|
21
|
+
import * as THREE from "three";
|
|
22
|
+
import * as SkeletonUtils from "three/addons/utils/SkeletonUtils.js";
|
|
23
|
+
import { VRMUtils } from "@pixiv/three-vrm";
|
|
24
|
+
import type { LocomotionProfile } from "./character-animations.ts";
|
|
25
|
+
import { loadCharacterClips } from "./animation-packs.ts";
|
|
26
|
+
import {
|
|
27
|
+
loadMeshyCharacter,
|
|
28
|
+
type LoadMeshyCharacterOptions,
|
|
29
|
+
type MeshyCharacter,
|
|
30
|
+
} from "./meshy/meshy-loader.ts";
|
|
31
|
+
import { loadVrm, loadVrmCloneBase } from "./vrm/vrm-loader.ts";
|
|
32
|
+
|
|
33
|
+
/** "generated" = this game's own themed character; "avatar" = the fallback. */
|
|
34
|
+
export type PlayerCharacterKind = "generated" | "avatar";
|
|
35
|
+
|
|
36
|
+
export interface PlayerCharacter {
|
|
37
|
+
kind: PlayerCharacterKind;
|
|
38
|
+
/** Renderable root — parent it under `character.root`. */
|
|
39
|
+
scene: THREE.Group;
|
|
40
|
+
/** Clips for CharacterAnimations (exact-rig generated clips, or retargeted UAL). */
|
|
41
|
+
clips: THREE.AnimationClip[];
|
|
42
|
+
/** Present only in the generated lane; pass straight through to CharacterAnimations. */
|
|
43
|
+
locomotionProfile?: LocomotionProfile;
|
|
44
|
+
/** Once per RENDER frame, after physics.step(). Ticks the VRM humanoid + spring
|
|
45
|
+
* bones in the fallback lane; a no-op for native rigs. Always call it — that
|
|
46
|
+
* is the point: the boot code never changes when the lane does. */
|
|
47
|
+
update(delta: number): void;
|
|
48
|
+
dispose(): void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** A REMOTE player's body: visual only, so no per-frame update and no physics. */
|
|
52
|
+
export interface RemotePlayerCharacter {
|
|
53
|
+
kind: PlayerCharacterKind;
|
|
54
|
+
scene: THREE.Group;
|
|
55
|
+
clips: THREE.AnimationClip[];
|
|
56
|
+
locomotionProfile?: LocomotionProfile;
|
|
57
|
+
/** Detach on 'leave'. GPU resources belong to the shared base, never a clone. */
|
|
58
|
+
dispose(): void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface LoadPlayerCharacterOptions {
|
|
62
|
+
/** The visiting player's VRM (`user.avatarUrl` from the embed identity). Fallback lane only. */
|
|
63
|
+
avatarUrl?: string | null;
|
|
64
|
+
/** Asset base. Default "./assets/". */
|
|
65
|
+
base?: string;
|
|
66
|
+
/** Manifest override. Default `${base}meshy-character.json`. */
|
|
67
|
+
manifestUrl?: string;
|
|
68
|
+
/** Passed through to the Meshy loader — the quality kit's decoder-wired loader
|
|
69
|
+
* and per-tier model rung ladder (`$genex-threejs-adaptive-quality`). */
|
|
70
|
+
meshy?: LoadMeshyCharacterOptions;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Does this game have a generated character? A dev server answers HTTP 200 with
|
|
75
|
+
* `index.html` for ANY unknown path, so `res.ok` is not evidence — the JSON
|
|
76
|
+
* parse plus the manifest's own identity fields are. False is the quiet,
|
|
77
|
+
* expected answer for a game that has not generated its character yet.
|
|
78
|
+
*
|
|
79
|
+
* Exported for tests: this probe is the whole routing decision.
|
|
80
|
+
*/
|
|
81
|
+
export async function probeGeneratedCharacter(url: string): Promise<boolean> {
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetch(url, { cache: "no-store" });
|
|
84
|
+
if (!response.ok) return false;
|
|
85
|
+
const manifest = (await response.json()) as { schema?: number; rig?: string } | null;
|
|
86
|
+
return manifest?.schema === 1 && manifest.rig === "meshy-biped";
|
|
87
|
+
} catch {
|
|
88
|
+
return false; // absent, or the dev server's index.html — same meaning here
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// One PARSED base per manifest URL — the clone source for remote players, so N
|
|
93
|
+
// remotes cost ~1 character of GPU memory instead of N (the same trade the VRM
|
|
94
|
+
// lane's loadVrmCloneBase makes). Never animated; clones carry their own mixer.
|
|
95
|
+
const generatedBaseCache = new Map<string, Promise<MeshyCharacter>>();
|
|
96
|
+
// Retargeted fallback clips per (avatar URL + asset base) — retargeting is per
|
|
97
|
+
// humanoid rig, and every clone of one URL shares one base rig.
|
|
98
|
+
const fallbackClipsCache = new Map<string, Promise<THREE.AnimationClip[]>>();
|
|
99
|
+
|
|
100
|
+
function generatedBase(url: string, options?: LoadMeshyCharacterOptions): Promise<MeshyCharacter> {
|
|
101
|
+
let cached = generatedBaseCache.get(url);
|
|
102
|
+
if (!cached) {
|
|
103
|
+
cached = loadMeshyCharacter(url, options);
|
|
104
|
+
cached.catch(() => generatedBaseCache.delete(url)); // failures retry next call
|
|
105
|
+
generatedBaseCache.set(url, cached);
|
|
106
|
+
}
|
|
107
|
+
return cached;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The LOCAL player's body. Wire it once at boot and never touch it again:
|
|
112
|
+
*
|
|
113
|
+
* ```ts
|
|
114
|
+
* const player = await loadPlayerCharacter({ avatarUrl: user.avatarUrl });
|
|
115
|
+
* const fit = capsuleFromModel(player.scene);
|
|
116
|
+
* character.root.add(player.scene);
|
|
117
|
+
* player.scene.position.y = fit.modelOffsetY;
|
|
118
|
+
* const anims = new CharacterAnimations(player.scene, player.clips, {
|
|
119
|
+
* locomotionProfile: player.locomotionProfile,
|
|
120
|
+
* });
|
|
121
|
+
* // per render frame: anims.update(character, delta); player.update(delta);
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export async function loadPlayerCharacter(
|
|
125
|
+
options: LoadPlayerCharacterOptions = {},
|
|
126
|
+
): Promise<PlayerCharacter> {
|
|
127
|
+
const base = options.base ?? "./assets/";
|
|
128
|
+
const manifestUrl = options.manifestUrl ?? `${base}meshy-character.json`;
|
|
129
|
+
const fallbackUrl = `${base}avatar.vrm`;
|
|
130
|
+
|
|
131
|
+
if (await probeGeneratedCharacter(manifestUrl)) {
|
|
132
|
+
try {
|
|
133
|
+
const native = await loadMeshyCharacter(manifestUrl, options.meshy);
|
|
134
|
+
return {
|
|
135
|
+
kind: "generated",
|
|
136
|
+
scene: native.scene,
|
|
137
|
+
clips: native.clips,
|
|
138
|
+
locomotionProfile: native.locomotionProfile,
|
|
139
|
+
update: (delta) => native.update(delta),
|
|
140
|
+
dispose: () => native.dispose(),
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
// LOUD: this game HAS a character and it failed — a dead R2 URL or a rig
|
|
144
|
+
// mismatch, not the ordinary "no character yet" path above.
|
|
145
|
+
console.warn(
|
|
146
|
+
"[player-character] the generated character failed to load — falling back to the avatar",
|
|
147
|
+
error,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const { scene, vrm } = await loadVrm(options.avatarUrl || fallbackUrl).catch(() =>
|
|
153
|
+
loadVrm(fallbackUrl),
|
|
154
|
+
);
|
|
155
|
+
const clips = await loadCharacterClips(vrm, { base });
|
|
156
|
+
return {
|
|
157
|
+
kind: "avatar",
|
|
158
|
+
scene,
|
|
159
|
+
clips,
|
|
160
|
+
update: (delta) => vrm.update(delta),
|
|
161
|
+
dispose: () => VRMUtils.deepDispose(scene),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* A REMOTE player's body. In a game with a generated character EVERY player
|
|
167
|
+
* wears it — the game should look like the game that was designed, and a mixed
|
|
168
|
+
* roster (one themed knight plus three stock avatars) is the same incoherence
|
|
169
|
+
* as capsule-and-cone remotes. Without a generated character each remote keeps
|
|
170
|
+
* their own `avatarUrl`, which is the right look for a game with no themed cast.
|
|
171
|
+
*
|
|
172
|
+
* Visual only: no physics body, no controller, no per-frame update. Clones share
|
|
173
|
+
* the base's geometry/materials/textures, so `dispose()` detaches — it never
|
|
174
|
+
* disposes GPU resources the base and every other clone still use.
|
|
175
|
+
*/
|
|
176
|
+
export async function loadRemotePlayerCharacter(
|
|
177
|
+
options: LoadPlayerCharacterOptions = {},
|
|
178
|
+
): Promise<RemotePlayerCharacter> {
|
|
179
|
+
const base = options.base ?? "./assets/";
|
|
180
|
+
const manifestUrl = options.manifestUrl ?? `${base}meshy-character.json`;
|
|
181
|
+
const fallbackUrl = `${base}avatar.vrm`;
|
|
182
|
+
|
|
183
|
+
if (await probeGeneratedCharacter(manifestUrl)) {
|
|
184
|
+
try {
|
|
185
|
+
const shared = await generatedBase(manifestUrl, options.meshy);
|
|
186
|
+
const scene = SkeletonUtils.clone(shared.scene) as THREE.Group;
|
|
187
|
+
return {
|
|
188
|
+
kind: "generated",
|
|
189
|
+
scene,
|
|
190
|
+
clips: shared.clips,
|
|
191
|
+
locomotionProfile: shared.locomotionProfile,
|
|
192
|
+
dispose: () => scene.removeFromParent(),
|
|
193
|
+
};
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.warn(
|
|
196
|
+
"[player-character] the generated character failed to load for a remote player — falling back to their avatar",
|
|
197
|
+
error,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const requested = options.avatarUrl || fallbackUrl;
|
|
203
|
+
const url = await loadVrmCloneBase(requested).then(
|
|
204
|
+
() => requested,
|
|
205
|
+
() => fallbackUrl,
|
|
206
|
+
);
|
|
207
|
+
const shared = await loadVrmCloneBase(url);
|
|
208
|
+
const clipsKey = `${url}${base}`;
|
|
209
|
+
let clips = fallbackClipsCache.get(clipsKey);
|
|
210
|
+
if (!clips) {
|
|
211
|
+
clips = loadCharacterClips(shared.vrm, { base });
|
|
212
|
+
clips.catch(() => fallbackClipsCache.delete(clipsKey));
|
|
213
|
+
fallbackClipsCache.set(clipsKey, clips);
|
|
214
|
+
}
|
|
215
|
+
const scene = SkeletonUtils.clone(shared.scene) as THREE.Group;
|
|
216
|
+
return { kind: "avatar", scene, clips: await clips, dispose: () => scene.removeFromParent() };
|
|
217
|
+
}
|
|
@@ -1,16 +1,39 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: genex-ai-character
|
|
3
|
-
description: Generate a controller-ready Meshy humanoid with Genex, search the committed Meshy animation catalog by gameplay intent, add exact same-rig actions, and install the shared physics controller's Meshy-native adapter. Use for the game's
|
|
3
|
+
description: Generate a controller-ready Meshy humanoid with Genex, search the committed Meshy animation catalog by gameplay intent, add exact same-rig actions, and install the shared physics controller's Meshy-native adapter. Use for the game's own character — the player's body in every game where a human body appears on screen — and for motion the bundled UAL library doesn't cover.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Genex AI Character
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
**The game's own generated character IS the player's body.** Not the platform's
|
|
9
|
+
profile avatar — that is the fallback. The rule applies wherever a human body
|
|
10
|
+
appears on screen: third-person obviously, and first-person too, the moment
|
|
11
|
+
co-op or versus remotes, a look-down body, a shadow, a death or spectator
|
|
12
|
+
camera, or a menu portrait shows one. "The camera is in the head" is not an
|
|
13
|
+
exemption; **"no human body ever appears in this game" is.** A game whose
|
|
14
|
+
player is genuinely not a person — a car, a ship, an RTS cursor, a puzzle
|
|
15
|
+
board — generates that object with `npx genex model` instead.
|
|
16
|
+
|
|
17
|
+
**Start it EARLY** — enqueue the character right after the design interview,
|
|
18
|
+
alongside the Stage-1 HUD concept, not as an afterthought once the world is
|
|
19
|
+
built. Its concept candidates ride the same review beat as the HUD concept, and
|
|
20
|
+
firing at minute 0 means it lands around the v0 preview instead of long after
|
|
21
|
+
it.
|
|
22
|
+
|
|
23
|
+
Until it lands, the player's body is the profile VRM avatar. That is a fallback
|
|
24
|
+
in two shapes and both are spoken aloud: a **temporary** body while the
|
|
25
|
+
character renders (say so in one plain line — the avatar is fully textured and
|
|
26
|
+
animated, so nothing on screen will look unfinished enough to remind you), or
|
|
27
|
+
the **stand-in** when generation genuinely could not happen (out of credits,
|
|
28
|
+
failed, email unverified) — then record `Player character: VRM — <reason>` in
|
|
29
|
+
DESIGN.md. Never let it become the game's character by default.
|
|
30
|
+
|
|
31
|
+
Both lanes use the same ECCTRL-derived Rapier controller, camera, inputs,
|
|
32
|
+
crossfades, transition rules, and multiplayer authority contract — and the
|
|
33
|
+
same one-call boot path (`loadPlayerCharacter`, in
|
|
34
|
+
`$genex-threejs-character-controller`), so when the character lands mid-build,
|
|
35
|
+
`npx genex controller character --character <id>` is the whole switch. There is
|
|
36
|
+
no code to rewrite, which is exactly why there is no excuse to skip it.
|
|
14
37
|
|
|
15
38
|
**Two lanes, two approval shapes:**
|
|
16
39
|
|
|
@@ -231,4 +254,27 @@ durations, nominal speeds, trajectories, and gameplay requirements.
|
|
|
231
254
|
For multiplayer, only the owning client advances the dynamic body and publishes
|
|
232
255
|
its pose plus compact animation state. Remote characters are visual-only: they
|
|
233
256
|
interpolate the owner's transform and replay the matching clip progress. Never
|
|
234
|
-
run a motion driver or a second physics controller for a remote player.
|
|
257
|
+
run a motion driver or a second physics controller for a remote player. In a
|
|
258
|
+
game with a generated character, every remote wears it — one
|
|
259
|
+
`loadRemotePlayerCharacter` call, one shared parsed base, N cheap clones.
|
|
260
|
+
|
|
261
|
+
## Troubleshooting
|
|
262
|
+
|
|
263
|
+
- **"Not authorized"** — run `npx genex init` first (in the project — it resolves this project's own CLI) (it writes your `GENEX_TOKEN`).
|
|
264
|
+
- **"Out of credits" (`insufficient_credits`)** — the account has no credits left for
|
|
265
|
+
this character stage. Tell the user the facts the CLI printed: their balance, this
|
|
266
|
+
stage's cost, and when their credits refill. Then keep the player on the profile
|
|
267
|
+
VRM avatar (the fallback lane — no code change, it is already what the game
|
|
268
|
+
loads), tell them in one plain line that the game is wearing the platform avatar
|
|
269
|
+
because the character couldn't be generated, record
|
|
270
|
+
`Player character: VRM — out of credits` in DESIGN.md, and mark the spot with
|
|
271
|
+
`// TODO(genex): regenerate when credits refill`. Do not stop the session over
|
|
272
|
+
this, and do not hand-build a stand-in humanoid.
|
|
273
|
+
- **"Email not verified" (`email_verification_required`)** — generation credits
|
|
274
|
+
unlock after the account's email is verified. Give the user the verify link the
|
|
275
|
+
CLI printed, wait for them to confirm, then re-run the command.
|
|
276
|
+
- **The character is stuck mid-lane** — each stage is a separate generation. `npx
|
|
277
|
+
genex wait --all` prints one line per job; a failed `preview` or `finalize` is
|
|
278
|
+
re-run from the id of the stage before it, never from the beginning.
|
|
279
|
+
- **The rig or a pose is bad** — reject and regenerate. Never repair limbs at
|
|
280
|
+
runtime; validation never rewrites limb animation.
|
|
@@ -213,7 +213,11 @@ See `$genex-threejs-multiplayer` for the `shared` channel rules and the room API
|
|
|
213
213
|
still re-renders and alpha is destroyed either way: after any masked edit,
|
|
214
214
|
re-run `--clean`/re-extraction downstream. A targeting scope, not a pixel
|
|
215
215
|
freeze.
|
|
216
|
-
- `--clean <url>` — ML background removal of THAT image only (prompt recorded,
|
|
216
|
+
- `--clean <url>` — ML background removal of THAT image only (prompt recorded,
|
|
217
|
+
unused). **For an image that HAS a background.** Running it over a
|
|
218
|
+
`--transparent` result re-keys edges that were already correct and can spray
|
|
219
|
+
colour speckle across the rim and into the art — `ui trim`/`extract`/`audit`
|
|
220
|
+
refuse that output, and nothing repairs it but going back to the original.
|
|
217
221
|
- `--upscale <url>` — 2x utility upscale of THAT image (prompt recorded,
|
|
218
222
|
unused; ~2-credit class). Use before printing/large billboards, not by
|
|
219
223
|
default.
|
|
@@ -373,12 +373,20 @@ not):
|
|
|
373
373
|
- **Logotype (default yes):**
|
|
374
374
|
`npx genex image "the word 'EMBERFALL' as an ornate engraved game logo, <style brief>" --transparent`
|
|
375
375
|
— the wordmark alone on a transparent background, in the brief's display
|
|
376
|
-
register, no extra text or scenery.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
376
|
+
register, no extra text or scenery. Then trim with `npx genex ui trim`.
|
|
377
|
+
**`--transparent` already returns a finished cutout — a `--clean` pass is a
|
|
378
|
+
REPAIR for a defect you can see, never routine polish.** Re-clean ONLY if
|
|
379
|
+
the letter counters came out filled: `--bg-mode glyph` for hard flat
|
|
380
|
+
shapes, `--bg-mode matte` for soft metallic, chromed, or glowing edges
|
|
381
|
+
(most ornate wordmarks are soft edges — `glyph` is the wrong cutter for
|
|
382
|
+
them). Then VERIFY over a bright test background, looking for BOTH
|
|
383
|
+
failures: **(1)** an opaque disc or plate behind the mark — that plate
|
|
384
|
+
ships as a hole punched in your key art; **(2)** coloured speckle or a
|
|
385
|
+
rainbow rim along the letter edges — that means the clean pass DAMAGED the
|
|
386
|
+
art, so go back to the `--transparent` original and ship that. Running a
|
|
387
|
+
background remover over an image that was already transparent re-keys every
|
|
388
|
+
edge it should have left alone; `ui trim`/`extract`/`audit` now refuse that
|
|
389
|
+
output outright, and nothing downstream repairs it. Short names (one or two
|
|
382
390
|
words) come out best. Wire it as the menu title AND the loader mark; the
|
|
383
391
|
DOM keeps an accessible text fallback (`aria-label` or visually-hidden
|
|
384
392
|
text).
|
|
@@ -23,12 +23,25 @@ towers, or any object painted into the sky render at infinite distance: they
|
|
|
23
23
|
never get closer as the player moves, they sit at the wrong parallax against
|
|
24
24
|
the real 3D world, and they read as broken the moment the camera strafes.
|
|
25
25
|
Structures belong in the scene as geometry (`npx genex model`); the sky
|
|
26
|
-
carries atmosphere — light, weather, clouds, haze, stars.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
carries atmosphere — light, weather, clouds, haze, stars.
|
|
27
|
+
|
|
28
|
+
**Writing "no cathedral" does not rescue a prompt that also says "cathedral".**
|
|
29
|
+
Image models are steered by the nouns they ARE given; a ban loses to any
|
|
30
|
+
positive mention of the same thing in the same prompt. A real pilot asked for
|
|
31
|
+
a *"cinematic dark anime gothic atmosphere, ruined cathedral courtyard mood,
|
|
32
|
+
**no buildings or structures**"* — and got a full gothic cathedral with a
|
|
33
|
+
courtyard balustrade and a baked ground plane. Five negations in that prompt,
|
|
34
|
+
zero effect. So never name a structure in a skybox prompt **even to exclude
|
|
35
|
+
it**: describe only light, colour, cloud, weather and time of day.
|
|
36
|
+
|
|
37
|
+
The CLI enforces this both ways. It appends a closed-positive environment
|
|
38
|
+
clause to every prompt (you'll see `↳ environment-only guard applied`; the
|
|
39
|
+
full final prompt is stored with the generation), and it **refuses outright,
|
|
40
|
+
before spending any credits**, when the prompt itself names a structure or
|
|
41
|
+
object. Landforms are fine — "golden hour over misty mountains", "a distant
|
|
42
|
+
sea horizon", "towering cumulonimbus" all pass. If you genuinely need content
|
|
43
|
+
baked into the sky — e.g. a space station panorama for a scene with no world
|
|
44
|
+
geometry — pass `--raw` to send your prompt exactly as written.
|
|
32
45
|
|
|
33
46
|
## Run
|
|
34
47
|
|
|
@@ -318,17 +318,35 @@ way this skill is worn by the main agent: you stay the director.
|
|
|
318
318
|
holding the only free slot"). The table is accountability, not ceremony —
|
|
319
319
|
inline can be the right call.
|
|
320
320
|
|
|
321
|
-
## 7. The game's character (Meshy) —
|
|
322
|
-
|
|
323
|
-
**
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
321
|
+
## 7. The game's character (Meshy) — the player's body
|
|
322
|
+
|
|
323
|
+
**The game's own generated character IS the player's body**, wherever a
|
|
324
|
+
human body appears on screen. Third-person obviously; first-person too, the
|
|
325
|
+
moment remotes, a look-down body, a shadow, a death or spectator camera, or
|
|
326
|
+
a menu portrait shows one. "The camera is in the head" is not an exemption —
|
|
327
|
+
**"no human body ever appears in this game" is**, and a game whose player is
|
|
328
|
+
genuinely not a person (a car, a ship, an RTS cursor, a board) generates
|
|
329
|
+
that object with `npx genex model` instead.
|
|
330
|
+
|
|
331
|
+
Put its row in the Assets table up front and **enqueue it with your first
|
|
332
|
+
art actions** — its concepts ride the same review beat as the Stage-1 HUD
|
|
333
|
+
concept, so firing at minute 0 lands it around the v0 preview instead of
|
|
334
|
+
after it. `npx genex controller character` installs the controller and the
|
|
335
|
+
fallback body in one command; the boot path is written once and never
|
|
336
|
+
rewritten (`loadPlayerCharacter` — see
|
|
337
|
+
`$genex-threejs-character-controller`), so when the character lands,
|
|
338
|
+
`npx genex controller character --character <id>` is the entire switch.
|
|
339
|
+
|
|
340
|
+
The profile VRM avatar is the FALLBACK, in two shapes and both spoken
|
|
341
|
+
aloud: a temporary body while the character renders (say so plainly — it's
|
|
342
|
+
a fully textured animated humanoid, so nothing on screen will look
|
|
343
|
+
unfinished enough to remind you), or the stand-in when generation genuinely
|
|
344
|
+
could not happen (out of credits, failed, unverified), recorded in DESIGN.md
|
|
345
|
+
as `Player character: VRM — <reason>`. A capsule or hand-built primitive
|
|
346
|
+
standing in for a person is never a shipped state, for the local player or a
|
|
347
|
+
remote one. In a game with a generated character, every remote wears it —
|
|
348
|
+
a mixed roster of one themed hero plus stock avatars is the same incoherence
|
|
349
|
+
as capsule-and-cone remotes.
|
|
332
350
|
|
|
333
351
|
**The default lane has ONE user stop, and it rides the concept review.**
|
|
334
352
|
When the user names a visual reference, inspect references before writing
|
|
@@ -20,9 +20,9 @@ Three.js release or branch, and do not blindly copy demo architecture.
|
|
|
20
20
|
| Work needed | Load |
|
|
21
21
|
| --- | --- |
|
|
22
22
|
| shot composition, chase/side/orbit rigs, camera handoffs, projection ownership, pointer look, mouse-aimed action (shooter, FPS/first-person, sniper, turret, crosshair/reticle), mouse-look, hand-rolled steering/pan/look input signs (screen-direction contract), floating origins | `$genex-threejs-camera-direction` |
|
|
23
|
-
| on-foot player movement: walk/run/jump/crouch, third-person character, slopes, stairs, moving platforms,
|
|
24
|
-
| the game's
|
|
25
|
-
| remote player bodies in multiplayer — NEVER hand-built primitives: the
|
|
23
|
+
| on-foot player movement: walk/run/jump/crouch, third-person character, slopes, stairs, moving platforms, the player's body loader, directional locomotion, transitions, action motion | `$genex-threejs-character-controller` |
|
|
24
|
+
| the game's own generated character — the player's BODY wherever a human body appears on screen (first-person included; the exemption is "no human body ever appears", not "the camera is in the head"), enqueued with the first art actions; one user stop riding the concept review, owner-ratified auto-pick on silence (director §7) — or Meshy animation coverage beyond UAL: reference-informed A-pose concepts, exact action IDs, same-rig adapter | `$genex-ai-character` + `$genex-threejs-character-controller` |
|
|
25
|
+
| remote player bodies in multiplayer — NEVER hand-built primitives: the game's generated character when it has one (everyone wears it), the player's `p.avatarUrl` VRM only when it doesn't | `$genex-threejs-multiplayer` + `$genex-threejs-character-controller` |
|
|
26
26
|
| the game has enemies, NPCs, or creatures — **load whenever an enemy roster exists**: rigged bipeds via `npx genex creature`, static + procedural motion for other body shapes, plus the mechanical floor every enemy owes (collider, verified facing, hit reaction, death moment) | `$genex-threejs-creatures` |
|
|
27
27
|
| the player drives or flies something: cars, drones, vehicle physics, gearbox, enter/exit between character and vehicle | `$genex-threejs-vehicle-controllers` |
|
|
28
28
|
| playable on phones: touch/mobile input for any game — joystick, virtual buttons, drag zones, per-genre touch recipes, rotate-device overlay — wired by default for every NEW game when a recipe fits (skip with a one-line reason) | `$genex-threejs-touch-controls` |
|