@drawcall/acta
v0.1.40
Published
Acta is a JSON format for 3D character behavior in three.js.
Readme
@drawcall/acta
Acta is a JSON format for 3D character behavior in three.js.
Runtime Shape
Acta separates host intent, Acta-owned animation state, motion output, and gameplay effects:
- The host sends persistent frame input with one flat
update(delta, { isGrounded, moveDirection, aimDirection, headDirection })object. Each direction states one thing the character is doing, in world space, and isnullwhen it isn't:moveDirectionwhere it is moving (length is the amount 0..1,null/zero is not moving),aimDirectionwhere it is aiming,headDirectionwhere it is looking. Acta derives body facing asaimDirection ?? moveDirection ?? headDirection. - One-shot action inputs are requested with
requestAction(...). - Behavior JSON decides which state currently owns locomotion, jumps, animation, and timed effects.
- Top-level runtime callbacks apply accepted locomotion and jump output.
- Gameplay handles Acta effects with the
effectsoption;acta testlogs those as effect lines.
This is the same shape for FPS players, third-person players, enemies, companions, and crowds. A first-person player may use camera-derived aimDirection and headDirection; a third-person player may send camera-relative moveDirection; an NPC may send navigation-derived moveDirection plus a target-derived aimDirection.
const interpreter = await CharacterBehaviorInterpreter.create(behavior, model, {
effects: {
muzzle: shootWeaponFromActaEffect,
},
jump: (jumpVelocity) => physics.applyVelocity(new Vector3(0, jumpVelocity ?? 8, 0)),
motion: (desiredVelocity, delta) => {
physics.inputVelocity.copy(desiredVelocity)
physics.update(model.scene, delta, physicsOptions)
},
})
interpreter.requestAction('fire')
interpreter.update(delta, {
aimDirection, // where the character is aiming (null when not aiming)
isGrounded: physics.isGrounded,
moveDirection, // where it is moving: length is the amount (0..1), null/zero is not moving
headDirection: aimDirection, // where it is looking
})Use effects for animation-timed gameplay moments:
{
"type": "animation",
"url": "/humanoid-animation/quaternius-ual1-pistol-shoot.glb",
"effects": [{ "name": "muzzle", "at": 0.08 }]
}Use movement.speed to declare the desired locomotion speed for an active animation. In blends, put movement on each animations[] sample so each clip declares its own speed and allowed directions. speed is a positive number in meters per second (root-motion traversal is not supported yet). allowedDirections describes which movement directions the animation supports relative to body facing, not relative to the camera; movement is accepted while it points within 30° of an allowed direction. A blend selects the nearest sample and crossfades to it rather than interpolating two clips, so provide a sample for every direction the character should move. Use "all" only for truly omnidirectional or direction-neutral animation; otherwise list the supported directions such as ["forward"] or ["back"].
Convert Behavior JSON To TypeScript
npx @drawcall/acta convert <behavior.json> > character-behavior.converted.tsconvert validates behavior JSON and turns it into a TypeScript character class. The generated class extends THREE.Group, loads a provided model or the default Viverse mannequin, and exposes a typed <Class>Options: you pass the Motion Output, Jump Output, and Effect Output hooks to create(options) / createFromModel(model, options) (each hook receives the character instance). Required hooks are enforced by the type, so a forgotten motion or effect hook is a compile error. Use --json only when you want to inspect the validated behavior JSON directly.
Test animations
Animation references point directly at single-animation GLB files. CLI commands look for the nearest public/ directory by default; pass --animation-dir to use a different directory.
