Premade actors
Generated by tools/docs/gen-actors.mjs. Do not edit.
Import premade classes from gameable before loading a level. Props override class defaults; asset strings are manifest ids.
Shared prop groups
Each group uses the first registered default for each prop. Actor tables list differences; a group link applies only to props supported by that actor.
Door kit
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
width | number | 1 | min 0; metres | Closed doorway width. |
height | number | 2.1 | min 0; metres | Closed doorway height. |
thickness | number | 0.1 | min 0; metres | Leaf thickness. |
range | number | 2 | min 0; metres | Planar reach and maximum vertical separation in metres. |
facingDegrees | number | 75 | min 0; max 180; degrees | Maximum angle from camera forward, degrees. |
action | string | "E" | — | Input action/key; E also accepts Tap. |
once | boolean | false | — | Allow use once per player. |
enabled | boolean | true | — | Whether the actor can apply its behaviour. |
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
prompt | string | "E: open" | — | HUD prompt for the nearest eligible interaction. |
seconds | number | 0.6 | min 0; seconds | Time to move fully open or closed. |
startOpen | boolean | false | — | Start at the fully open pose. |
autoCloseSeconds | number | 0 | min 0; seconds | Wait after opening; zero disables automatic closing. |
lockedBy | string | "" | — | Inventory item required to unlock; empty means unlocked. |
consumeKey | boolean | false | — | Remove one key on successful unlock. |
stayOpen | boolean | false | — | Once opened, never close or offer interaction again. |
openPrompt | string | "E: open" | — | Prompt while closed or closing. |
closePrompt | string | "E: close" | — | Prompt while open or opening. |
lockedPrompt | string | "Locked" | — | Prompt when the player lacks the required key. |
lockedMessage | string | "" | — | Notice on locked interaction; empty disables it. |
openSound | string | "" | — | Manifest audio id on opening; empty is silent. |
closeSound | string | "" | — | Manifest audio id on closing; empty is silent. |
lockedSound | string | "" | — | Manifest audio id on locked interaction; empty is silent. |
colour | color | [0.45,0.28,0.13] | min 0; max 1 | Linear RGB panel colour. |
look | enum | "wood" | wood, metal, none | Built-in painted panel detail; asset overrides this. |
opensWhen | array | [] | — | Named actors whose signals must all be on. |
toggledBy | enum | "interact" | interact, signal, proximity | Interaction, all named signals, or nearby players. |
LevelSettings
Level class: LevelSettings. Category: Markers.
Level-wide settings; at most one per level.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
killHeight | number | false | null | null | metres | Kill plane: null = lowest placed actor minus 50 m (off if nothing is placed); false = off. |
Events:
None.
Components:
root: scene
json
{
"class": "LevelSettings",
"position": [
0,
0,
0
],
"props": {
"killHeight": null
}
}PlayerStart
Level class: PlayerStart. Category: Markers.
Player spawn marker; yaw comes from its root transform.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
tag | string | "" | — | Added to instance tags at construction. |
seat | number | null | null | — | Preferred player seat; null is not seat-specific. |
Events:
None.
Components:
root: scene
json
{
"class": "PlayerStart",
"position": [
0,
0,
0
],
"props": {
"tag": "",
"seat": null
}
}TargetPoint
Level class: TargetPoint. Category: Markers.
Named destination with no collision or behaviour.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
tag | string | "" | — | Added to instance tags at construction. |
Events:
None.
Components:
root: scene
json
{
"class": "TargetPoint",
"position": [
0,
0,
0
],
"props": {
"tag": ""
}
}TriggerBox
Level class: TriggerBox. Category: Volumes.
Overlap marker.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
halfExtents | vec3 | [1,1,1] | metres | Box half sizes [x,y,z], metres, read at spawn. |
Events:
onTrigger: Dispatches named enter and exit listeners for eligible actors and players.
Components:
root: box
json
{
"class": "TriggerBox",
"position": [
0,
0,
0
],
"props": {
"halfExtents": [
1,
1,
1
]
}
}TriggerSphere
Level class: TriggerSphere. Category: Volumes.
Overlap marker.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
radius | number | 1 | min 0; metres | Radius in metres, read at spawn. |
Events:
onTrigger: Dispatches named enter and exit listeners for eligible actors and players.
Components:
root: sphere
json
{
"class": "TriggerSphere",
"position": [
0,
0,
0
],
"props": {
"radius": 1
}
}TriggerCapsule
Level class: TriggerCapsule. Category: Volumes.
Overlap marker.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
radius | number | 0.5 | min 0; metres | Radius in metres, read at spawn. |
halfHeight | number | 1 | min 0; metres | Capsule straight-section half height, metres. |
Events:
onTrigger: Dispatches named enter and exit listeners for eligible actors and players.
Components:
root: capsule
json
{
"class": "TriggerCapsule",
"position": [
0,
0,
0
],
"props": {
"radius": 0.5,
"halfHeight": 1
}
}BlockingVolume
Level class: BlockingVolume. Category: Volumes.
Invisible fixed box; dimensions are half extents in metres, read at spawn.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
halfExtents | vec3 | [1,1,1] | metres | Box half sizes [x,y,z], metres, read at spawn. |
Events:
None.
Components:
root: box
json
{
"class": "BlockingVolume",
"position": [
0,
0,
0
],
"props": {
"halfExtents": [
1,
1,
1
]
}
}KillVolume
Level class: KillVolume. Category: Volumes.
Recover vehicles through their circuit or spawn policy, respawn players, destroy other actors.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
halfExtents | vec3 | [1,1,1] | metres | Box half sizes [x,y,z], metres, read at spawn. |
Events:
None.
Components:
root: box
json
{
"class": "KillVolume",
"position": [
0,
0,
0
],
"props": {
"halfExtents": [
1,
1,
1
]
}
}DamageVolume
Level class: DamageVolume. Category: Volumes.
Interval damage: one interval's damage on entry, then every interval seconds.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
halfExtents | vec3 | [1,1,1] | metres | Box half sizes [x,y,z], metres, read at spawn. |
damagePerSecond | number | 10 | min 0; damage/second | Damage rate; each hit deals rate × interval. |
interval | number | 1 | min 0; seconds | Seconds between hits, clamped to at least one fixed step. |
type | string | "" | — | Game-defined damage category. |
enabled | boolean | true | — | Whether the actor can apply its behaviour. |
Events:
None.
Components:
root: box
json
{
"class": "DamageVolume",
"position": [
0,
0,
0
],
"props": {
"halfExtents": [
1,
1,
1
],
"damagePerSecond": 10,
"interval": 1,
"type": "",
"enabled": true
}
}PhysicsVolume
Level class: PhysicsVolume. Category: Volumes.
Highest-priority overlapping volume controls bodyless gravity and falling speed.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
halfExtents | vec3 | [1,1,1] | metres | Box half sizes [x,y,z], metres, read at spawn. |
gravityScale | number | 1 | — | Gravity multiplier. |
terminalVelocity | number | 55 | min 0; metres/second | Maximum falling speed in metres/second for bodyless Velocity entities. |
water | boolean | false | — | Halves requested horizontal character speed; no character buoyancy. |
priority | number | 0 | — | Highest overlapping priority wins; earlier spawn breaks ties. |
Events:
None.
Components:
root: box
json
{
"class": "PhysicsVolume",
"position": [
0,
0,
0
],
"props": {
"halfExtents": [
1,
1,
1
],
"gravityScale": 1,
"terminalVelocity": 55,
"water": false,
"priority": 0
}
}ForceField
Level class: ForceField. Category: World.
Radial physics force source with an optional initial impulse.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
radius | number | 5 | min 0; metres | Effect radius. |
impulseStrength | number | 10 | newton-seconds | One-shot impulse magnitude. |
forceStrength | number | 0 | newtons | Continuous force magnitude. |
falloff | enum | "constant" | constant, linear | Distance attenuation. |
fireOnBeginPlay | boolean | false | — | Fire once on the first authority step. |
active | boolean | true | — | Enable continuous force. |
Events:
None.
Components:
root: sceneforce: scene
json
{
"class": "ForceField",
"position": [
0,
0,
0
],
"props": {
"radius": 5,
"impulseStrength": 10,
"forceStrength": 0,
"falloff": "constant",
"fireOnBeginPlay": false,
"active": true
}
}AudioVolume
Level class: AudioVolume. Category: World.
Solo pawn reverb box; skipped in rooms because authority has no single listener. Exclusively owns audio.fx on each named bus.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
halfExtents | vec3 | [1,1,1] | min 0; metres | Box half sizes. |
reverb | enum | "room" | dry, room, hall, cave, underwater, radio, muffled, arena | Bus effect preset. |
bus | enum | "sfx" | master, music, sfx, voice, ui | Mixer bus exclusively owned by this module. |
fadeMs | number | 300 | min 0; milliseconds | Transition duration. |
priority | number | 0 | — | Higher priority wins; ties use spawn order. |
Events:
None.
Components:
root: box
json
{
"class": "AudioVolume",
"position": [
0,
0,
0
],
"props": {
"halfExtents": [
1,
1,
1
],
"reverb": "room",
"bus": "sfx",
"fadeMs": 300,
"priority": 0
}
}PhysicsJoint
Level class: PhysicsJoint. Category: World.
Physics joint between named actor roots or an actor and the world.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
kind | enum | "hinge" | hinge, slider, point, fixed, distance | Joint family. |
actorA | string | "" | — | First body actor name; empty uses own root. |
actorB | string | "" | — | Second body actor name; empty anchors to world. |
axis | vec3 | [0,1,0] | — | Constraint-local axis. |
min | number | null | null | degrees (hinge), metres (slider/distance) | Lower limit; null with null max means free. |
max | number | null | null | degrees (hinge), metres (slider/distance) | Upper limit; null with null min means free. |
motorMode | enum | "off" | off, velocity, position | Initial motor control mode. |
motorTarget | number | 0 | degrees or metres; per second for velocity | Initial motor coordinate or velocity target. |
motorMaxForce | number | 0 | min 0; newtons or newton-metres | Motor force or torque cap; zero uses host default. |
Events:
None.
Components:
root: sceneconstraint: scene
json
{
"class": "PhysicsJoint",
"position": [
0,
0,
0
],
"props": {
"kind": "hinge",
"actorA": "",
"actorB": "",
"axis": [
0,
1,
0
],
"min": null,
"max": null,
"motorMode": "off",
"motorTarget": 0,
"motorMaxForce": 0
}
}NavigationBounds
Level class: NavigationBounds. Category: Volumes.
Shared navigation grid over the union of axis-aligned bounds volumes.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
halfExtents | vec3 | [10,10,10] | min 0.001; metres | Axis-aligned XZ footprint and vertical bake range. |
cellSize | number | 0.5 | min 0.001; metres | Grid cell width. |
agentRadius | number | 0.4 | min 0.001; metres | Obstacle clearance radius. |
maxClimb | number | 0.4 | min 0.001; metres | Maximum traversable height change. |
maxSlope | number | 45 | min 0.001; max 90; degrees | Maximum ground slope. |
build | enum | "physics" | physics, flat | Bake colliders or supply flat ground. |
Events:
None.
Components:
root: scene
json
{
"class": "NavigationBounds",
"position": [
0,
0,
0
],
"props": {
"halfExtents": [
10,
10,
10
],
"cellSize": 0.5,
"agentRadius": 0.4,
"maxClimb": 0.4,
"maxSlope": 45,
"build": "physics"
}
}NavigationModifier
Level class: NavigationModifier. Category: Volumes.
Axis-aligned blocking or costly navigation area, removed on destruction.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
halfExtents | vec3 | [1,1,1] | min 0; metres | Blocked half sizes; navigation uses the XZ footprint. |
areaClass | enum | "null" | null, costly | Null blocks; costly multiplies entry cost (overlaps take the max). |
cost | number | 3 | min 1 | Cell entry multiplier for costly areas. |
Events:
None.
Components:
root: scene
json
{
"class": "NavigationModifier",
"position": [
0,
0,
0
],
"props": {
"halfExtents": [
1,
1,
1
],
"areaClass": "null",
"cost": 3
}
}NavigationLink
Level class: NavigationLink. Category: Volumes.
One-way or two-way navigation connection; AI teleports between its endpoints.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
end | vec3 | [2,0,0] | metres | Relative endpoint, including teleport landing height. |
bidirectional | boolean | true | — | Allow traversal in both directions. |
cost | number | 2 | min 0; metres | Absolute traversal cost. |
Events:
None.
Components:
root: scene
json
{
"class": "NavigationLink",
"position": [
0,
0,
0
],
"props": {
"end": [
2,
0,
0
],
"bidirectional": true,
"cost": 2
}
}PostProcessVolume
Level class: PostProcessVolume. Category: Volumes.
Local post overrides inside a box, composed by ascending priority; requires features: { vfx: true }. Null settings do not override.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
unbound | boolean | false | — | Apply everywhere. |
halfExtents | vec3 | [1,1,1] | min 0; metres | Box half size. |
priority | number | 0 | — | Higher priorities override lower priorities; ties use spawn order. |
blendRadius | number | 0 | min 0; max 0; metres | V1 supports zero only (hard boundary). |
bloom | number | null | null | min 0 | Bloom strength. |
bloomThreshold | number | null | null | min 0 | Bloom threshold in linear light. |
bloomRadius | number | null | null | min 0 | Bloom radius. |
exposure | number | null | null | min 0 | Exposure multiplier. |
toneMapping | enum | null | null | none, aces, agx, neutral | Tone mapping operator. |
contrast | number | null | null | min 0 | Contrast multiplier. |
saturation | number | null | null | min 0 | Saturation multiplier. |
tint | vec3 | null | null | min 0 | Linear RGB grade. |
vignette | number | null | null | min 0; max 1 | Vignette strength. |
fogColor | vec3 | null | null | min 0 | Linear RGB fog color. |
fogDensity | number | null | null | min 0 | Exponential fog density. |
Events:
None.
Components:
root: box
json
{
"class": "PostProcessVolume",
"position": [
0,
0,
0
],
"props": {
"unbound": false,
"halfExtents": [
1,
1,
1
],
"priority": 0,
"blendRadius": 0,
"bloom": null,
"bloomThreshold": null,
"bloomRadius": null,
"exposure": null,
"toneMapping": null,
"contrast": null,
"saturation": null,
"tint": null,
"vignette": null,
"fogColor": null,
"fogDensity": null
}
}HeightFog
Level class: HeightFog. Category: World.
Exponential fog composed before volume post overrides; no height falloff. Requires features: { vfx: true }.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
fogDensity | number | 0.02 | min 0 | Exponential fog density. |
fogColor | vec3 | [0.5,0.55,0.6] | min 0 | Fog linear RGB color. |
Events:
None.
Components:
root: scene
json
{
"class": "HeightFog",
"position": [
0,
0,
0
],
"props": {
"fogDensity": 0.02,
"fogColor": [
0.5,
0.55,
0.6
]
}
}Pickup
Level class: Pickup. Category: Gameplay.
Player reward with optional spinning and bobbing mesh.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
give | object | object | object | enum | {"heal":25} | — | Reward; item rewards require a shared inventory. |
respawnSeconds | number | 0 | seconds | Delay before becoming collectible again; zero or negative means never. |
spin | number | 90 | degrees/second | Rotation about +Y, degrees/second. |
bob | number | 0.1 | min 0; metres | Vertical amplitude in metres; one cycle per second. |
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
Events:
onPickedUp: Called when a player successfully collects the reward.
Components:
root: spherespin: scene
json
{
"class": "Pickup",
"position": [
0,
0,
0
],
"props": {
"give": {
"heal": 25
},
"respawnSeconds": 0,
"spin": 90,
"bob": 0.1,
"asset": ""
}
}Interactable
Level class: Interactable. Category: Gameplay.
Ready interaction point; derive a class with onInteract for game-specific behavior.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
prompt | string | "E: interact" | — | HUD prompt for the nearest eligible interaction. |
range | number | 2 | min 0; metres | Planar reach and maximum vertical separation in metres. |
facingDegrees | number | 75 | min 0; max 180; degrees | Maximum angle from camera forward, degrees. |
action | string | "E" | — | Input action/key; E also accepts Tap. |
once | boolean | false | — | Allow use once per player. |
enabled | boolean | true | — | Whether the actor can apply its behaviour. |
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
Events:
onInteract: Called when an eligible player activates the interaction.
Components:
root: sceneinteraction: scene
json
{
"class": "Interactable",
"position": [
0,
0,
0
],
"props": {
"prompt": "E: interact",
"range": 2,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": ""
}
}Door
Level class: Door. Category: Gameplay.
Interactable kinematic door.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
prompt | string | "E: open door" | — | HUD prompt for the nearest eligible interaction. |
range | number | 2 | min 0; metres | Planar reach and maximum vertical separation in metres. |
facingDegrees | number | 75 | min 0; max 180; degrees | Maximum angle from camera forward, degrees. |
action | string | "E" | — | Input action/key; E also accepts Tap. |
once | boolean | false | — | Allow interaction once per player; normally leave false so it can toggle. |
enabled | boolean | true | — | Whether the actor can apply its behaviour. |
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
openOffset | vec3 | [0,2,0] | metres | World-relative slide offset from the closed spawn position. |
openYaw | number | 0 | degrees | Degrees to rotate instead of sliding when nonzero. |
seconds | number | 1 | min 0; seconds | Duration in seconds. |
lockedBy | string | "" | — | Inventory item required; empty means unlocked. Cleared on successful unlock. |
consumeKey | boolean | false | — | Remove one required item when unlocking. |
autoCloseSeconds | number | 0 | min 0; seconds | Wait after opening before closing; zero disables automatic close. |
startOpen | boolean | false | — | Start at the open pose. |
Events:
onInteract: Called when an eligible player activates the interaction.
Components:
root: boxinteraction: scene
json
{
"class": "Door",
"position": [
0,
0,
0
],
"props": {
"prompt": "E: open door",
"range": 2,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": "",
"openOffset": [
0,
2,
0
],
"openYaw": 0,
"seconds": 1,
"lockedBy": "",
"consumeKey": false,
"autoCloseSeconds": 0,
"startOpen": false
}
}Spawner
Level class: Spawner. Category: Gameplay.
Spawn registered actor classes in gameplay waves; destruction counts as a kill on the next fixed step.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
actorClass | string | "" | — | Registered class name to spawn; must be supplied. |
points | string | "" | — | TargetPoint tag; empty or no matches uses this actor pose. |
waves | array | [{"count":1,"every":1}] | — | Wave counts, spawn interval and optional pre-wave delay in seconds. |
maxAlive | number | 8 | min 0 | Maximum living wave children at once. |
autoStart | boolean | true | — | Start waves at beginPlay. |
Events:
None.
Components:
root: scene
json
{
"class": "Spawner",
"position": [
0,
0,
0
],
"props": {
"actorClass": "",
"points": "",
"waves": [
{
"count": 1,
"every": 1
}
],
"maxAlive": 8,
"autoStart": true
}
}CheckpointVolume
Level class: CheckpointVolume. Category: Gameplay.
Ordered player respawn checkpoint, or a lap gate owned by a circuit race.
Lap gates use swept plane crossings, including fast movers. Overlap never awards progress. Place all gates before calling race.init(ctx); name them in dense order in createCircuitRace({id:'track',laps:2,maxRacers:4,gates:['start','turn']}). The race id must match every gate's raceId. Gate zero is the start; the first expected gate is one. Yaw is degrees in a level file; local +Z is forward. once is ignored in lap mode. respawnOffset is a local offset in metres, rotated by gate yaw: negative Z is behind, positive Y is above.
Lap-mode level entries (inside a format:1 level):
json
[
{"class":"CheckpointVolume","name":"start","position":[0,0,0],"scale":[3,2,1],"props":{"mode":"lap","raceId":"track","order":0,"respawnOffset":[0,1,-2]}},
{"class":"CheckpointVolume","name":"turn","position":[0,0,20],"yaw":180,"scale":[3,2,1],"props":{"mode":"lap","raceId":"track","order":1,"respawnOffset":[0,1,-2]}}
]Register pawns with race.register(ctx,{racer:1,seat:0,entity:ctx.player}) and step race.system. race.respawn(ctx,1) preserves gate/lap progress and uses respawnPlayer to clear velocity and reset view. A bounded nine-slot grid checks racerRadius (default 0.5m), including racers in other circuits; if occupied, respawn returns false and the system holds progress and retries every 0.25 seconds. The onRespawn(ctx,progress) option is the adapter hook for a future vehicle simulation reset.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
mode | enum | "ordered" | ordered, lap | Ordered respawn checkpoint or swept circuit gate. |
raceId | string | "" | — | Lap mode: matching createCircuitRace id. |
respawnOffset | vec3 | [0,0,0] | metres | Lap mode: local safe-pose offset in metres, rotated by gate yaw. |
forwardOnly | boolean | true | — | Lap mode: accept crossings toward local +Z only. |
order | number | 0 | — | Ordered mode: progression rank; a lower rank cannot replace a higher one. Lap mode: dense gate index 0..N-1; zero is start. |
once | boolean | false | — | Allow use once per player in ordered mode; ignored in lap mode. |
Events:
None.
Components:
root: box
json
{
"class": "CheckpointVolume",
"position": [
0,
0,
0
],
"props": {
"mode": "ordered",
"raceId": "",
"respawnOffset": [
0,
0,
0
],
"forwardOnly": true,
"order": 0,
"once": false
}
}JumpPad
Level class: JumpPad. Category: Gameplay.
Launch an entering character body or entity carrying Velocity.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
launch | vec3 | [0,10,0] | metres/second | Entering character/Velocity entity launch velocity [x,y,z], metres/second. |
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
scale | vec3 | [2,1,2] | — | Mesh scale only; collider remains in world metres. |
tint | color | [1,1,1] | min 0; max 1 | Mesh RGB material colour. |
Events:
None.
Components:
root: boxmesh: mesh
json
{
"class": "JumpPad",
"position": [
0,
0,
0
],
"props": {
"launch": [
0,
10,
0
],
"asset": "",
"scale": [
2,
1,
2
],
"tint": [
1,
1,
1
]
}
}MovingPlatform
Level class: MovingPlatform. Category: Gameplay.
Kinematic box moved by the reusable path component.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
points | array | [[0,2,0]] | — | Path offsets [x,y,z] relative to spawn; legacy {x,y,z} objects are also accepted. |
seconds | number | 2 | min 0; seconds | Positive whole-path traversal time in seconds; zero is invalid. |
mode | enum | "pingPong" | once, loop, pingPong | Path traversal mode. |
waitSeconds | number | 0 | min 0; seconds | Pause at path endpoints, seconds. |
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
Events:
None.
Components:
root: boxmovement: scene
json
{
"class": "MovingPlatform",
"position": [
0,
0,
0
],
"props": {
"points": [
[
0,
2,
0
]
],
"seconds": 2,
"mode": "pingPong",
"waitSeconds": 0,
"asset": ""
}
}Projectile
Level class: Projectile. Category: Gameplay.
Swept projectile that damages actors or players, attributes hits to its owner and stops after its bounce budget.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
speed | number | 20 | min 0; metres/second | Launch speed along local +Z, metres/second. |
homingTarget | number | 0 | min 0 | Target ECS entity id; zero disables homing. |
damage | number | 10 | min 0 | Damage applied on a swept hit. |
hitResponse | enum | "damage" | damage, effect, both | Hit handling: damage, effect hook only, or damage then effect hook. |
gravityScale | number | 0 | — | Multiplier on world gravity for swept motion. |
bounces | number | 0 | min 0 | Number of allowed bounces before stopping. |
bounciness | number | 0.6 | min 0; max 1 | Reflected velocity multiplier. |
lifeSpan | number | 5 | min 0; seconds | Seconds before destruction; zero means forever. |
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
Events:
onProjectileHit: Definition hook (self, hit, ctx, stopped); once per effect/both sweep hit before terminal destroy. Hit coordinates are borrowed; bounces report stopped=false.
Components:
root: spheremovement: scene
json
{
"class": "Projectile",
"position": [
0,
0,
0
],
"props": {
"speed": 20,
"homingTarget": 0,
"damage": 10,
"hitResponse": "damage",
"gravityScale": 0,
"bounces": 0,
"bounciness": 0.6,
"lifeSpan": 5,
"asset": ""
}
}Custom impacts
defineProjectile(name, spec) retains flight, bounces and lifespan. hitResponse defaults to damage; effect requires onProjectileHit at definition (instance overrides are checked at spawn). both applies damage then the hook once per accepted hit, before terminal destruction. Bounces report stopped=false. Hooks may destroy self or target safely; hit coordinates are borrowed. Entity 0 is terrain. The ray sweep excludes the owner entity and projectile body, but not attached owner children; it has no finite radius.
Push a dynamic body without Health; no vehicle-specific helpers are needed:
ts
import { defineProjectile } from 'gameable';
const PushShot = defineProjectile('PushShot', {
props: { damage: 0, hitResponse: 'effect' },
onProjectileHit(self, hit, ctx, stopped) {
// No allocation or retained pooled coordinates; off-centre impulses also impart spin.
ctx.physics.applyImpulse(hit.entity, 0, 12, 0, hit.point.x, hit.point.y, hit.point.z);
},
});HingedDoor
Level class: HingedDoor. Category: Gameplay.
A painted single leaf door with a fixed edge hinge.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
hinge | enum | "left" | left, right | Hinge edge viewed from local +Z. |
swingDegrees | number | 95 | degrees | Full opening angle. |
swingAway | boolean | true | — | Choose a swing away from the interacting player. |
Plus the Door kit props.
Events:
onInteract: An eligible player presses the interaction action.onOpened: The panel reaches fully open.onClosed: The panel reaches fully closed.onLocked: A player attempts to open without the key.onUnlocked: A player successfully unlocks the door.onPassed: A player crosses the closed doorway plane while not closed.
Components:
root: sceneleaf: boxleafPaint: boxleafTop: boxleafBottom: boxleafSeam: boxinteraction: scene
json
{
"class": "HingedDoor",
"position": [
0,
0,
0
],
"props": {
"width": 1,
"height": 2.1,
"thickness": 0.1,
"range": 2,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": "",
"prompt": "E: open",
"seconds": 0.6,
"startOpen": false,
"autoCloseSeconds": 0,
"lockedBy": "",
"consumeKey": false,
"stayOpen": false,
"openPrompt": "E: open",
"closePrompt": "E: close",
"lockedPrompt": "Locked",
"lockedMessage": "",
"openSound": "",
"closeSound": "",
"lockedSound": "",
"colour": [
0.45,
0.28,
0.13
],
"look": "wood",
"opensWhen": [],
"toggledBy": "interact",
"hinge": "left",
"swingDegrees": 95,
"swingAway": true
}
}SlidingDoor
Level class: SlidingDoor. Category: Gameplay.
One or two kinematic panels sliding along the door face.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
colour | color | [0.35,0.4,0.45] | min 0; max 1 | Linear RGB panel colour. |
look | enum | "metal" | wood, metal, none | Built-in painted panel detail; asset overrides this. |
panels | number | 1 | min 1; max 2 | One full-width panel or two opposing half panels. |
slide | enum | "right" | left, right, up | Single panel travel direction in local space. |
travel | number | 0 | min 0; metres | Distance per panel; zero uses panel width. |
Plus the Door kit props.
Events:
onInteract: An eligible player presses the interaction action.onOpened: The panel reaches fully open.onClosed: The panel reaches fully closed.onLocked: A player attempts to open without the key.onUnlocked: A player successfully unlocks the door.onPassed: A player crosses the closed doorway plane while not closed.
Components:
root: sceneleaf: boxsecond: boxleafPaint: boxleafTop: boxleafBottom: boxleafSeam: boxsecondPaint: boxsecondTop: boxsecondBottom: boxsecondSeam: boxinteraction: scene
json
{
"class": "SlidingDoor",
"position": [
0,
0,
0
],
"props": {
"width": 1,
"height": 2.1,
"thickness": 0.1,
"range": 2,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": "",
"prompt": "E: open",
"seconds": 0.6,
"startOpen": false,
"autoCloseSeconds": 0,
"lockedBy": "",
"consumeKey": false,
"stayOpen": false,
"openPrompt": "E: open",
"closePrompt": "E: close",
"lockedPrompt": "Locked",
"lockedMessage": "",
"openSound": "",
"closeSound": "",
"lockedSound": "",
"colour": [
0.35,
0.4,
0.45
],
"look": "metal",
"opensWhen": [],
"toggledBy": "interact",
"panels": 1,
"slide": "right",
"travel": 0
}
}DoubleDoor
Level class: DoubleDoor. Category: Gameplay.
Two opposing hinged leaves meeting in the centre of a doorway.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
width | number | 2 | min 0; metres | Closed doorway width. |
swingDegrees | number | 90 | min 0; degrees | Full opening angle of each leaf. |
swingAway | boolean | true | — | Swing both leaves away from the interacting player. |
Plus the Door kit props.
Events:
onInteract: An eligible player presses the interaction action.onOpened: The panel reaches fully open.onClosed: The panel reaches fully closed.onLocked: A player attempts to open without the key.onUnlocked: A player successfully unlocks the door.onPassed: A player crosses the closed doorway plane while not closed.
Components:
root: sceneleaf: boxsecond: boxleafPaint: boxleafTop: boxleafBottom: boxleafSeam: boxsecondPaint: boxsecondTop: boxsecondBottom: boxsecondSeam: boxinteraction: scene
json
{
"class": "DoubleDoor",
"position": [
0,
0,
0
],
"props": {
"width": 2,
"height": 2.1,
"thickness": 0.1,
"range": 2,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": "",
"prompt": "E: open",
"seconds": 0.6,
"startOpen": false,
"autoCloseSeconds": 0,
"lockedBy": "",
"consumeKey": false,
"stayOpen": false,
"openPrompt": "E: open",
"closePrompt": "E: close",
"lockedPrompt": "Locked",
"lockedMessage": "",
"openSound": "",
"closeSound": "",
"lockedSound": "",
"colour": [
0.45,
0.28,
0.13
],
"look": "wood",
"opensWhen": [],
"toggledBy": "interact",
"swingDegrees": 90,
"swingAway": true
}
}AutomaticDoor
Level class: AutomaticDoor. Category: Gameplay.
An automatic double sliding door with a departure hold and key checks.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
range | number | 2.5 | min 0; metres | Planar reach and maximum vertical separation in metres. |
seconds | number | 0.4 | min 0; seconds | Time to move fully open or closed. |
lockedMessage | string | "Locked" | — | Notice on locked interaction; empty disables it. |
colour | color | [0.35,0.4,0.45] | min 0; max 1 | Linear RGB panel colour. |
look | enum | "metal" | wood, metal, none | Built-in painted panel detail; asset overrides this. |
toggledBy | enum | "proximity" | interact, signal, proximity | Interaction, all named signals, or nearby players. |
panels | number | 2 | min 1; max 2 | One full-width panel or two opposing half panels. |
slide | enum | "right" | left, right, up | Single panel travel direction in local space. |
travel | number | 0 | min 0; metres | Distance per panel; zero uses panel width. |
holdSeconds | number | 0.5 | min 0; seconds | Hold open after the last player leaves. |
Plus the Door kit props.
Events:
onInteract: An eligible player presses the interaction action.onOpened: The panel reaches fully open.onClosed: The panel reaches fully closed.onLocked: A player attempts to open without the key.onUnlocked: A player successfully unlocks the door.onPassed: A player crosses the closed doorway plane while not closed.
Components:
root: sceneleaf: boxsecond: boxleafPaint: boxleafTop: boxleafBottom: boxleafSeam: boxsecondPaint: boxsecondTop: boxsecondBottom: boxsecondSeam: boxinteraction: scene
json
{
"class": "AutomaticDoor",
"position": [
0,
0,
0
],
"props": {
"width": 1,
"height": 2.1,
"thickness": 0.1,
"range": 2.5,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": "",
"prompt": "E: open",
"seconds": 0.4,
"startOpen": false,
"autoCloseSeconds": 0,
"lockedBy": "",
"consumeKey": false,
"stayOpen": false,
"openPrompt": "E: open",
"closePrompt": "E: close",
"lockedPrompt": "Locked",
"lockedMessage": "Locked",
"openSound": "",
"closeSound": "",
"lockedSound": "",
"colour": [
0.35,
0.4,
0.45
],
"look": "metal",
"opensWhen": [],
"toggledBy": "proximity",
"panels": 2,
"slide": "right",
"travel": 0,
"holdSeconds": 0.5
}
}Gate
Level class: Gate. Category: Gameplay.
A metal barred gate rising by its height minus the remaining lip.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
width | number | 3 | min 0; metres | Closed doorway width. |
height | number | 3 | min 0; metres | Closed doorway height. |
seconds | number | 2 | min 0; seconds | Time to move fully open or closed. |
colour | color | [0.35,0.4,0.45] | min 0; max 1 | Linear RGB panel colour. |
look | enum | "metal" | wood, metal, none | Built-in painted panel detail; asset overrides this. |
toggledBy | enum | "signal" | interact, signal, proximity | Interaction, all named signals, or nearby players. |
lip | number | 0.1 | min 0; metres | Amount remaining below the ceiling when open. |
Plus the Door kit props.
Events:
onInteract: An eligible player presses the interaction action.onOpened: The panel reaches fully open.onClosed: The panel reaches fully closed.onLocked: A player attempts to open without the key.onUnlocked: A player successfully unlocks the door.onPassed: A player crosses the closed doorway plane while not closed.
Components:
root: sceneleaf: boxbar0: boxbar1: boxbar2: boxbar3: boxbar4: boxbar5: boxbar6: boxbar7: boxbar8: boxinteraction: scene
json
{
"class": "Gate",
"position": [
0,
0,
0
],
"props": {
"width": 3,
"height": 3,
"thickness": 0.1,
"range": 2,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": "",
"prompt": "E: open",
"seconds": 2,
"startOpen": false,
"autoCloseSeconds": 0,
"lockedBy": "",
"consumeKey": false,
"stayOpen": false,
"openPrompt": "E: open",
"closePrompt": "E: close",
"lockedPrompt": "Locked",
"lockedMessage": "",
"openSound": "",
"closeSound": "",
"lockedSound": "",
"colour": [
0.35,
0.4,
0.45
],
"look": "metal",
"opensWhen": [],
"toggledBy": "signal",
"lip": 0.1
}
}TrapDoor
Level class: TrapDoor. Category: Gameplay.
A floor hatch swinging down so its supporting surface clears the opening.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
width | number | 1.5 | min 0; metres | Closed doorway width. |
height | number | 1.5 | min 0; metres | Hatch depth along local Z. |
toggledBy | enum | "signal" | interact, signal, proximity | Interaction, all named signals, or nearby players. |
swingDegrees | number | 90 | min 0; degrees | Downward opening angle about local X. |
Plus the Door kit props.
Events:
onInteract: An eligible player presses the interaction action.onOpened: The panel reaches fully open.onClosed: The panel reaches fully closed.onLocked: A player attempts to open without the key.onUnlocked: A player successfully unlocks the door.
Components:
root: sceneleaf: boxsurface: scenesurfacePaint: boxsurfaceTop: boxsurfaceBottom: boxsurfaceSeam: boxinteraction: scene
json
{
"class": "TrapDoor",
"position": [
0,
0,
0
],
"props": {
"width": 1.5,
"height": 1.5,
"thickness": 0.1,
"range": 2,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": "",
"prompt": "E: open",
"seconds": 0.6,
"startOpen": false,
"autoCloseSeconds": 0,
"lockedBy": "",
"consumeKey": false,
"stayOpen": false,
"openPrompt": "E: open",
"closePrompt": "E: close",
"lockedPrompt": "Locked",
"lockedMessage": "",
"openSound": "",
"closeSound": "",
"lockedSound": "",
"colour": [
0.45,
0.28,
0.13
],
"look": "wood",
"opensWhen": [],
"toggledBy": "signal",
"swingDegrees": 90
}
}Lever
Level class: Lever. Category: Gameplay.
A keyed or momentary lever supplying an on/off signal to named doors.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
prompt | string | "E: interact" | — | HUD prompt for the nearest eligible interaction. |
range | number | 2 | min 0; metres | Planar reach and maximum vertical separation in metres. |
facingDegrees | number | 75 | min 0; max 180; degrees | Maximum angle from camera forward, degrees. |
action | string | "E" | — | Input action/key; E also accepts Tap. |
once | boolean | false | — | Allow only one successful flip on, including startOn. |
enabled | boolean | true | — | Whether the actor can apply its behaviour. |
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
startOn | boolean | false | — | Start with the signal on and the handle fully pulled. |
resetSeconds | number | 0 | min 0; seconds | Time before springing back off; zero keeps the chosen state. |
seconds | number | 0.25 | min 0; seconds | Time for a full handle flip. |
onPrompt | string | "E: pull lever" | — | Prompt while the signal is off. |
offPrompt | string | "E: reset lever" | — | Prompt while the signal is on. |
switchSound | string | "" | — | Manifest audio id on each signal change; empty is silent. |
colour | color | [0.455,0.925,0.776] | min 0; max 1 | Linear RGB switch colour. |
Plus the Door kit props.
Events:
onInteract: An eligible player presses E or Tap.onSwitched: The signal changes; who is the last activator, or zero for a startOn reset.
Components:
root: scenebase: boxhandle: boxinteraction: scene
json
{
"class": "Lever",
"position": [
0,
0,
0
],
"props": {
"prompt": "E: interact",
"range": 2,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": "",
"startOn": false,
"resetSeconds": 0,
"seconds": 0.25,
"onPrompt": "E: pull lever",
"offPrompt": "E: reset lever",
"lockedBy": "",
"lockedPrompt": "Locked",
"lockedMessage": "",
"consumeKey": false,
"switchSound": "",
"colour": [
0.455,
0.925,
0.776
]
}
}PressurePlate
Level class: PressurePlate. Category: Gameplay.
A sinking floor plate that signals while enough players or named actors stand on it.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
width | number | 1 | min 0; metres | Top footprint width. |
depth | number | 1 | min 0; metres | Top footprint depth. |
weight | number | 1 | min 1; occupants | Minimum number of occupants required. |
pressedBy | array | [] | — | Additional actor instance or class names that count as occupants. |
holdSeconds | number | 0 | min 0; seconds | Keep the signal on after occupancy drops below the required weight. |
once | boolean | false | — | Latch the signal on forever after the first press. |
pressSound | string | "" | — | Manifest audio id when pressed; empty is silent. |
releaseSound | string | "" | — | Manifest audio id when released; empty is silent. |
colour | color | [0.455,0.925,0.776] | min 0; max 1 | Linear RGB plate colour. |
Events:
onPressed: The signal turns on after reaching the required weight.onReleased: The signal turns off after the hold time expires.
Components:
root: sceneplate: box
json
{
"class": "PressurePlate",
"position": [
0,
0,
0
],
"props": {
"width": 1,
"depth": 1,
"weight": 1,
"pressedBy": [],
"holdSeconds": 0,
"once": false,
"pressSound": "",
"releaseSound": "",
"colour": [
0.455,
0.925,
0.776
]
}
}LevelDoor
Level class: LevelDoor. Category: Gameplay.
A swinging or sliding door that takes players to another level.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
hinge | enum | "left" | left, right | Hinge edge viewed from local +Z. |
swingDegrees | number | 95 | degrees | Full opening angle. |
swingAway | boolean | true | — | Choose a swing away from the interacting player. |
opens | enum | "swing" | swing, slide | Leaf movement. |
toLevel | string | "" | — | Declared destination level; empty fires only onTravel. |
toPlayerStart | string | "" | — | Arrival PlayerStart or TargetPoint name; empty uses the first PlayerStart. |
travel | enum | "party" | party, player | Move the whole party or only the crossing player. |
requireOpen | boolean | true | — | Require open or opening; false accepts any unlocked door phase. |
travelPrompt | string | "Walk through to travel" | — | Prompt once fully open. |
Plus the Door kit props.
Events:
onInteract: An eligible player presses the interaction action.onOpened: The panel reaches fully open.onClosed: The panel reaches fully closed.onLocked: A player attempts to open without the key.onUnlocked: A player successfully unlocks the door.onPassed: A player crosses the closed doorway plane while not closed.onTravel: Once per accepted crossing, before loading; may change toLevel.
Components:
root: sceneleaf: boxsecond: boxleafPaint: boxleafTop: boxleafBottom: boxleafSeam: boxsecondPaint: boxsecondTop: boxsecondBottom: boxsecondSeam: boxinteraction: scene
json
{
"class": "LevelDoor",
"position": [
0,
0,
0
],
"props": {
"width": 1,
"height": 2.1,
"thickness": 0.1,
"range": 2,
"facingDegrees": 75,
"action": "E",
"once": false,
"enabled": true,
"asset": "",
"prompt": "E: open",
"seconds": 0.6,
"startOpen": false,
"autoCloseSeconds": 0,
"lockedBy": "",
"consumeKey": false,
"stayOpen": false,
"openPrompt": "E: open",
"closePrompt": "E: close",
"lockedPrompt": "Locked",
"lockedMessage": "",
"openSound": "",
"closeSound": "",
"lockedSound": "",
"colour": [
0.45,
0.28,
0.13
],
"look": "wood",
"opensWhen": [],
"toggledBy": "interact",
"hinge": "left",
"swingDegrees": 95,
"swingAway": true,
"opens": "swing",
"toLevel": "",
"toPlayerStart": "",
"travel": "party",
"requireOpen": true,
"travelPrompt": "Walk through to travel"
}
}VehicleGripVolume
Level class: VehicleGripVolume. Category: Volumes.
Timed spherical oil area; overlapping vehicle chassis refresh minimum-combined tyre grip, with recovery after exit. No damage.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
radius | number | 3 | min 0; metres | Sphere radius at unit scale. |
duration | number | 8 | min 0; seconds | Area lifetime from spawn; zero expires immediately. |
gripScale | number | 0.15 | min 0; max 1 | Tyre grip while affected; overlapping sources combine by minimum. |
recovery | number | 1 | min 0; seconds | Grip drop retained after last contact; zero means current step only. |
Events:
None.
Components:
root: sphere
json
{
"class": "VehicleGripVolume",
"position": [
0,
0,
0
],
"props": {
"radius": 3,
"duration": 8,
"gripScale": 0.15,
"recovery": 1
}
}SoftPlatform
Level class: SoftPlatform. Category: Gameplay.
One-way top surface for characterMovement; supports per-character drop-through.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
scale | vec3 | [4,0.3,2] | — | Mesh scale only; collider remains in world metres. |
tint | color | [1,1,1] | min 0; max 1 | Mesh RGB material colour. |
Events:
None.
Components:
root: boxmesh: mesh
json
{
"class": "SoftPlatform",
"position": [
0,
0,
0
],
"props": {
"asset": "",
"scale": [
4,
0.3,
2
],
"tint": [
1,
1,
1
]
}
}MeshActor
Level class: MeshActor. Category: Scene.
A prop mesh with optional fixed box collision.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
asset | string | "" | — | Manifest mesh id; empty means no mesh. |
scale | vec3 | [1,1,1] | — | Mesh scale only; collider remains in world metres. |
tint | color | [1,1,1] | min 0; max 1 | Mesh RGB material colour. |
collision | enum | "none" | none, block | Optional fixed box collider; authored halfExtents, not manifest geometry. |
halfExtents | vec3 | [0.5,0.5,0.5] | metres | Positive box half sizes [x,y,z], world metres, read at spawn. |
Events:
None.
Components:
root: scenemesh: meshcollision: box
json
{
"class": "MeshActor",
"position": [
0,
0,
0
],
"props": {
"asset": "",
"scale": [
1,
1,
1
],
"tint": [
1,
1,
1
],
"collision": "none",
"halfExtents": [
0.5,
0.5,
0.5
]
}
}CharacterActor
Level class: CharacterActor. Category: Scene.
An NPC character controller and splat bundle that stands and animates.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
character | string | "" | — | Manifest character-bundle id; empty means no bundle. |
animation | string | "" | — | Body-layer clip; empty keeps bundle idle. |
Events:
None.
Components:
root: character
json
{
"class": "CharacterActor",
"position": [
0,
0,
0
],
"props": {
"character": "",
"animation": ""
}
}PointLight
Level class: PointLight. Category: Scene.
Placeable light placeholder until the host accepts guest light commands.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
color | color | [1,1,1] | min 0; max 1 | RGB light colour, preserved only; no light command exists. |
intensity | number | 1 | min 0 | Light intensity, preserved only. |
radius | number | 10 | min 0; metres | Light range in metres, preserved only. |
Events:
None.
Components:
root: scene
json
{
"class": "PointLight",
"position": [
0,
0,
0
],
"props": {
"color": [
1,
1,
1
],
"intensity": 1,
"radius": 10
}
}SpotLight
Level class: SpotLight. Category: Scene.
Entity-owned spotlight with optional active-camera attachment.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
color | color | [1,1,1] | min 0; max 1 | RGB light colour. |
intensity | number | 20 | min 0 | Light intensity. |
radius | number | 12 | min 0; metres | Light range. |
angle | number | 30 | min 0; max 90; degrees | Cone half angle. |
penumbra | number | 0.4 | min 0; max 1 | Edge softness. |
cameraAttached | boolean | false | — | Attach beam to the active camera view. |
enabled | boolean | true | — | Whether the light illuminates. |
Events:
None.
Components:
root: scene
json
{
"class": "SpotLight",
"position": [
0,
0,
0
],
"props": {
"color": [
1,
1,
1
],
"intensity": 20,
"radius": 12,
"angle": 30,
"penumbra": 0.4,
"cameraAttached": false,
"enabled": true
}
}DirectionalLight
Level class: DirectionalLight. Category: Scene.
Placeable light placeholder until the host accepts guest light commands.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
color | color | [1,1,1] | min 0; max 1 | RGB light colour, preserved only; no light command exists. |
intensity | number | 1 | min 0 | Light intensity, preserved only. |
Events:
None.
Components:
root: scene
json
{
"class": "DirectionalLight",
"position": [
0,
0,
0
],
"props": {
"color": [
1,
1,
1
],
"intensity": 1
}
}WorldText
Level class: WorldText. Category: Scene.
Placeable world text anchored to its component entity.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
text | string | "" | — | World text contents. |
size | enum | "lg" | sm, md, lg, xl, display | Text size; defaults to lg. |
tone | enum | "default" | default, muted, accent, danger | Semantic text tone; defaults to default. |
Events:
None.
Components:
root: scene
json
{
"class": "WorldText",
"position": [
0,
0,
0
],
"props": {
"text": "",
"size": "lg",
"tone": "default"
}
}AmbientSound
Level class: AmbientSound. Category: Scene.
Entity-attached ambient sound, looping and autoplaying by default.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
sound | string | "" | — | Manifest audio id. |
volume | number | 1 | min 0; max 1 | Linear playback gain. |
looping | boolean | true | — | Loop until stopped or destroyed. |
autoPlay | boolean | true | — | Play on the first actor step. |
bus | enum | "sfx" | master, music, sfx, voice, ui | Mixer bus. |
Events:
None.
Components:
root: scenesound: scene
json
{
"class": "AmbientSound",
"position": [
0,
0,
0
],
"props": {
"sound": "",
"volume": 1,
"looping": true,
"autoPlay": true,
"bus": "sfx"
}
}CameraActor
Level class: CameraActor. Category: Scene.
A placeable camera.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
fov | number | 60 | min 0; degrees | Vertical camera field of view, degrees. |
activateOnStart | boolean | false | — | Select this view during construction; last activation wins. |
Events:
None.
Components:
root: scene
json
{
"class": "CameraActor",
"position": [
0,
0,
0
],
"props": {
"fov": 60,
"activateOnStart": false
}
}ParticleEffect
Level class: ParticleEffect. Category: Scene.
Persistent entity-attached built-in particle effect; requires features: { vfx: true }.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
preset | enum | "fire" | hit-sparks, explosion, smoke, fire, magic, dust, trail | Built-in particle preset. |
autoActivate | boolean | true | — | Activate on the first step. |
scale | number | 1 | min 0 | Particle size multiplier. |
intensity | number | 1 | min 0 | Particle count and brightness multiplier. |
tint | vec3 | [1,1,1] | min 0 | Linear RGB; values above one glow. |
Events:
None.
Components:
root: scene
json
{
"class": "ParticleEffect",
"position": [
0,
0,
0
],
"props": {
"preset": "fire",
"autoActivate": true,
"scale": 1,
"intensity": 1,
"tint": [
1,
1,
1
]
}
}Character
Level class: Character. Category: Framework.
Possessable character bundle with walking, jumping and independent facing.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
character | string | "" | — | Manifest character-bundle id; empty means no bundle. |
maxWalkSpeed | number | 6 | min 0; metres/second | Maximum walking speed. |
jumpZVelocity | number | 4.2 | min 0; metres/second | Upward speed on jump (world Y; the option name uses a Z-up convention). |
orientRotationToMovement | boolean | true | — | Turn toward movement; disable to hold a controller yaw with setDesiredRotation. |
rotationRate | number | 540 | min 0; degrees/second | Maximum yaw speed. |
Events:
None.
Components:
root: capsule
json
{
"class": "Character",
"position": [
0,
0,
0
],
"props": {
"character": "",
"maxWalkSpeed": 6,
"jumpZVelocity": 4.2,
"orientRotationToMovement": true,
"rotationRate": 540
}
}FlyingPawn
Level class: FlyingPawn. Category: Framework.
Flying pawn with a kinematic sphere root. WASD or the left touch stick moves horizontally; Space flies up and Control flies down.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
maxSpeed | number | 12 | min 0; metres/second | Maximum flying speed. |
collision | boolean | true | — | Create a blocking sphere body. |
Events:
None.
Components:
root: spheremovement: scene
json
{
"class": "FlyingPawn",
"position": [
0,
0,
0
],
"props": {
"maxSpeed": 12,
"collision": true
}
}Spectator
Level class: Spectator. Category: Framework.
Flying spectator without collision or damageable health.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
maxSpeed | number | 12 | min 0; metres/second | Maximum flying speed. |
Events:
None.
Components:
root: scenemovement: scene
json
{
"class": "Spectator",
"position": [
0,
0,
0
],
"props": {
"maxSpeed": 12
}
}AIController
Level class: AIController. Category: Framework.
AI possession, walking navigation, vehicle driving, racing lines and pursuit.
After possess(ctx, ai, car), use driveTowards(ctx, ai, point, {speed:12}) or followRacingLine(ctx, ai, {actor:'track',component:'line'}, {speed:16,loop:true}). An explicitly ordered array of TargetPoint names can replace the spline reference. chaseVehicle(ctx, ai, suspect, {leadTime:0.6,maxLead:12,ramDistance:5,ramCooldown:4}) uses prediction, speed matching and physical contact. stopDriving(ctx, ai) parks once. Requests require vehicleMovement; intent and tuning are saved in aiMovement.drive. The tuning option accepts lookAheadMin (5m), lookAheadTime (0.45s), steerGain (9), speedGain (0.5), brakeGain (0.3), maxSpeed (30m/s), stuckSeconds (3s), reverseSeconds (1.5s). Recovery aborts after three retries. Destroyed targets/lines or unpossess abort once. Controls run before pawn steps; setFocus never rotates cars. Walking moveTo is unchanged. See the vehicles reference for arrival, preview braking and rival offsets.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
moveSpeed | number | 4 | min 0; metres/second | Desired movement speed. |
acceptanceRadius | number | 0.5 | min 0; metres | Distance at which a move succeeds. |
turnRate | number | 360 | min 0; degrees/second | Maximum pedestrian focus yaw rate; ignored for vehicle pawns. |
possessOnSpawn | string | "" | — | Actor instance name to possess on the first step; empty disables. |
Events:
onNavigationLinkReached: Called with pooled {from,to} after teleporting through a navigation link.onMoveCompleted: Called once when walking or driving succeeds or is aborted, including lost targets and exhausted recovery.
Components:
root: sceneaiMovement: scene
json
{
"class": "AIController",
"position": [
0,
0,
0
],
"props": {
"moveSpeed": 4,
"acceptanceRadius": 0.5,
"turnRate": 360,
"possessOnSpawn": ""
}
}GameState
Level class: GameState. Category: Framework.
Read-only match lifecycle, clock and winners; rulesState normalizes legacy and phased modes to waiting, inProgress or ended.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
phase | string | "" | — | Active phase name; empty outside configured phases. |
round | number | 0 | min 0 | One-based round; zero before start. |
phaseTimeRemaining | number | 0 | min 0; seconds | Fixed-step phase countdown; zero for indefinite phases or sudden death. |
teamScores | array | [] | — | Accumulated points indexed by team id; teamScore reads this lane. |
matchState | enum | "waitingToStart" | waitingToStart, inProgress, waitingPostMatch | Legacy lifecycle names; rulesState exposes the normalized lifecycle. |
rulesState | enum | "waiting" | waiting, inProgress, ended | Normalized lifecycle for all modes: waitingToStart maps to waiting, inProgress stays inProgress, waitingPostMatch maps to ended. |
elapsed | number | 0 | min 0; seconds | Seconds played in this match. |
remaining | number | 0 | min 0; seconds | Time remaining; zero with no time limit. |
winners | array | [] | — | Winning seats, including ties. |
Events:
None.
Components:
root: scene
json
{
"class": "GameState",
"position": [
0,
0,
0
],
"props": {
"phase": "",
"round": 0,
"phaseTimeRemaining": 0,
"teamScores": [],
"matchState": "waitingToStart",
"rulesState": "waiting",
"elapsed": 0,
"remaining": 0,
"winners": []
}
}PlayerController
Level class: PlayerController. Category: Framework.
Player controller possessing the body provided by definition.player.
| Prop | Type | Default | Range | Meaning |
|---|
Events:
None.
Components:
root: sceneframeworkPlayerTick: scene
json
{
"class": "PlayerController",
"position": [
0,
0,
0
],
"props": {}
}PlayerState
Level class: PlayerState. Category: Framework.
Per-player match identity, team and statistics.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
playerName | string | "" | — | Display name supplied at login. |
score | number | 0 | points | Match score. |
kills | number | 0 | min 0 | Other players killed. |
deaths | number | 0 | min 0 | Deaths this match. |
team | number | 0 | — | Game-defined team identifier. |
seat | number | -1 | min -1 | Player seat; -1 means unassigned. |
Events:
None.
Components:
root: scene
json
{
"class": "PlayerState",
"position": [
0,
0,
0
],
"props": {
"playerName": "",
"score": 0,
"kills": 0,
"deaths": 0,
"team": 0,
"seat": -1
}
}GameMode
Level class: GameMode. Category: Framework.
Authority match rules, player login, death scoring and respawning.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
phases | array | [] | — | Ordered phases repeated each round; zero/null duration waits for endPhase. Optional respawn overrides the phase death policy. |
rounds | number | 1 | min 1 | Maximum rounds per match. |
roundsToWin | number | 0 | min 0 | Round victories needed to win early; zero disables. |
teams | array | [] | — | Teams in id order, starting at one; zero means unassigned. |
autoAssign | enum | "none" | balanced, none | Assign joining players to the smallest available team, ties in definition order. |
friendlyFire | boolean | false | — | Whether damage code should allow damage between teammates. |
allowLateJoin | boolean | true | — | Allow new seats after the match starts. |
lastTeamStanding | boolean | false | — | End a round when only one of at least two teams remains alive; use with respawn disabled. |
tieBreak | enum | "draw" | draw, suddenDeath | Finish tied rounds as draws or wait for a unique score leader. |
minPlayers | number | 1 | min 0; players | Logged-in seats required to start. |
startDelay | number | 0 | min 0; seconds | Continuous readiness time before starting. |
matchTime | number | 0 | min 0; seconds | Match duration; zero disables the time limit. |
scoreToWin | number | 0 | min 0; points | Winning score; zero disables the score limit. |
killScore | number | 1 | points | Points awarded for killing another player. |
respawnDelay | number | 3 | seconds | Death-to-respawn delay; negative disables respawn. |
restartAfter | number | 0 | min 0; seconds | Post-match delay; zero leaves the match ended. |
playerControllerClass | string | "PlayerController" | — | Registered controller class spawned at login. |
playerStateClass | string | "PlayerState" | — | Registered player statistics class spawned at login. |
gameStateClass | string | "GameState" | — | Registered match state class spawned with the mode. |
Events:
onPhaseStarted: Phase begins; borrowed payload contains phase, round and winner.onPhaseEnded: Phase ends before the next begins.onRoundStarted: Round begins before its first phase.onRoundEnded: Round finishes with winner (team id or seat), null for draw.onMatchEnded: Match finishes with winner (team id or seat), null for draw.
Components:
root: sceneframeworkMatch: scene
json
{
"class": "GameMode",
"position": [
0,
0,
0
],
"props": {
"phases": [],
"rounds": 1,
"roundsToWin": 0,
"teams": [],
"autoAssign": "none",
"friendlyFire": false,
"allowLateJoin": true,
"lastTeamStanding": false,
"tieBreak": "draw",
"minPlayers": 1,
"startDelay": 0,
"matchTime": 0,
"scoreToWin": 0,
"killScore": 1,
"respawnDelay": 3,
"restartAfter": 0,
"playerControllerClass": "PlayerController",
"playerStateClass": "PlayerState",
"gameStateClass": "GameState"
}
}WheeledVehicle
Level class: WheeledVehicle. Category: Framework.
Possessable four-wheel car with heading-following chase camera. Arcade handling, authority-simulated; no client prediction yet. Four raycast wheels only; visuals use manifest ids.
| Prop | Type | Default | Range | Meaning |
|---|---|---|---|---|
handling | enum | "arcade" | arcade, grippy, drift | Base tuning; explicit fields override the preset. |
collision | enum | null | null | Vehicle | Chassis collision profile. |
halfExtents | vec3 | null | null | min 0; metres | Box chassis half sizes. |
mass | number | null | null | min 0; kilograms | Dynamic chassis mass. |
wheels | array | null | null | — | Exactly four wheels: local mount, radius/restLength/travel in metres, springHz, dampingRatio, steer/driven/handbrake flags. |
maxSpeed | number | null | null | min 0; metres/second | Forward speed cap. |
reverseSpeed | number | null | null | min 0; metres/second | Reverse speed cap. |
acceleration | number | null | null | min 0; metres/second² | Drive acceleration. |
brakeDeceleration | number | null | null | min 0; metres/second² | Braking acceleration. |
reverseAsBrake | boolean | null | null | — | Opposite throttle brakes before reversing. |
steerAngle | number | null | null | min 0; degrees | Maximum steering angle. |
steerRate | number | null | null | min 0; degrees/second | Steering slew rate. |
steeringBySpeed | array | null | null | — | Ascending [speed m/s, steering multiplier] points. |
gripBySlip | array | null | null | — | Ascending [slip, grip multiplier] points. |
rearHandbrakeGrip | number | null | null | min 0; max 1 | Rear grip with handbrake. |
driftRecovery | number | null | null | min 0; seconds | Grip recovery time. |
rollingDrag | number | null | null | min 0; metres/second² | Coasting deceleration. |
downforce | number | null | null | min 0; N/(m/s)² | Speed squared downforce coefficient. |
antiRoll | number | null | null | min 0; N/m | Axle load transfer stiffness. |
uprightAssist | number | null | null | min 0; N m | Bounded upright torque. |
airControl | number | null | null | min 0; max 1 | Airborne assist fraction. |
asset | string | "" | — | Chassis manifest mesh id; empty uses debug chassis. |
wheelAsset | string | "" | — | Wheel manifest mesh id, model axle along local X; empty uses primitive wheels. |
Events:
None.
Components:
root: boxvisual: meshwheel0: spherewheel1: spherewheel2: spherewheel3: spherewheels: scenearm: scenecamera: scene
json
{
"class": "WheeledVehicle",
"position": [
0,
0,
0
],
"props": {
"handling": "arcade",
"collision": null,
"halfExtents": null,
"mass": null,
"wheels": null,
"maxSpeed": null,
"reverseSpeed": null,
"acceleration": null,
"brakeDeceleration": null,
"reverseAsBrake": null,
"steerAngle": null,
"steerRate": null,
"steeringBySpeed": null,
"gripBySlip": null,
"rearHandbrakeGrip": null,
"driftRecovery": null,
"rollingDrag": null,
"downforce": null,
"antiRoll": null,
"uprightAssist": null,
"airControl": null,
"asset": "",
"wheelAsset": ""
}
}Gameplay modules
Import factories from gameable. Helpers, not level classes. Initialize match modules with init after snapshot restoration; reset other helpers in game init. Start timer/wave clocks and use abilities explicitly; step every fixed tick (match modules use system). No default means required; ? means optional. Callbacks are synchronous.
createTriggers
Guest box/sphere trigger zones for watched entity points; no physics/networking. Entities without Transform are outside. Optional contains overrides geometry. Reset before adding zones.
| Option | Type | Default | Meaning |
|---|---|---|---|
capacity | number | — | Volume capacity (safe integer). |
watchCapacity | number | — | Watch capacity (safe integer). |
contains? | function | — | (volume: number, entity: number) => boolean. Override point geometry. |
compactSnapshot? | boolean | false | Sparse persistence. |
onEnter? | function | — | (ctx: GameContext, volume: number, entity: number) => void. Enter callback. |
onExit? | function | — | (ctx: GameContext, volume: number, entity: number) => void. Exit callback. |
ts
import { createTriggers, defineGame } from 'gameable';
const zones = createTriggers({ capacity: 1, watchCapacity: 1 });
export default defineGame({
init(ctx) { zones.reset(); zones.addSphere(0, 0, 0, 3); zones.watch(ctx.player); },
systems: [ctx => zones.step(ctx)],
});createMatchModule
All-ready lobby/loading/countdown/racing/results match flow, fixed seats, late-join spectators and freeze messages. No movement or checkpoint/finish detection.
| Option | Type | Default | Meaning |
|---|---|---|---|
maxPlayers | number | — | Fixed seat capacity. |
history | number | — | Per-seat sound history capacity. |
spawns | Array<Array<number>> | — | Seat-start XYZ; at least maxPlayers. |
start | "all-ready" | — | Only all-ready. |
minPlayers | number | — | Network player minimum. |
soloMinPlayers | number | — | Solo player minimum. |
lateJoin | "spectate-after-loading" | — | Late-join policy. |
spectatorSpawnZ | number | — | Spectator Z; seat-spawn X/Y. |
loadingSeconds | number | — | Loading timeout (seconds). |
cardSeconds | number | — | Delay before tick cues (seconds). |
countdownSeconds | number | — | Countdown seconds. |
limitSeconds | number | — | Racing time limit (seconds). |
afterFirstSeconds | number | — | Time after first finish (seconds). |
resultsSeconds | number | — | Results seconds. |
ts
import { createMatchModule, type MatchModuleOptions } from 'gameable';
const configure = (options: MatchModuleOptions) => createMatchModule(options);
console.log(configure);createCheckpointModule
Checkpoints and fall recovery for straight decreasing-Z courses. Uses shared match state; no circuit or lap tracking. Run before the match system.
| Option | Type | Default | Meaning |
|---|---|---|---|
axis | "z" | — | Only Z. |
direction | "decreasing" | — | Only decreasing Z. |
thresholds | Array<number> | — | Decreasing-Z thresholds. |
points | Array<Array<number>> | — | Respawn XYZ per threshold. |
spawns | Array<Array<number>> | — | Lobby/start XYZ per seat. |
maxAbsX | number | — | Checkpoint X bound. |
minY | number | — | Checkpoint minimum Y. |
fallY | number | — | Fall/respawn Y threshold. |
lobbyRespawn | "seat-start" | — | Lobby respawn policy. |
resetKnock | boolean | — | Clear knock/hit deadlines. |
ts
import { createCheckpointModule, type MatchModule, type CheckpointModuleOptions } from 'gameable';
const configure = (match: MatchModule, options: CheckpointModuleOptions) => createCheckpointModule(match, options);
console.log(configure);createPlacingModule
Finish order at one bounded decreasing-Z plane; no laps or checkpoint enforcement. Tie policy. Uses shared match state; run before its system.
| Option | Type | Default | Meaning |
|---|---|---|---|
axis | "z" | — | Only Z. |
direction | "decreasing" | — | Only decreasing Z. |
at | number | — | Finish plane Z. |
maxAbsX | number | — | Maximum absolute X at finish. |
minY | number | — | Minimum Y at finish. |
maxY | number | — | Maximum Y at finish. |
tieBreak | "seat" | — | Tie policy. |
requiresAllCheckpoints | boolean | — | Only false is supported. |
freezeFinishers | boolean | — | Freeze placed seats. |
ts
import { createPlacingModule, type MatchModule, type PlacingModuleOptions } from 'gameable';
const configure = (match: MatchModule, options: PlacingModuleOptions) => createPlacingModule(match, options);
console.log(configure);createMatchHudModule
Publish roster, local seat state, quantized elapsed seconds and sound history to player HUD data; no UI rendering. Uses shared match state; run after its system.
| Option | Type | Default | Meaning |
|---|---|---|---|
namespace | string | — | HUD namespace (nonempty). |
secondsStep | number | — | Time quantum (positive seconds). |
ts
import { createMatchHudModule, type MatchModule } from 'gameable';
const configure = (match: MatchModule) => createMatchHudModule(match, { namespace: 'race', secondsStep: 0.1 });
console.log(configure);createTimers
Fixed-step one-shot/repeating timer slots with persistence. Durations capture ctx.dt at start; zero/negative durations take one step.
| Option | Type | Default | Meaning |
|---|---|---|---|
capacity | number | — | Slot capacity (safe integer). |
onFire? | function | — | (ctx: GameContext, timer: number) => void. Slot-order callback. |
ts
import { createTimers, defineGame } from 'gameable';
const timers = createTimers({ capacity: 1 });
export default defineGame({ init(ctx) { timers.reset(); timers.start(ctx, 0, 1); }, systems: [ctx => timers.step(ctx)] });createAbilities
Per-owner ability charges and fixed-step cooldowns with persistence; no ability effects. Owners are indices, not entity ids. Running recharges retain their duration/timestep.
| Option | Type | Default | Meaning |
|---|---|---|---|
owners | number | — | Owner capacity (safe integer). |
abilities | Array<{ id: string; cooldown: number; charges?: number = 1 }> | — | Unique ability definitions. |
onReady? | function | — | (ctx: GameContext, owner: number, ability: number) => void. Restored-charge callback. |
ts
import { createAbilities, defineGame } from 'gameable';
const skills = createAbilities({ owners: 1, abilities: [{ id: 'dash', cooldown: 0.5 }] });
export default defineGame({ init: () => skills.reset(), systems: [ctx => skills.step(ctx)] });createWaves
Timed spawn attempts with survivor tracking and persistence. onSpawn returns a unique entity id or zero; report death/despawn with killed. Capacity pauses clocks; stop forgets tracking without despawning.
| Option | Type | Default | Meaning |
|---|---|---|---|
waves | Array<{ count: number; every: number; delay?: number = 0 }> | — | Wave definitions. |
loop? | boolean | false | Loop after last clear. |
maxAlive? | number | — | Survivor limit; omitted = unlimited. |
onSpawn | function | — | (ctx: GameContext, wave: number, index: number) => number. Return entity id or zero. |
onWaveStart? | function | — | (ctx: GameContext, wave: number) => void. Wave starts. |
onWaveClear? | function | — | (ctx: GameContext, wave: number) => void. Wave clears. |
onAllClear? | function | — | (ctx: GameContext) => void. Final nonlooping clear. |
ts
import { createWaves, defineGame } from 'gameable';
const waves = createWaves({ waves: [{ count: 2, every: 0.5 }], onSpawn: () => 0 });
export default defineGame({ init: ctx => { waves.reset(); waves.start(ctx); }, systems: [ctx => waves.step(ctx)] });createCircuitRace
Circuit laps and live places; publishes full circuits[id] HUD standings with revision-on-change for clients and late joiners. Read with circuitStandings(adapter.hudModel, id). Shares the match HUD transport; compose custom HUD data in a single publisher. Needs gates as positions/yaw/half-extents or named box actors; optional section line. Teleport adapters must reset crossing history.
| Option | Type | Default | Meaning |
|---|---|---|---|
id | string | — | Unique race id. |
laps | number | — | Lap count. |
maxRacers | number | — | Racer capacity. |
gates | Array<string> | — | Ordered gate geometry or actor names; start is zero. |
line? | object | — | Section polylines or named spline. |
racerRadius | number | 0.5 | Respawn occupancy radius in metres. |
onRespawn? | function | — | After pawn respawn; adapter hook for vehicle reset. |
resetOnDisconnect | boolean | false | Remove disconnected identity. |
ts
import { createCircuitRace } from 'gameable';
const race = createCircuitRace({id:'race',laps:2,maxRacers:4,gates:['start','turn']});
console.log(race.standings);