@genex-ai/cli-demo 0.36.0 → 0.38.0
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 +11 -5
- package/dist/index.js +382 -28
- package/package.json +1 -1
- package/templates/controllers/NOTICE.md +8 -5
- package/templates/controllers/assets/animation-library.glb +0 -0
- package/templates/controllers/assets/anims-manifest.json +1306 -0
- package/templates/controllers/character/animation-packs.ts +70 -0
- package/templates/controllers/character/character-animations.ts +51 -8
- package/templates/controllers/character/character-controller.ts +153 -5
- package/templates/controllers/character/keyboard-input.ts +12 -1
- package/templates/controllers/character/presets.ts +19 -1
- package/templates/controllers/character/vrm/foot-ik.ts +71 -15
- package/templates/controllers/character/vrm/vrm-retarget.ts +72 -12
- package/templates/skills/genex-ai-image/SKILL.md +181 -0
- package/templates/skills/genex-ai-video/SKILL.md +148 -0
- package/templates/skills/genex-getting-started/SKILL.md +4 -1
- package/templates/skills/genex-threejs-character-controller/SKILL.md +55 -22
- package/templates/skills/genex-threejs-character-controller/references/animations.md +91 -46
- package/templates/skills/genex-threejs-character-controller/references/tuning-and-presets.md +3 -0
- package/templates/skills/genex-threejs-multiplayer/references/host-physics.md +7 -6
- package/templates/skills/genex-threejs-skill-router/SKILL.md +9 -8
- package/templates/skills/genex-threejs-vehicle-controllers/references/enter-exit.md +3 -2
- package/templates/controllers/assets/character.glb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: genex-threejs-character-controller
|
|
3
|
-
description: Add a tuned physics character controller to a Genex Three.js game with `npx genex controller character` — dynamic-capsule
|
|
3
|
+
description: Add a tuned physics character controller to a Genex Three.js game with `npx genex controller character` — dynamic-capsule walk/run/jump/crouch (slopes, stairs, moving platforms), follow camera, keyboard + touch input, animation binding. Animation packs (sword, pistol, magic, climb, swim, emotes…) install via `npx genex controller anims <tags>`. Use for any on-foot player or third-person movement, and whenever the user asks for ecctrl — this is that controller, ported to plain Three.js.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Genex Three.js Character Controller
|
|
@@ -14,9 +14,13 @@ npm i @dimforge/rapier3d-compat @pixiv/three-vrm # three is already in the sca
|
|
|
14
14
|
|
|
15
15
|
The command vendors tested, tuned controller code into the game: TypeScript
|
|
16
16
|
modules into `src/controllers/` (including `character/vrm/` — VRM loading,
|
|
17
|
-
animation retargeting, capsule auto-fit, foot IK) and the
|
|
18
|
-
`animation-library.glb`
|
|
19
|
-
|
|
17
|
+
animation retargeting, capsule auto-fit, foot IK) and the 12-clip core
|
|
18
|
+
`animation-library.glb` (idle/walk/run/jump/crouch + hit/death/interact,
|
|
19
|
+
~1.3 MB) into `public/assets/`. Need more — swords, pistols, magic, climbing,
|
|
20
|
+
swimming, emotes? Install exactly what the game uses with
|
|
21
|
+
`npx genex controller anims <tags|clip names…>` (see Animations below). The
|
|
22
|
+
command also writes the player's avatar to `public/assets/avatar.vrm` —
|
|
23
|
+
**your** avatar when you're signed in,
|
|
20
24
|
otherwise a bundled CC0 default (attribution in `src/controllers/NOTICE.md`).
|
|
21
25
|
The character plays as that VRM. The copied files are then owned by the game —
|
|
22
26
|
edit them freely; re-running skips existing files unless `--force`. Do not write
|
|
@@ -36,27 +40,28 @@ platforms, climbs stairs and slides on too-steep slopes out of the box.
|
|
|
36
40
|
| `character/keyboard-input.ts` | `KeyboardInput` | WASD/arrows/Shift/Space/F state, no per-frame polling setup |
|
|
37
41
|
| `character/touch-joystick.ts` | `TouchJoystick`, `VirtualButton` | mobile controls |
|
|
38
42
|
| `character/character-animations.ts` | `CharacterAnimations` | animation state machine + fuzzy clip binding + `playOneShot` + procedural fallback |
|
|
39
|
-
| `character/
|
|
43
|
+
| `character/animation-packs.ts` | `loadCharacterClips` | one call loads the core library + every installed `genex controller anims` pack, retargeted onto the VRM |
|
|
44
|
+
| `character/vrm/*` | `loadVrm`, `retargetClips`, `capsuleFromModel`, `FootIK` | load the VRM avatar, retarget library clips onto its humanoid rig, auto-fit the capsule, ground the feet |
|
|
40
45
|
|
|
41
46
|
## Minimal wiring
|
|
42
47
|
|
|
43
48
|
```ts
|
|
44
|
-
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
45
49
|
import { PhysicsWorld } from "./controllers/shared/physics-world.ts";
|
|
46
50
|
import { CharacterController } from "./controllers/character/character-controller.ts";
|
|
47
51
|
import { CharacterAnimations } from "./controllers/character/character-animations.ts";
|
|
52
|
+
import { loadCharacterClips } from "./controllers/character/animation-packs.ts";
|
|
48
53
|
import { characterPresets } from "./controllers/character/presets.ts";
|
|
49
54
|
import { FollowCamera } from "./controllers/character/follow-camera.ts";
|
|
50
55
|
import { KeyboardInput } from "./controllers/character/keyboard-input.ts";
|
|
51
56
|
import { loadVrm } from "./controllers/character/vrm/vrm-loader.ts";
|
|
52
|
-
import { retargetClips } from "./controllers/character/vrm/vrm-retarget.ts";
|
|
53
57
|
import { capsuleFromModel } from "./controllers/character/vrm/capsule-fit.ts";
|
|
54
58
|
|
|
55
59
|
const physics = await PhysicsWorld.create(); // nothing RAPIER-related may run before this resolves
|
|
56
60
|
|
|
57
|
-
// Load the player's avatar
|
|
61
|
+
// Load the player's avatar, then every animation the game has (the bundled core
|
|
62
|
+
// library + any packs installed by `genex controller anims`), retargeted onto the VRM.
|
|
58
63
|
const { scene: avatar, vrm } = await loadVrm("./assets/avatar.vrm");
|
|
59
|
-
const
|
|
64
|
+
const clips = await loadCharacterClips(vrm);
|
|
60
65
|
|
|
61
66
|
const fit = capsuleFromModel(avatar); // collider fits THIS avatar's bounds
|
|
62
67
|
const character = new CharacterController(physics.world, camera, {
|
|
@@ -70,7 +75,7 @@ character.root.add(avatar); // parent the avatar under the c
|
|
|
70
75
|
avatar.position.y = fit.modelOffsetY; // root = capsule CENTER; drop the model so feet touch the floor
|
|
71
76
|
physics.registerBody(character.body, character.root); // root now follows the body, interpolated
|
|
72
77
|
|
|
73
|
-
const anims = new CharacterAnimations(avatar,
|
|
78
|
+
const anims = new CharacterAnimations(avatar, clips);
|
|
74
79
|
addEventListener("pointerdown", () => anims.playOneShot("Punch_Jab")); // punch on click
|
|
75
80
|
|
|
76
81
|
const kb = new KeyboardInput();
|
|
@@ -127,17 +132,39 @@ preset table with provenance, the density/spring scaling rule, and the
|
|
|
127
132
|
- The capsule ships with friction `-0.5` **on purpose** (grip is synthesized by
|
|
128
133
|
the controller). Do not "fix" it to a positive value.
|
|
129
134
|
|
|
130
|
-
## Animations
|
|
135
|
+
## Animations + animation packs
|
|
131
136
|
|
|
132
|
-
`CharacterAnimations` resolves
|
|
133
|
-
JUMP_START / JUMP_IDLE / JUMP_FALL / JUMP_LAND)
|
|
134
|
-
and crossfades mixer actions. Every OTHER
|
|
135
|
-
pistol fire, spells, sit, dance, hit reactions —
|
|
136
|
-
`anims.playOneShot("Punch_Jab")`, which layers over locomotion
|
|
137
|
-
when done (punch-on-click is the default).
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
`CharacterAnimations` resolves nine locomotion states (IDLE / WALK / RUN /
|
|
138
|
+
CROUCH_IDLE / CROUCH_MOVE / JUMP_START / JUMP_IDLE / JUMP_FALL / JUMP_LAND)
|
|
139
|
+
from the controller's live flags and crossfades mixer actions. Every OTHER
|
|
140
|
+
clip — punches, sword swings, pistol fire, spells, sit, dance, hit reactions —
|
|
141
|
+
plays through `anims.playOneShot("Punch_Jab")`, which layers over locomotion
|
|
142
|
+
and returns to it when done (punch-on-click is the default).
|
|
143
|
+
|
|
144
|
+
The bundled library carries only the 12 core clips. **Install what the game's
|
|
145
|
+
theme needs** from the 120-clip catalog, by tag or exact clip name:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx genex controller anims sword pistol # a sword+shooter game
|
|
149
|
+
npx genex controller anims stealth climb crawl # a ninja game
|
|
150
|
+
npx genex controller anims --list # browse tags; --list <tag> for per-clip details
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Clips land in `public/assets/anims/` and `loadCharacterClips(vrm)` picks them
|
|
154
|
+
up automatically — re-run with more tags any time (additive). Read
|
|
155
|
+
[references/animations.md](references/animations.md) for the tag catalog with
|
|
156
|
+
genre hints, `playOneShot` options, overrides, foot IK, and remote-player
|
|
157
|
+
animation.
|
|
158
|
+
|
|
159
|
+
## Crouch (built in)
|
|
160
|
+
|
|
161
|
+
`C` toggles crouch (capsule shrinks, speed drops to `crouchSpeedRatio ×
|
|
162
|
+
maxWalkVel`, CROUCH_IDLE/CROUCH_MOVE play). Standing back up is
|
|
163
|
+
ceiling-checked: under a low obstacle the character STAYS crouched and pops up
|
|
164
|
+
automatically once clear; jumping while crouched requests a stand instead of
|
|
165
|
+
jumping. `crouchMode: "hold"` makes it hold-to-crouch;
|
|
166
|
+
`character.setCrouch(bool)` and `character.crouchActive` are the programmatic
|
|
167
|
+
hooks (that's also how a touch button wires in — see Mobile below).
|
|
141
168
|
|
|
142
169
|
## Mobile: TouchJoystick + VirtualButton (wire by default)
|
|
143
170
|
|
|
@@ -151,6 +178,11 @@ import { TouchJoystick, VirtualButton } from "./controllers/character/touch-joys
|
|
|
151
178
|
|
|
152
179
|
const joy = new TouchJoystick({ wrapperStyle: { left: "20px", bottom: "20px" } }); // position is REQUIRED
|
|
153
180
|
const btnJump = new VirtualButton({ label: "Jump", wrapperStyle: { right: "30px", bottom: "30px" } });
|
|
181
|
+
const btnCrouch = new VirtualButton({
|
|
182
|
+
label: "Crouch",
|
|
183
|
+
wrapperStyle: { right: "100px", bottom: "30px" },
|
|
184
|
+
onPress: () => character.setCrouch(!character.crouchActive), // tap = toggle
|
|
185
|
+
});
|
|
154
186
|
|
|
155
187
|
physics.onBeforeStep(() => {
|
|
156
188
|
character.setMovement({
|
|
@@ -178,8 +210,9 @@ players' physics locally guarantees divergence — every client would compute a
|
|
|
178
210
|
different world.
|
|
179
211
|
|
|
180
212
|
- Publish your own `currPos` + yaw on the fixed 10–20 Hz tick, not per frame.
|
|
181
|
-
- To animate remotes, sync the
|
|
182
|
-
|
|
213
|
+
- To animate remotes, sync the six animation booleans (`isOnGround`,
|
|
214
|
+
`isFalling`, `isMoving`, `runActive`, `jumpActive`, `crouchActive`) and feed
|
|
215
|
+
them to a per-remote `CharacterAnimations` — see the animations reference.
|
|
183
216
|
- Load `$genex-threejs-multiplayer` before writing any networking code; it is
|
|
184
217
|
mandatory for any 2+ player game.
|
|
185
218
|
|
|
@@ -1,37 +1,43 @@
|
|
|
1
1
|
# Character animations
|
|
2
2
|
|
|
3
3
|
`CharacterAnimations` turns the controller's live flags into crossfaded
|
|
4
|
-
`THREE.AnimationMixer` playback:
|
|
5
|
-
works with the bundled library, Mixamo exports, or
|
|
6
|
-
procedural bob/lean fallback when a model has no usable
|
|
4
|
+
`THREE.AnimationMixer` playback: nine states, alias-based clip binding that
|
|
5
|
+
works with the bundled library, installed animation packs, Mixamo exports, or
|
|
6
|
+
arbitrary rigs, and a procedural bob/lean fallback when a model has no usable
|
|
7
|
+
clips at all.
|
|
7
8
|
|
|
8
|
-
## The bundled assets
|
|
9
|
+
## The bundled assets + animation packs
|
|
9
10
|
|
|
10
11
|
`npx genex controller character` sets the game up to play as a **VRM avatar**:
|
|
11
12
|
|
|
12
13
|
- `public/assets/avatar.vrm` — the player's avatar (yours when signed in, else a
|
|
13
14
|
bundled CC0 default). Always present; always one path.
|
|
14
|
-
- `public/assets/animation-library.glb` (
|
|
15
|
-
|
|
15
|
+
- `public/assets/animation-library.glb` (~1.3 MB) — the 12-clip core
|
|
16
|
+
(idle/walk/jog/sprint, the jump trio, crouch idle+move, hit, death, interact)
|
|
17
|
+
on a shared Quaternius rig (provenance in `src/controllers/NOTICE.md`).
|
|
18
|
+
- `public/assets/anims/*.glb` — OPTIONAL per-clip packs installed by
|
|
19
|
+
`npx genex controller anims <tags|clip names…>` from the 120-clip Quaternius
|
|
20
|
+
Universal Animation Library Pro catalog (see the tag catalog below). Re-runs
|
|
21
|
+
are additive; `--reset` starts over; a `manifest.json` alongside lists what's
|
|
22
|
+
installed.
|
|
16
23
|
|
|
17
24
|
VRM helpers live in `src/controllers/character/vrm/`. Install three-vrm once:
|
|
18
25
|
`npm i @pixiv/three-vrm`.
|
|
19
26
|
|
|
20
|
-
Load the avatar,
|
|
21
|
-
|
|
22
|
-
subpath:
|
|
27
|
+
Load the avatar, then EVERYTHING the game has via `loadCharacterClips` — core
|
|
28
|
+
library + installed packs, each retargeted onto the avatar's humanoid rig —
|
|
29
|
+
all with **relative** paths so the published game works under its subpath:
|
|
23
30
|
|
|
24
31
|
```ts
|
|
25
|
-
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
26
32
|
import { loadVrm } from "./controllers/character/vrm/vrm-loader.ts";
|
|
27
|
-
import {
|
|
33
|
+
import { loadCharacterClips } from "./controllers/character/animation-packs.ts";
|
|
28
34
|
import { capsuleFromModel } from "./controllers/character/vrm/capsule-fit.ts";
|
|
29
35
|
import { CharacterController } from "./controllers/character/character-controller.ts";
|
|
30
36
|
import { CharacterAnimations } from "./controllers/character/character-animations.ts";
|
|
31
37
|
import { characterPresets } from "./controllers/character/presets.ts";
|
|
32
38
|
|
|
33
39
|
const { scene, vrm } = await loadVrm("./assets/avatar.vrm");
|
|
34
|
-
const
|
|
40
|
+
const clips = await loadCharacterClips(vrm); // core + every installed pack, retargeted
|
|
35
41
|
|
|
36
42
|
// capsuleFromModel derives the collider from the avatar's bounds — no manual
|
|
37
43
|
// per-avatar tuning even as heights/proportions vary across the library.
|
|
@@ -44,11 +50,13 @@ const character = new CharacterController(physics.world, camera, {
|
|
|
44
50
|
character.root.add(scene);
|
|
45
51
|
scene.position.y = fit.modelOffsetY; // root = capsule CENTER; drop the model so feet touch the floor
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
// (VRM 0.x and 1.0 alike); the result feeds CharacterAnimations unchanged.
|
|
49
|
-
const anims = new CharacterAnimations(scene, retargetClips(vrm, lib.scene, lib.animations));
|
|
53
|
+
const anims = new CharacterAnimations(scene, clips);
|
|
50
54
|
```
|
|
51
55
|
|
|
56
|
+
(Advanced: to load a single GLB by hand, `retargetClips(vrm, gltf.scene,
|
|
57
|
+
gltf.animations)` from `vrm/vrm-retarget.ts` is what `loadCharacterClips` uses
|
|
58
|
+
internally — it auto-detects the source rig per file.)
|
|
59
|
+
|
|
52
60
|
Per render frame, **after** `physics.step(delta)`:
|
|
53
61
|
|
|
54
62
|
```ts
|
|
@@ -73,11 +81,27 @@ import { FootIK } from "./controllers/character/vrm/foot-ik.ts";
|
|
|
73
81
|
const footIK = new FootIK(vrm, (foot) => {
|
|
74
82
|
const hit = physics.world.castRay(
|
|
75
83
|
new RAPIER.Ray({ x: foot.x, y: foot.y + 0.5, z: foot.z }, { x: 0, y: -1, z: 0 }),
|
|
76
|
-
1.5, true
|
|
84
|
+
1.5, true, RAPIER.QueryFilterFlags.EXCLUDE_SENSORS,
|
|
85
|
+
undefined, undefined, character.body); // exclude the character's own capsule
|
|
77
86
|
return hit ? foot.y + 0.5 - hit.timeOfImpact : null;
|
|
87
|
+
}, {
|
|
88
|
+
isActive: () => character.isOnGround, // keep the jump pose while airborne
|
|
89
|
+
allowReachDown: () => !anims.oneShotActive, // REQUIRED with one-shots: they are
|
|
90
|
+
// choreography — without this a firing stance over a step edge drags the pelvis
|
|
91
|
+
// down into the staircase. While gated, feet are only lifted as much as needed
|
|
92
|
+
// to stay out of the step under them (anti dig-in).
|
|
78
93
|
});
|
|
79
|
-
|
|
80
|
-
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
ORDER IS LOAD-BEARING — foot IK poses the VRM's *normalized* bones, which
|
|
97
|
+
`vrm.update()` then copies onto the render mesh. Run it BETWEEN the two (after
|
|
98
|
+
`vrm.update` it has no visible effect at all — the next frame's mixer tick
|
|
99
|
+
overwrites it before the copy):
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
anims.update(character, delta); // 1. mixer poses the normalized rig
|
|
103
|
+
footIK.update(delta); // 2. plant the feet on that pose
|
|
104
|
+
vrm.update(delta); // 3. copy normalized -> render mesh + spring bones
|
|
81
105
|
```
|
|
82
106
|
|
|
83
107
|
## States and default clips
|
|
@@ -89,14 +113,16 @@ The pure resolver (`resolveAnimationState`) maps controller flags to one of:
|
|
|
89
113
|
| `IDLE` | `Idle_Loop` | starts playing immediately on construction |
|
|
90
114
|
| `WALK` | `Walk_Loop` | |
|
|
91
115
|
| `RUN` | `Jog_Fwd_Loop` | **the default run clip**; `Sprint_Loop` also binds via alias — force it with an override if the user wants an all-out sprint look |
|
|
116
|
+
| `CROUCH_IDLE` | `Crouch_Idle_Loop` | while `crouchActive`; degrades to `IDLE` on rigs without crouch clips |
|
|
117
|
+
| `CROUCH_MOVE` | `Crouch_Fwd_Loop` | crouched + moving; degrades to `WALK` |
|
|
92
118
|
| `JUMP_START` | `Jump_Start` | one-shot, played at 1.6× so it finishes inside the hop |
|
|
93
119
|
| `JUMP_IDLE` | `Jump_Loop` | airborne, moving up |
|
|
94
120
|
| `JUMP_FALL` | `Jump_Loop` | airborne, moving down (shares the clip — no restart mid-air) |
|
|
95
121
|
| `JUMP_LAND` | `Jump_Land` | one-shot |
|
|
96
122
|
|
|
97
|
-
## One-shot actions + the
|
|
123
|
+
## One-shot actions + the 120-clip pack catalog
|
|
98
124
|
|
|
99
|
-
The
|
|
125
|
+
The nine states above cover locomotion. **Every other clip** plays through
|
|
100
126
|
`anims.playOneShot(clipName, options?)`: it crossfades the clip over the current
|
|
101
127
|
motion, plays it once, then hands control back to the state machine. Returns
|
|
102
128
|
`false` if the clip name isn't in the set you passed to the constructor.
|
|
@@ -116,31 +142,44 @@ addEventListener("pointerdown", () => {
|
|
|
116
142
|
`options`: `fadeIn` (default 0.1 s), `timeScale`, `clamp` (hold the final pose —
|
|
117
143
|
for deaths), `onDone`.
|
|
118
144
|
|
|
119
|
-
|
|
145
|
+
The 12 core clips are always available; everything else comes from
|
|
146
|
+
`npx genex controller anims <selectors…>` — selectors are **tags** (install a
|
|
147
|
+
themed set) or **exact clip names** (cherry-pick), freely mixed:
|
|
120
148
|
|
|
121
|
-
|
|
149
|
+
```bash
|
|
150
|
+
npx genex controller anims sword pistol # tags: a sword + shooter game
|
|
151
|
+
npx genex controller anims stealth Celebration # a tag + one exact clip
|
|
152
|
+
npx genex controller anims --list # all tags with sizes
|
|
153
|
+
npx genex controller anims --list sword magic # per-clip durations + descriptions
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Tag catalog (tag → clips → reach for it when). Pick tags from the game's theme;
|
|
157
|
+
run `--list <tag>` for per-clip descriptions before wiring one-shots:
|
|
158
|
+
|
|
159
|
+
| Tag | Clips (≈size) | Use for |
|
|
122
160
|
| --- | --- | --- |
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
161
|
+
| `core` | Idle/Walk/Jog/Sprint loops, Jump trio, Crouch idle+fwd, Hit_Chest, Death01, Interact | already bundled — never needs installing |
|
|
162
|
+
| `locomotion-extra` | Jog strafe/diagonal/lean ×10, Turn90_L/R, Walk_Formal_Loop, Sprint_Enter/Exit (~1 MB) | 8-way strafe rigs, formal NPC gaits, sprint transitions |
|
|
163
|
+
| `stealth` | Crouch_Enter/Exit + crouched strafe/diagonal/backward ×9 (~0.6 MB) | stealth games (the built-in crouch only needs core; these add direction variety + transitions) |
|
|
164
|
+
| `crawl` | Crawl enter/exit/idle + 4 directions (~0.5 MB) | prone crawling, vents, tunnels |
|
|
165
|
+
| `climb` | Climb enter/exit/idle, up/down/left/right, ClimbLedge (~0.6 MB) | ladders, walls, parkour |
|
|
166
|
+
| `parkour` | BackFlip, Roll, Dodge_Left/Right (~0.3 MB) | dodges and flips |
|
|
167
|
+
| `brawl` | Punch_Jab/Cross, Kick, PunchKick_Enter/Exit (~0.35 MB) | fist fighting |
|
|
168
|
+
| `sword` | Sword_Enter/Exit/Idle/Attack/Attack_Standing (~0.35 MB) | melee weapons |
|
|
169
|
+
| `pistol` | Pistol_Idle_Loop, Aim_Up/Neutral/Down (pick by camera pitch), Shoot, Reload (~0.3 MB) | shooters |
|
|
170
|
+
| `magic` | Spell_Simple + Spell_Double enter/exit/idle/shoot cycles (~0.5 MB) | casters — Double's shoot is a channel/beam loop |
|
|
171
|
+
| `damage` | Hit_Head/Shoulder_L/R/Stomach, Death02 (~0.3 MB) | directional hit reactions beyond the core pair |
|
|
172
|
+
| `swim` | Swim_Idle_Loop, Swim_Fwd_Loop (~0.15 MB) | water |
|
|
173
|
+
| `sit` | Sitting enter/exit + 5 idles, GroundSit set (~0.7 MB) | seats, campfires, dialogue |
|
|
174
|
+
| `emote` | Celebration, Crying, Dance_Loop, Drink, talking/tired/look-around idles, Rock/Paper/Scissors (~0.7 MB) | emotes, NPCs, minigames |
|
|
175
|
+
| `interact` | PickUp_Kneeling/Table, Fixing_Kneeling, Push enter/exit/loop (~0.4 MB) | pickups, levers, crafting, pushing |
|
|
176
|
+
| `shop` | Counter enter/exit/idle/give/show/angry (~0.4 MB) | shopkeeper NPCs |
|
|
177
|
+
| `drive` | Driving_Loop (~0.05 MB) | vehicles (see `$genex-threejs-vehicle-controllers`) |
|
|
140
178
|
|
|
141
179
|
For looping poses that should **persist** (aiming, sitting, swimming) rather than
|
|
142
180
|
play once, drive them through the public `anims.mixer` escape hatch instead; the
|
|
143
|
-
`$genex-threejs-vehicle-controllers` skill shows the seated pattern.
|
|
181
|
+
`$genex-threejs-vehicle-controllers` skill shows the seated pattern. All clips
|
|
182
|
+
are in-place (no root motion) — the physics controller owns all translation.
|
|
144
183
|
|
|
145
184
|
## Binding arbitrary rigs (Mixamo included)
|
|
146
185
|
|
|
@@ -149,10 +188,12 @@ explicit override (exact, then case-insensitive) → library exact names →
|
|
|
149
188
|
case-insensitive → each alias as a case-insensitive **substring**, shortest
|
|
150
189
|
matching clip name wins (so `Walk_Loop` beats `Walk_Bwd_Loop`, and Mixamo's
|
|
151
190
|
`walking` beats `walking_backwards`). Aliases include `idle`, `walk`, `run`,
|
|
152
|
-
`jog`, `sprint`, `
|
|
153
|
-
catch-all so a rig whose only airborne clip is
|
|
154
|
-
jump states. Unbound loop states chain
|
|
155
|
-
|
|
191
|
+
`jog`, `sprint`, `crouch_idle`, `sneak`, `jump_start`, `takeoff`, `fall`,
|
|
192
|
+
`land`, and a bare `jump` catch-all so a rig whose only airborne clip is
|
|
193
|
+
"Jumping" still binds all four jump states. Unbound loop states chain
|
|
194
|
+
(RUN↔WALK, JUMP_IDLE↔JUMP_FALL, CROUCH_IDLE→IDLE, CROUCH_MOVE→WALK — rigs
|
|
195
|
+
without crouch clips sneak in a standing pose instead of T-posing); unbound
|
|
196
|
+
one-shots stay silent so the previous loop keeps playing.
|
|
156
197
|
|
|
157
198
|
If a name refuses to bind, pass overrides:
|
|
158
199
|
|
|
@@ -195,9 +236,9 @@ const anims = new CharacterAnimations(placeholderMesh, []); // procedural fallba
|
|
|
195
236
|
|
|
196
237
|
## Remote players (multiplayer)
|
|
197
238
|
|
|
198
|
-
The snapshot type is structural — **anything** with the
|
|
239
|
+
The snapshot type is structural — **anything** with the six booleans works,
|
|
199
240
|
which is exactly what remote players need. Remote players have no physics and
|
|
200
|
-
no `CharacterController` (see the SKILL's multiplayer rule): sync the
|
|
241
|
+
no `CharacterController` (see the SKILL's multiplayer rule): sync the six
|
|
201
242
|
flags from the owner and feed them straight in.
|
|
202
243
|
|
|
203
244
|
```ts
|
|
@@ -208,12 +249,16 @@ const flags = {
|
|
|
208
249
|
isMoving: character.isMoving,
|
|
209
250
|
runActive: character.runActive,
|
|
210
251
|
jumpActive: character.jumpActive,
|
|
252
|
+
crouchActive: character.crouchActive,
|
|
211
253
|
};
|
|
212
254
|
|
|
213
255
|
// Receiver: one CharacterAnimations per remote model, fed the synced flags.
|
|
214
256
|
remoteAnims.update(remoteState.flags, delta);
|
|
215
257
|
```
|
|
216
258
|
|
|
259
|
+
`crouchActive` is additive: an older game syncing only five flags still
|
|
260
|
+
resolves correctly (a missing flag reads as not-crouched).
|
|
261
|
+
|
|
217
262
|
The mixer crossfades exactly as it does locally, so remote players animate
|
|
218
263
|
correctly without simulating anything. In v1 every player loads the same
|
|
219
264
|
`./assets/avatar.vrm` (the game owner's avatar), so remotes look like the owner —
|
package/templates/skills/genex-threejs-character-controller/references/tuning-and-presets.md
CHANGED
|
@@ -64,6 +64,9 @@ tip-overs mean the `autoBalance*` pair is too soft.
|
|
|
64
64
|
| "drifts sideways through turns" | raise `rejectVelFactor` toward 1 (default 1; `ice-slide` lowers it to 0.2) |
|
|
65
65
|
| "too slow / too fast" | `maxWalkVel` (default 2) / `maxRunVel` (default 5) |
|
|
66
66
|
| "run should be hold, not toggle" | `enableToggleRun: false` (default true = Shift toggles) |
|
|
67
|
+
| "sneaks too fast / too slow" | `crouchSpeedRatio` (default 0.45 × `maxWalkVel`) |
|
|
68
|
+
| "can't fit under the obstacle when crouched" | lower `crouchCapsuleScale` (default 0.6 — the crouched capsule's cylinder half-height as a fraction of standing; the head drops by `2*(1-scale)*capsuleHalfHeight`) |
|
|
69
|
+
| "crouch should be hold, not toggle" | `crouchMode: "hold"` (default `"toggle"` — C flips it) |
|
|
67
70
|
| "no control in the air" | raise `airDragFactor` (default 0.1) |
|
|
68
71
|
| "falls too fast at terminal velocity" | `fallingMaxVel` (default 20 m/s) |
|
|
69
72
|
| "leans too much when running" | lower `moveImpulsePointOffset` (default 0.5; 0 = no lean) |
|
|
@@ -224,7 +224,7 @@ remote simulation:
|
|
|
224
224
|
|
|
225
225
|
The vendored controllers expose their pose as `currPos` (a `THREE.Vector3`) and `currQuat` (a
|
|
226
226
|
`THREE.Quaternion`) — **not** `.position` / `.quaternion` — plus boolean state getters. Character
|
|
227
|
-
animation is driven by **
|
|
227
|
+
animation is driven by **six booleans**, not a single enum; publish the booleans and let remotes
|
|
228
228
|
reconstruct the animation. There is **no** `rig.animState`, no `remoteAnimator.play(...)`, and no
|
|
229
229
|
vehicle `wheelSpinPhase` getter.
|
|
230
230
|
|
|
@@ -235,9 +235,9 @@ const q = character.currQuat; // THREE.Quaternion
|
|
|
235
235
|
room.me.set({
|
|
236
236
|
x: r2(p.x), y: r2(p.y), z: r2(p.z),
|
|
237
237
|
q: [r2(q.x), r2(q.y), r2(q.z), r2(q.w)], // quaternion array — never a scalar yaw
|
|
238
|
-
// character animation =
|
|
238
|
+
// character animation = 6 booleans (the CharacterController exposes each as a getter):
|
|
239
239
|
g: character.isOnGround, f: character.isFalling, m: character.isMoving,
|
|
240
|
-
r: character.runActive, j: character.jumpActive,
|
|
240
|
+
r: character.runActive, j: character.jumpActive, c: character.crouchActive,
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
// Remote players: a VISUAL-ONLY avatar — NO Rapier body, NO controller instance for remotes.
|
|
@@ -248,15 +248,16 @@ remoteAvatar.group.position.set(pl.state.x, pl.state.y, pl.state.z);
|
|
|
248
248
|
remoteAvatar.group.quaternion.fromArray(pl.state.q);
|
|
249
249
|
const raw = pl.stateRaw; // discrete flags: read RAW, never smoothed
|
|
250
250
|
remoteAvatar.update(
|
|
251
|
-
{ isOnGround: !!raw.g, isFalling: !!raw.f, isMoving: !!raw.m, runActive: !!raw.r, jumpActive: !!raw.j },
|
|
251
|
+
{ isOnGround: !!raw.g, isFalling: !!raw.f, isMoving: !!raw.m, runActive: !!raw.r, jumpActive: !!raw.j, crouchActive: !!raw.c },
|
|
252
252
|
dt,
|
|
253
253
|
);
|
|
254
254
|
```
|
|
255
255
|
|
|
256
256
|
What to publish per controller:
|
|
257
257
|
|
|
258
|
-
- **character**: `currPos` → `x/y/z`, `currQuat` → `q`, and the
|
|
259
|
-
(`isOnGround`/`isFalling`/`isMoving`/`runActive`/`jumpActive`
|
|
258
|
+
- **character**: `currPos` → `x/y/z`, `currQuat` → `q`, and the six booleans above
|
|
259
|
+
(`isOnGround`/`isFalling`/`isMoving`/`runActive`/`jumpActive`/`crouchActive` — the last is
|
|
260
|
+
additive; a peer syncing only five still animates, minus crouch). Remotes rebuild the animation
|
|
260
261
|
with `avatar.update(flags, dt)` — see the `genex-threejs-character-controller` animations
|
|
261
262
|
reference for the flag set (single source of truth; don't invent a `play(anim)` call).
|
|
262
263
|
- **vehicle**: body `currPos` → `x/y/z` + `currQuat` → `q`. For visible steering, publish the
|
|
@@ -16,7 +16,7 @@ map, execution order, and acceptance gate.
|
|
|
16
16
|
| Work needed | Load |
|
|
17
17
|
| --- | --- |
|
|
18
18
|
| shot composition, chase/side/orbit rigs, camera handoffs, projection ownership, pointer look, floating origins | `$genex-threejs-camera-direction` |
|
|
19
|
-
| on-foot player movement: walk/run/jump, third-person character, slopes, stairs, moving platforms, animation binding | `$genex-threejs-character-controller` |
|
|
19
|
+
| on-foot player movement: walk/run/jump/crouch, third-person character, slopes, stairs, moving platforms, animation binding, extra animation packs (sword/pistol/magic/climb/swim/emotes via `genex controller anims`) | `$genex-threejs-character-controller` |
|
|
20
20
|
| the player drives or flies something: cars, drones, vehicle physics, gearbox, enter/exit between character and vehicle | `$genex-threejs-vehicle-controllers` |
|
|
21
21
|
| anything falls, collides, gets pushed, or needs physics: Rapier world setup, colliders for meshes and GLBs, collision events | `$genex-threejs-physics-rapier` |
|
|
22
22
|
| launch and docking timelines, procedural transform phases, springs, staging, rotating-frame alignment, debris motion | `$genex-threejs-procedural-animation` |
|
|
@@ -60,13 +60,12 @@ recipes (sports/ball, shooter, co-op).
|
|
|
60
60
|
## Real (AI-generated) assets — `npx genex` commands
|
|
61
61
|
|
|
62
62
|
When the user wants a **specific, recognizable asset** (a named object, a described
|
|
63
|
-
sky, a particular sound or
|
|
64
|
-
it with an `npx genex` command (run inside the project, where the
|
|
65
|
-
dev dependency makes `genex` resolve to the right CLI). Each
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
loader code.
|
|
63
|
+
sky, a particular sound, surface, image, or clip) rather than something authored in
|
|
64
|
+
code, generate it with an `npx genex` command (run inside the project, where the
|
|
65
|
+
`@genex-ai/cli-demo` dev dependency makes `genex` resolve to the right CLI). Each
|
|
66
|
+
prints a permanent public `assets.genex.technology` URL you load straight from at
|
|
67
|
+
runtime — the asset lives in Genex storage (R2), not your repo, so there's nothing to
|
|
68
|
+
commit. Each skill has the exact Three.js loader code.
|
|
70
69
|
|
|
71
70
|
| Work needed | Generate with | Skill |
|
|
72
71
|
| --- | --- | --- |
|
|
@@ -74,6 +73,8 @@ loader code.
|
|
|
74
73
|
| a described 360° sky / backdrop + image-based lighting | `npx genex skybox "<prompt>"` | `$genex-ai-skybox` |
|
|
75
74
|
| a specific sound effect tied to an event | `npx genex sfx "<prompt>"` | `$genex-ai-sfx` |
|
|
76
75
|
| a photoreal surface/material on a mesh or terrain | `npx genex texture "<prompt>" [--terrain]` | `$genex-ai-texture` |
|
|
76
|
+
| a picture on a plane/sprite — poster, sign, sprite, card art, decal/sticker | `npx genex image "<prompt>" [--transparent]` | `$genex-ai-image` |
|
|
77
|
+
| a video clip on a surface — in-game screen, billboard, cutscene, backdrop | `npx genex video "<prompt>" [--loop]` | `$genex-ai-video` |
|
|
77
78
|
|
|
78
79
|
Prefer the **procedural** skills above for abstract/parametric/animated systems
|
|
79
80
|
(geometry, materials, sky, water, VFX) — no files, infinite variation. Prefer the
|
|
@@ -109,8 +109,9 @@ a seated pilot; that is game glue on the `onHandoff` seam. The pattern: keep
|
|
|
109
109
|
a reference to the character's visual model, re-parent it into a seat anchor
|
|
110
110
|
under the vehicle's chassis object, and play the seat clips through a
|
|
111
111
|
dedicated `THREE.AnimationMixer` (the locomotion state machine is idle while
|
|
112
|
-
parked). The clips
|
|
113
|
-
`npx genex controller
|
|
112
|
+
parked). The seat clips are animation-pack clips — install them once with
|
|
113
|
+
`npx genex controller anims sit drive` (they land in `public/assets/anims/`
|
|
114
|
+
and `loadCharacterClips` from the character controller picks them up).
|
|
114
115
|
|
|
115
116
|
```ts
|
|
116
117
|
const seatMixer = new THREE.AnimationMixer(characterModel);
|
|
Binary file
|