SuperHQ Motion experiment: locomotion and action checkpoints, runtime, clips, evaluation
6adbbb1 verified Download src/game-driver.js from superhq-ai/motion: direct link, hf CLI and curl.
- Browser
- Download file 3.51 kB
-
https://huggingface.co/superhq-ai/motion/resolve/main/src/game-driver.js
- Command line
-
hf download hf://superhq-ai/motion/src/game-driver.js
-
curl -L -o game-driver.js https://huggingface.co/superhq-ai/motion/resolve/main/src/game-driver.js
3.51 kB
| // Game-side intent, transition routing and heading. The network still generates every pose. | |
| export function transitionPath(config, from, to) { | |
| if (!config.profiles[from] || !config.profiles[to]) throw Error('Unknown motion profile.'); | |
| const queue = [[from]], seen = new Set([from]); | |
| while (queue.length) { | |
| const path = queue.shift(), last = path.at(-1); | |
| if (last === to) return path.slice(1); | |
| for (const [a, b] of config.transitions) { | |
| const next = a === last ? b : b === last ? a : null; | |
| if (next && !seen.has(next)) { seen.add(next); queue.push([...path, next]); } | |
| } | |
| } | |
| throw Error('No validated transition path.'); | |
| } | |
| export class PlaygroundDriver { | |
| constructor(controller) { this.controller = controller; this.reset(); } | |
| reset() { | |
| this.controller.reset(); this.position = [0, 0, 0]; this.heading = 0; | |
| this.lastSimulation = [0, 0, 0]; this.desired = 'Idle_Loop'; this.path = []; | |
| this.events = []; this.frames = 0; | |
| } | |
| step({moving = false, crouch = false, jog = false, sprint = false, style = 'natural', heading = this.heading} = {}) { | |
| if (!Number.isFinite(heading) || !['natural', 'carry', 'zombie'].includes(style)) throw Error('Invalid playground intent.'); | |
| const c = this.controller; | |
| this.desired = crouch ? (moving ? 'Crouch_Fwd_Loop' : 'Crouch_Idle_Loop') : | |
| style === 'zombie' ? (moving ? 'Zombie_Walk_Fwd_Loop' : 'Zombie_Idle_Loop') : | |
| !moving ? 'Idle_Loop' : style === 'carry' ? 'Walk_Carry_Loop' : | |
| sprint ? 'Sprint_Loop' : jog ? 'Jog_Fwd_Loop' : 'Walk_Loop'; | |
| // Recompute from current target so rapid input changes never reset phase or interrupt a blend. | |
| this.path = transitionPath(c.config, c.profile, this.desired); | |
| // Never start walking solely to satisfy a stationary stance/style request. | |
| // Missing idle-to-idle edges remain pending until the player actually moves. | |
| const waitingForMovement = !moving && this.path.length > 0 && | |
| c.config.profiles[c.profile].gait === 'idle' && | |
| this.path.some(name => c.config.profiles[name].gait !== 'idle') && | |
| c.clock.time - c.clock.start >= c.clock.transitionSeconds - 1e-8; | |
| if (this.path.length && !waitingForMovement && c.clock.time - c.clock.start >= c.clock.transitionSeconds - 1e-8) { | |
| const from = c.profile, to = this.path[0]; c.setProfile(to); | |
| this.events.push({frame: this.frames, from, to}); if (this.events.length > 100) this.events.shift(); | |
| } | |
| if (moving) { | |
| const delta = Math.atan2(Math.sin(heading - this.heading), Math.cos(heading - this.heading)); | |
| this.heading += Math.max(-.075, Math.min(.075, delta)); | |
| } | |
| const pose = c.step(), dx = pose.simulationPosition[0] - this.lastSimulation[0], dz = pose.simulationPosition[2] - this.lastSimulation[2]; | |
| const sin = Math.sin(this.heading), cos = Math.cos(this.heading); | |
| this.position[0] += dx * cos + dz * sin; this.position[2] += dz * cos - dx * sin; | |
| this.lastSimulation = pose.simulationPosition.slice(); this.frames++; | |
| // Keep inference in the canonical forward frame; the host transforms the resulting pose. | |
| const root = pose.rootPosition.map((v, i) => v - pose.simulationPosition[i]); | |
| return {...pose, rootPosition: root, worldPosition: this.position.slice(), heading: this.heading, | |
| desired: this.desired, path: transitionPath(c.config, c.profile, this.desired), | |
| waitingForMovement: !!waitingForMovement, | |
| blending: c.clock.time - c.clock.start < c.clock.transitionSeconds - 1e-8}; | |
| } | |
| } | |