@genex-ai/cli-demo 0.37.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 +1 -0
- package/dist/index.js +344 -20
- 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-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 +1 -1
- package/templates/skills/genex-threejs-vehicle-controllers/references/enter-exit.md +3 -2
- package/templates/controllers/assets/character.glb +0 -0
|
@@ -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` |
|
|
@@ -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
|