@nodetool-ai/agents 0.8.4-a24.3 → 0.8.4
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/dist/.nodetool-build-stamp +1 -1
- package/dist/_skills/LICENSE-iart-motion-design-skills +21 -0
- package/dist/_skills/README.md +211 -0
- package/dist/_skills/api-agents/SKILL.md +58 -0
- package/dist/_skills/api-apps/SKILL.md +54 -0
- package/dist/_skills/api-assets/SKILL.md +66 -0
- package/dist/_skills/api-collections/SKILL.md +33 -0
- package/dist/_skills/api-games/SKILL.md +81 -0
- package/dist/_skills/api-media/SKILL.md +131 -0
- package/dist/_skills/api-memory/SKILL.md +72 -0
- package/dist/_skills/api-models/SKILL.md +78 -0
- package/dist/_skills/api-scripts/SKILL.md +79 -0
- package/dist/_skills/api-settings/SKILL.md +49 -0
- package/dist/_skills/api-sketches/SKILL.md +61 -0
- package/dist/_skills/api-storyboards/SKILL.md +94 -0
- package/dist/_skills/api-timelines/SKILL.md +151 -0
- package/dist/_skills/api-web/SKILL.md +57 -0
- package/dist/_skills/api-workflows/SKILL.md +123 -0
- package/dist/_skills/beat-sync-editing/SKILL.md +208 -0
- package/dist/_skills/caption-titles/SKILL.md +94 -0
- package/dist/_skills/color-motion/SKILL.md +185 -0
- package/dist/_skills/commercial-beat-sheet/SKILL.md +338 -0
- package/dist/_skills/elevenlabs-audio-prompting/SKILL.md +163 -0
- package/dist/_skills/explainer-storyboard/SKILL.md +63 -0
- package/dist/_skills/flux-2-klein-prompting/SKILL.md +107 -0
- package/dist/_skills/frame-composition/SKILL.md +291 -0
- package/dist/_skills/game-direction/SKILL.md +99 -0
- package/dist/_skills/gpt-image-2-prompting/SKILL.md +161 -0
- package/dist/_skills/hailuo-prompting/SKILL.md +107 -0
- package/dist/_skills/kling-video-prompting/SKILL.md +188 -0
- package/dist/_skills/launch-commercial/SKILL.md +267 -0
- package/dist/_skills/launch-kit/SKILL.md +120 -0
- package/dist/_skills/logo-reveal/SKILL.md +225 -0
- package/dist/_skills/manimgx/SKILL.md +123 -0
- package/dist/_skills/minimax-h3-prompting/SKILL.md +134 -0
- package/dist/_skills/motion-ad/SKILL.md +281 -0
- package/dist/_skills/motion-background/SKILL.md +145 -0
- package/dist/_skills/motion-curves/SKILL.md +302 -0
- package/dist/_skills/motion-direction/SKILL.md +259 -0
- package/dist/_skills/motion-graphics/SKILL.md +217 -0
- package/dist/_skills/motion-principles/SKILL.md +255 -0
- package/dist/_skills/music-video-treatment/SKILL.md +40 -0
- package/dist/_skills/nano-banana-pro-prompting/SKILL.md +133 -0
- package/dist/_skills/native-game/SKILL.md +310 -0
- package/dist/_skills/native-game-reference/SKILL.md +879 -0
- package/dist/_skills/nodetool-3d-scene/SKILL.md +110 -0
- package/dist/_skills/nodetool-api-reference/SKILL.md +338 -0
- package/dist/_skills/nodetool-app-builder/SKILL.md +291 -0
- package/dist/_skills/nodetool-browser-agent/SKILL.md +119 -0
- package/dist/_skills/nodetool-chat-cli/SKILL.md +184 -0
- package/dist/_skills/nodetool-custom-node-developer/SKILL.md +294 -0
- package/dist/_skills/nodetool-deployment/SKILL.md +195 -0
- package/dist/_skills/nodetool-js-scripting/SKILL.md +223 -0
- package/dist/_skills/nodetool-model-provider-config/SKILL.md +348 -0
- package/dist/_skills/nodetool-rag-indexing/SKILL.md +153 -0
- package/dist/_skills/nodetool-sketch/SKILL.md +138 -0
- package/dist/_skills/nodetool-skill-author/SKILL.md +125 -0
- package/dist/_skills/nodetool-troubleshooter/SKILL.md +370 -0
- package/dist/_skills/nodetool-video-post/SKILL.md +149 -0
- package/dist/_skills/nodetool-workflow-builder/SKILL.md +295 -0
- package/dist/_skills/product-commercial/SKILL.md +132 -0
- package/dist/_skills/qwen-image-prompting/SKILL.md +122 -0
- package/dist/_skills/script-video/SKILL.md +106 -0
- package/dist/_skills/seedance-2-prompting/SKILL.md +194 -0
- package/dist/_skills/seedream-prompting/SKILL.md +117 -0
- package/dist/_skills/short-film/SKILL.md +123 -0
- package/dist/_skills/stable-audio-prompting/SKILL.md +130 -0
- package/dist/_skills/storyboard-core/SKILL.md +716 -0
- package/dist/_skills/timeline-edit-ops/SKILL.md +736 -0
- package/dist/_skills/trailer-template/SKILL.md +291 -0
- package/dist/_skills/ugc-video/SKILL.md +186 -0
- package/dist/_skills/veo-3-prompting/SKILL.md +140 -0
- package/dist/_skills/video-audio-continuity/SKILL.md +90 -0
- package/dist/_skills/video-clone/SKILL.md +96 -0
- package/dist/_skills/video-workflow/SKILL.md +119 -0
- package/dist/_skills/wan-2-6-prompting/SKILL.md +107 -0
- package/dist/app-build/bridge.d.ts.map +1 -1
- package/dist/app-build/bridge.js +44 -17
- package/dist/app-build/bridge.js.map +1 -1
- package/dist/capabilities/apps.d.ts.map +1 -1
- package/dist/capabilities/apps.js +55 -13
- package/dist/capabilities/apps.js.map +1 -1
- package/dist/capabilities/apps.specs.d.ts.map +1 -1
- package/dist/capabilities/apps.specs.js +5 -3
- package/dist/capabilities/apps.specs.js.map +1 -1
- package/dist/capabilities/game-asset-browser.d.ts.map +1 -1
- package/dist/capabilities/game-asset-browser.js +6 -1
- package/dist/capabilities/game-asset-browser.js.map +1 -1
- package/dist/capabilities/game.d.ts +1 -1
- package/dist/capabilities/game.d.ts.map +1 -1
- package/dist/capabilities/game.js +71 -56
- package/dist/capabilities/game.js.map +1 -1
- package/dist/capabilities/game.specs.js +1 -1
- package/dist/capabilities/game.specs.js.map +1 -1
- package/dist/capabilities/game3d.d.ts.map +1 -1
- package/dist/capabilities/game3d.js +14 -19
- package/dist/capabilities/game3d.js.map +1 -1
- package/dist/capabilities/gameAssertions.d.ts +52 -0
- package/dist/capabilities/gameAssertions.d.ts.map +1 -0
- package/dist/capabilities/gameAssertions.js +32 -0
- package/dist/capabilities/gameAssertions.js.map +1 -0
- package/dist/capabilities/models.d.ts.map +1 -1
- package/dist/capabilities/models.js +9 -0
- package/dist/capabilities/models.js.map +1 -1
- package/dist/capabilities/storyboards.d.ts.map +1 -1
- package/dist/capabilities/storyboards.js +36 -11
- package/dist/capabilities/storyboards.js.map +1 -1
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
- package/dist/capabilities/storyboards.specs.js +11 -1
- package/dist/capabilities/storyboards.specs.js.map +1 -1
- package/dist/capabilities/workflows.d.ts.map +1 -1
- package/dist/capabilities/workflows.js +1 -0
- package/dist/capabilities/workflows.js.map +1 -1
- package/dist/capabilities/workflows.specs.d.ts.map +1 -1
- package/dist/capabilities/workflows.specs.js +4 -3
- package/dist/capabilities/workflows.specs.js.map +1 -1
- package/dist/evals/surfaces/game.d.ts.map +1 -1
- package/dist/evals/surfaces/game.js +29 -1
- package/dist/evals/surfaces/game.js.map +1 -1
- package/dist/game-authoring.d.ts +120 -0
- package/dist/game-authoring.d.ts.map +1 -1
- package/dist/game-build-integrity.d.ts +12 -0
- package/dist/game-build-integrity.d.ts.map +1 -0
- package/dist/game-build-integrity.js +78 -0
- package/dist/game-build-integrity.js.map +1 -0
- package/dist/game-examples.d.ts +44 -3
- package/dist/game-examples.d.ts.map +1 -1
- package/dist/game-examples.js +60 -10
- package/dist/game-examples.js.map +1 -1
- package/dist/system-skills.d.ts +1 -1
- package/dist/system-skills.js +1 -1
- package/dist/tools/builtin-tools.d.ts.map +1 -1
- package/dist/tools/builtin-tools.js +2 -0
- package/dist/tools/builtin-tools.js.map +1 -1
- package/package.json +19 -19
|
@@ -0,0 +1,879 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: native-game-reference
|
|
3
|
+
description: "Field reference for native game documents: scripts, lifecycle hooks, events, tags and props, 2D visuals, particles, lighting, effects, 3D rendering, physics layers, audio mix, animation graphs, input bindings, HUD widgets, performance and editor panels. Load after native-game when an edit needs one of these fields."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Native game field reference
|
|
7
|
+
|
|
8
|
+
The fields and runtime rules behind `native-game`. Load `native-game` first for
|
|
9
|
+
the build, playtest and asset loop. The `edit_native_game` op description also
|
|
10
|
+
lists the op shapes for prefabs, the mixer, script params, input bindings, the
|
|
11
|
+
HUD tree, animation graphs, performance, spatial audio and collision layers.
|
|
12
|
+
Sections here say what the fields do and how they interact.
|
|
13
|
+
|
|
14
|
+
## Schema versions
|
|
15
|
+
|
|
16
|
+
New 2D games start at `schemaVersion: 4` with `engineVersion: "3"`. Older 2D
|
|
17
|
+
documents use schema 1 or 2 with engine `"1"`. Fonts, prepared assets, scene
|
|
18
|
+
music, lighting, backgrounds, gravity, `visualAnimation` and a render effect
|
|
19
|
+
chain need schema 2 or later. Tags, props and script params need schema 4.
|
|
20
|
+
Validation rejects these fields on an earlier schema. 2D `schemaVersion: 5` with
|
|
21
|
+
`engineVersion: "4"` is reserved for Rapier 2D physics, which this runtime does
|
|
22
|
+
not provide: validation and sessions refuse it with `engine_unavailable`. Do not
|
|
23
|
+
author schema 5. A schema must use its own engine version. 3D games use
|
|
24
|
+
`schemaVersion: 3` with `engineVersion: "2"`.
|
|
25
|
+
|
|
26
|
+
## Entity tags and properties
|
|
27
|
+
|
|
28
|
+
Both dimensions support optional `tags` and `props` on an entity. Use
|
|
29
|
+
`update_entity` with `set: {tags: ["hero"], props: {health: 10}}`. Each field
|
|
30
|
+
replaces its complete value. A top-level `null` removes the stored field.
|
|
31
|
+
Nested `null` is JSON data. Tags are unique, with at most 64 strings of
|
|
32
|
+
1–128 characters. Properties hold finite JSON, with at most 64 top-level keys,
|
|
33
|
+
16 levels of nesting and 64 KiB of UTF-8 JSON. Keys contain 1–128 characters.
|
|
34
|
+
`__proto__`, `constructor` and `prototype` are rejected at every nesting level.
|
|
35
|
+
|
|
36
|
+
Scripts read `entity.tags`, `entity.props`, `entity.active` and
|
|
37
|
+
`entity.rotation`, and the same fields on existing `world` entries and
|
|
38
|
+
`world.get(id)` results. `world.query({tag})` returns the ids carrying a tag.
|
|
39
|
+
Rotation is radians in 2D and the committed world quaternion `[x, y, z, w]` in 3D.
|
|
40
|
+
Use `setProp {key, value}` and `removeProp {key}` to change the caller's
|
|
41
|
+
runtime properties. Setting `null` keeps that key. Removal deletes it. The
|
|
42
|
+
runtime checks every property command in a tick before it changes any state,
|
|
43
|
+
and an error names the entity and tick. Each entity's props count once toward
|
|
44
|
+
the 64 KiB script input limit. Runtime properties survive snapshot restore and
|
|
45
|
+
remain independent of the authored document and other prefab instances.
|
|
46
|
+
Mutating an input object does not change runtime state.
|
|
47
|
+
|
|
48
|
+
## 2D scripts
|
|
49
|
+
|
|
50
|
+
A `script` behavior is a function expression, or an object of lifecycle hooks
|
|
51
|
+
(see Lifecycle hooks and timers). A function receives `{tick, pressed,
|
|
52
|
+
justPressed, events, entity, world, state, random}` and returns `{state,
|
|
53
|
+
commands}`. `events` are the previous tick's events. `world` lists every active
|
|
54
|
+
entity with a collider or camera as `{id, source, x, y}`. `entity` holds the
|
|
55
|
+
caller's `id`, `source`, position, velocity, and `touching {down, up, left,
|
|
56
|
+
right}`, which names the sides of its collider that rest against a solid at the
|
|
57
|
+
start of the tick. A spawned instance has the id `<prefab>#<n>`, its `source`
|
|
58
|
+
is the prefab id, and it runs the prefab's script with its own state.
|
|
59
|
+
|
|
60
|
+
Functions, lifecycle hooks, and timer methods must be synchronous. Async functions
|
|
61
|
+
and generators fail preparation. Returning a promise, thenable, or iterator fails
|
|
62
|
+
the call. Use tick timers for delayed work.
|
|
63
|
+
|
|
64
|
+
Store persistent values in returned `state`. A function that the engine proves
|
|
65
|
+
keeps no hidden state is compiled once and reused. Other sources evaluate in a
|
|
66
|
+
fresh context each call, so closures and globals do not persist between calls.
|
|
67
|
+
Source initialization uses the seeded random generator. `maxTickMs` limits each
|
|
68
|
+
call, subject to the total script budget for the tick. Source and input/output
|
|
69
|
+
limits count UTF-8 bytes.
|
|
70
|
+
|
|
71
|
+
During a function call, global `world.get(id)` returns an entity or `undefined`,
|
|
72
|
+
and `world.query({source?, tag?, near?, radius?, limit?})` returns matching IDs
|
|
73
|
+
in runtime entity order. Both read the same immutable tick-start snapshot,
|
|
74
|
+
before movement behaviors or script commands run. Returned values are private
|
|
75
|
+
copies. Queries cover every active entity. Legacy `input.world` keeps its
|
|
76
|
+
existing shape and population, including only collider or camera entities in 2D.
|
|
77
|
+
The 2D query record is `{id, source, x, y, velocityX, velocityY, grounded}`.
|
|
78
|
+
The 3D record is `{id, source, position, velocity, grounded}`, with XYZ vectors.
|
|
79
|
+
3D records and schema 4 2D records add `tags`, `props`, `rotation` and `active`.
|
|
80
|
+
A `tag` filter matches only entities that carry the tag, so it matches nothing
|
|
81
|
+
on schema 1 or 2. Pass `near: {x, y, z?}` and `radius` together. The radius is
|
|
82
|
+
nonnegative and includes entities on its boundary, measured between centers,
|
|
83
|
+
with omitted `z` treated as zero. `source` matches the record's original entity
|
|
84
|
+
ID, not a 3D prefab asset ID. `limit` is an integer from 0 to 1024, defaulting
|
|
85
|
+
to 1024. A call allows 64 combined `get`/`query` attempts. Exceeding the count
|
|
86
|
+
fails the call even if guest code catches it. Query arguments allow 4096 JSON
|
|
87
|
+
characters, `get` IDs 1024 characters, and responses 64 KiB. These APIs are
|
|
88
|
+
unavailable during source initialization.
|
|
89
|
+
|
|
90
|
+
The commands are `setVelocity`, `setPosition`, `setVisual {tint?, opacity?,
|
|
91
|
+
rotation?, scaleX?, scaleY?, flipX?}`, `playAnimation {clip}`, `spawn {prefabId,
|
|
92
|
+
x?, y?, velocityX?, velocityY?}`, `despawn`, `emit`, `sceneTransition`, and
|
|
93
|
+
`hud {id, text, x, y, size?, color?, align?}`. HUD coordinates are canvas
|
|
94
|
+
pixels. Empty text removes a label. A label with the id `score` or `win`
|
|
95
|
+
replaces the built-in label. Set `fontId` to a logical font binding for a HUD
|
|
96
|
+
label. Font assets use `mediaKind: "font"`, `fontFormat: "ttf" | "otf"`, and
|
|
97
|
+
`required: true` by default. Required fonts load before the first frame or fail
|
|
98
|
+
play and capture. An optional font (`required: false`) reports a fallback before
|
|
99
|
+
play. Export embeds the font bytes for offline use.
|
|
100
|
+
|
|
101
|
+
`log {message, level?}` writes a line to the editor console in 2D and 3D.
|
|
102
|
+
`level` is `"log"` (the default), `"warning"` or `"error"`. A log line never
|
|
103
|
+
changes the simulation, snapshots or replay, and it does not count toward
|
|
104
|
+
`maxCommands`. A tick keeps 32 lines and reports the rest as one dropped-lines
|
|
105
|
+
warning. Messages longer than 1000 characters are truncated. Log text still
|
|
106
|
+
counts toward the 64 KiB output limit of a script call. Lines logged before a
|
|
107
|
+
script failure appear in the console ahead of the error.
|
|
108
|
+
|
|
109
|
+
### Script time limits
|
|
110
|
+
|
|
111
|
+
`maxTickMs` accepts 1 through 20 ms, defaults to 8 ms, and cannot raise the
|
|
112
|
+
host's 20 ms ceiling. It counts the CPU time of the thread running the script, from just
|
|
113
|
+
before the call (or before compiling its source) until its output is checked.
|
|
114
|
+
Waiting for a processor does not count. A call ends only when both its CPU time
|
|
115
|
+
and its wall time reach the limit. Browsers have no thread CPU clock, so there
|
|
116
|
+
the limit counts wall time. The 50 ms script budget for a whole tick and the
|
|
117
|
+
100 ms limit for evaluating a source during preparation count wall time on
|
|
118
|
+
every host. A session's first scripted tick has 1,000 ms instead of 50 ms,
|
|
119
|
+
because it runs each script for the first time. A script that loops forever still ends at its limit.
|
|
120
|
+
|
|
121
|
+
### Script parameters
|
|
122
|
+
|
|
123
|
+
Declare a script's tunables as `params` on its script behavior instead of
|
|
124
|
+
constants in the source. The inspector edits them and scripts read the values
|
|
125
|
+
on `input.params`. Set them with `set_script_params {entity_id, scene_id?,
|
|
126
|
+
index, params?, values?}`. `params` replaces the declarations and `null`
|
|
127
|
+
removes them with their values. `values` merges stored values, and a `null`
|
|
128
|
+
value returns one to its default. 2D games need schema 4. 3D games accept
|
|
129
|
+
params on schema 3.
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{"op": "set_script_params", "entity_id": "player", "index": 0,
|
|
133
|
+
"params": {"speed": {"type": "number", "default": 3, "minimum": 0, "maximum": 10},
|
|
134
|
+
"target": {"type": "entity"}, "hit": {"type": "asset", "kind": "audio"}},
|
|
135
|
+
"values": {"speed": 5, "target": "goal"}}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Types are `number` (`minimum`, `maximum`, `integer`), `boolean`, `color`
|
|
139
|
+
(`#rrggbb`), `enum` (`options`, default the first option), `entity`, `asset`
|
|
140
|
+
(optional `kind`) and `vector` (`dimensions` 2 or 3). A behavior declares at
|
|
141
|
+
most 32 params with identifier names. `input.params` holds the stored value,
|
|
142
|
+
else the default. An `entity` or `asset` param without either is `null`.
|
|
143
|
+
Validation rejects an entity reference outside the behavior's scene, a missing
|
|
144
|
+
asset slot, an asset of the wrong `kind`, and in 3D an entity reference inside
|
|
145
|
+
a prefab or an undeclared prefab asset. A behavior without `params` has no
|
|
146
|
+
`input.params` key. Params count once per behavior definition toward the
|
|
147
|
+
64 KiB script input limit.
|
|
148
|
+
|
|
149
|
+
### Lifecycle hooks and timers
|
|
150
|
+
|
|
151
|
+
A script source may be an object of hooks instead of a function. It works in
|
|
152
|
+
2D and 3D with no schema change.
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
({
|
|
156
|
+
onStart(input) { every(30, "fire"); return { state: { shots: 0 } }; },
|
|
157
|
+
fire(input) { return { state: { shots: input.state.shots + 1 },
|
|
158
|
+
commands: [{ kind: "spawn", prefabId: "bolt" }] }; },
|
|
159
|
+
onTriggerEnter(input, contact) { return { commands: [{ kind: "emit", event: "hit:" + contact.otherId }] }; },
|
|
160
|
+
onDestroy(input) { return { commands: [{ kind: "emit", event: "gone" }] }; }
|
|
161
|
+
})
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Hooks receive the same `input` as a function script and return
|
|
165
|
+
`{state?, commands?}` or nothing. Each hook sees the state returned by the
|
|
166
|
+
one before it. Within a tick they run in this order:
|
|
167
|
+
|
|
168
|
+
1. `onStart`, on the behavior's first call.
|
|
169
|
+
2. `onSceneEnter`, on the first tick of the scene.
|
|
170
|
+
3. `onTriggerEnter`, `onTriggerExit` and `onContact`, once per contact of
|
|
171
|
+
the entity in the previous tick's events, in event order. A contact where
|
|
172
|
+
either collider is a sensor is a trigger. Triggers fire on `enter` and
|
|
173
|
+
`exit`. `onContact` receives every phase of a solid contact. The second
|
|
174
|
+
argument is `{otherId, phase, sensor}`.
|
|
175
|
+
4. `onEvent`, once per trigger event of the previous tick that reaches the
|
|
176
|
+
entity, in event order. See Typed events.
|
|
177
|
+
5. Due timers, in the order they were scheduled.
|
|
178
|
+
6. `onFixedUpdate` or `onUpdate`. They are aliases. Define one of them.
|
|
179
|
+
|
|
180
|
+
`onDestroy` runs alone in the tick after the entity despawns. It may return
|
|
181
|
+
only `hud`, `emit`, `spawn`, `despawn`, `sceneTransition`, `log`, `save` and `load` commands. A scene
|
|
182
|
+
transition discards behavior state without calling `onDestroy`.
|
|
183
|
+
|
|
184
|
+
`after(ticks, name)` calls the object's method `name` once, `ticks` ticks
|
|
185
|
+
later. `every(ticks, name)` calls it every `ticks` ticks. `ticks` is an
|
|
186
|
+
integer from 1 to 1000000, `name` must be a method that is not a hook, and a
|
|
187
|
+
behavior holds at most 32 timers. Scheduling an existing name replaces that
|
|
188
|
+
timer. Timers live in the behavior's snapshot state, so they replay from any
|
|
189
|
+
snapshot. Session preparation rejects an unknown `on*` key, a hook that is not
|
|
190
|
+
a function, and an object that defines both `onUpdate` and `onFixedUpdate`.
|
|
191
|
+
Lifecycle objects run in a fresh context each call.
|
|
192
|
+
|
|
193
|
+
### Typed events
|
|
194
|
+
|
|
195
|
+
`emit` takes an optional `payload` and `target` in 2D and 3D, with no schema
|
|
196
|
+
change. The payload is any JSON value up to 1 KiB serialized and 16 levels
|
|
197
|
+
deep. The target is `{entityId}` for one entity or `{tag}` for every entity
|
|
198
|
+
with that tag. Omit `target` to broadcast.
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
// Sender
|
|
202
|
+
({ onTriggerEnter(input, contact) { return { commands: [{ kind: "emit", event: "alarm",
|
|
203
|
+
payload: { by: contact.otherId, level: 2 }, target: { tag: "guard" } }] }; } })
|
|
204
|
+
// Receiver, on an entity tagged "guard"
|
|
205
|
+
({ onEvent(input, event) { if (event.event === "alarm") { return { state: { level: event.payload.level } }; } } })
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The event lands in the stream as `{kind: "trigger", event, entityId,
|
|
209
|
+
payload?, target?}`, where `entityId` is the sender, and reaches scripts on
|
|
210
|
+
the next tick. `onEvent(input, event)` receives that object for each trigger
|
|
211
|
+
event that reaches its entity, broadcasts included, and the sender receives
|
|
212
|
+
its own broadcasts. Built-in `trigger` behaviors emit untargeted events, so
|
|
213
|
+
`onEvent` sees them too. The target also limits built-in listeners: a `spawn`
|
|
214
|
+
or `sceneTransition` behavior with `onEvent` and an `audioSource` with
|
|
215
|
+
`onEvent` react only to events that reach their entity. `input.events` still
|
|
216
|
+
holds every event of the previous tick, targeted or not. A tag target reaches
|
|
217
|
+
nothing on 2D schemas before 4. A plain `{kind: "emit", event}` produces the
|
|
218
|
+
same event as before. Payloads count toward the 64 KiB script input limit once
|
|
219
|
+
per tick.
|
|
220
|
+
|
|
221
|
+
### Player saves
|
|
222
|
+
|
|
223
|
+
Scripts save and load in 2D and 3D with two commands:
|
|
224
|
+
|
|
225
|
+
- `save {slot, data, scope?, snapshot?}` writes `data` to a slot after the tick.
|
|
226
|
+
- `load {slot, scope?}` reads a slot.
|
|
227
|
+
|
|
228
|
+
`scope` is `"progress"` (the default) or `"settings"`. The two scopes are
|
|
229
|
+
separate stores, so a progress slot and a settings slot can share a name and
|
|
230
|
+
removing one never touches the other. `snapshot: true` also stores the
|
|
231
|
+
end-of-tick session snapshot with a progress save. A settings save with a
|
|
232
|
+
snapshot fails the tick. The player keeps one snapshot save pending at a time,
|
|
233
|
+
and the editor stores the data without the snapshot.
|
|
234
|
+
|
|
235
|
+
The player runs saves and loads in order, with at most 8 waiting. A request
|
|
236
|
+
past that limit fails without stopping the game, and a failed load answers
|
|
237
|
+
with null data.
|
|
238
|
+
|
|
239
|
+
A slot name is 1 to 32 letters, digits, `_` or `-`. `data` must be a JSON
|
|
240
|
+
object of at most 16 KiB serialized, 256 keys counted at every depth and 16
|
|
241
|
+
levels deep. Each scope holds at most 16 slots per game. A tick accepts at most
|
|
242
|
+
4 save and load commands across all scripts, and more fail the tick. These
|
|
243
|
+
commands do not count toward `maxCommands` and never enter snapshots.
|
|
244
|
+
|
|
245
|
+
A load answers on a later tick through `input.loaded`, a list of `{scope, slot,
|
|
246
|
+
data}` where `data` is null for an empty slot. A frame carries at most 2
|
|
247
|
+
results. Keep what the game needs in script state:
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
({ onStart() { return { commands: [{ kind: "load", slot: "main" }] }; },
|
|
251
|
+
onUpdate(input) {
|
|
252
|
+
const result = input.loaded?.find((entry) => entry.slot === "main");
|
|
253
|
+
return { state: result ? { best: result.data?.best ?? 0 } : input.state };
|
|
254
|
+
} })
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The loaded data is part of the input frame, so a replay of recorded frames
|
|
258
|
+
reproduces it without storage. The standalone player stores saves in IndexedDB.
|
|
259
|
+
The editor stores them in localStorage under the game id. `nodetool game
|
|
260
|
+
simulate` and agent playtests keep them in memory. `simulate --saves <file>`
|
|
261
|
+
seeds slots from `{progress?, settings?}`, and its JSON report lists the slots
|
|
262
|
+
after the run.
|
|
263
|
+
|
|
264
|
+
## 2D visuals
|
|
265
|
+
|
|
266
|
+
For generated art in a workflow, pass one style entity with a reference image.
|
|
267
|
+
The guided graph sends that image through an image-to-image generation input
|
|
268
|
+
for every visual slot. `StageGameAssets.preparation` accepts per-slot alpha
|
|
269
|
+
trimming, target size with a crop policy, pivot, nearest or linear sampling,
|
|
270
|
+
and mirror tiling for single images. Trimming records crop offsets so placement
|
|
271
|
+
keeps its anchor. `StageGameAssets.fonts` stages TrueType or OpenType assets by
|
|
272
|
+
logical font ID.
|
|
273
|
+
|
|
274
|
+
1. Use `sprite.blend: "additive"` for light drawn on black, such as glows and
|
|
275
|
+
sparks. Set `sampling: "linear"` on painted or soft assets.
|
|
276
|
+
2. Use `lifetime {ticks, fade, endScale}` for short-lived entities. A kinematic
|
|
277
|
+
body without a collider moves but never collides, which keeps them cheap.
|
|
278
|
+
Prefer the `particles` component for sparks, smoke and dust.
|
|
279
|
+
3. Use `animator` frames on a sprite sheet. Playback starts when the entity
|
|
280
|
+
spawns. Named `animator.clips` hold other frame sets. `playAnimation` starts
|
|
281
|
+
a clip from its first frame, and a repeated request for the current clip
|
|
282
|
+
continues it. Set `sprite.flipX` or the `setVisual` flag to mirror art, or
|
|
283
|
+
`sprite.faceMotion: "left" | "right"` to turn art that faces that way toward
|
|
284
|
+
the body's horizontal motion.
|
|
285
|
+
4. Give `audioSource.onEvent` an event kind or the name of an emitted trigger.
|
|
286
|
+
Set `audioSource.volume` from `0` to `1` for effect loudness. Effects receive
|
|
287
|
+
distinct voice ids, so repeated triggers can overlap within the 32-voice cap.
|
|
288
|
+
5. Make hazards sensors. Only a non-sensor body collects a `collectible`.
|
|
289
|
+
|
|
290
|
+
Set `scene.music` to an installed audio `assetId` for looping music. `volume`
|
|
291
|
+
ranges from `0` to `1`, and `fadeInTicks` and `fadeOutTicks` range from `0` to
|
|
292
|
+
`600`. Music starts at the scene's entry tick. Pause suspends audio, reset
|
|
293
|
+
restarts the entry scene once, and loading resumes the loop at the saved
|
|
294
|
+
logical tick. Set `music.streaming: {loopStartSeconds, loopEndSeconds?}` for
|
|
295
|
+
long tracks in 2D or 3D. This streams through the music bus without decoding
|
|
296
|
+
the whole asset. Start defaults to 0 seconds, end defaults to the track's end,
|
|
297
|
+
and an explicit end must exceed start. Both are bounded by 86400 seconds.
|
|
298
|
+
The intro plays once. Custom loop boundaries follow browser media events.
|
|
299
|
+
Existing fades crossfade outgoing and incoming scene tracks. Use `update_scene`
|
|
300
|
+
to replace the whole `music` object. Omit `streaming` to retain decoded
|
|
301
|
+
playback. Browser playback starts after a user gesture. A missing or
|
|
302
|
+
unplayable audio asset is reported in the player.
|
|
303
|
+
|
|
304
|
+
`visualAnimation` on a sprite animates `rotation`, `scaleX`, `scaleY`,
|
|
305
|
+
`opacity`, or `tint` from `from` to `to` over `durationTicks` in its `tracks`.
|
|
306
|
+
`delayTicks`, `repeat`, `pingPong`, and `easing` control timing. `rotationRate`
|
|
307
|
+
adds radians per tick for continuous spins and cannot share a rotation track.
|
|
308
|
+
Each property has at most one track. Tracks start at scene entry or spawn time.
|
|
309
|
+
The visual order is authored transform and sprite values, tracks, script
|
|
310
|
+
`setVisual`, then lifetime fade and scale. Tracks do not change collision.
|
|
311
|
+
|
|
312
|
+
Scene `backgrounds` define image layers independent of entities. Each layer
|
|
313
|
+
sets `assetId`, world-unit `width` and `height`, `origin` at the tile center,
|
|
314
|
+
`layer`, per-axis `parallax`, `scrollRate` in world units per second, and `mode`
|
|
315
|
+
(`none`, `repeat`, `repeatX`, or `mirror`). `repeatX` tiles only sideways, which
|
|
316
|
+
suits a strip of scenery. Parallax `0` stays fixed to the screen and `1`
|
|
317
|
+
stays fixed in the world. Optional `frame` selects an atlas rectangle.
|
|
318
|
+
|
|
319
|
+
Scene `lighting` holds an `ambient` color and intensity plus up to 32 `points`
|
|
320
|
+
with world position, color, intensity, radius, and falloff. Lighting multiplies
|
|
321
|
+
world color before effects. HUD stays unlit. Set `sprite.unlit: true` for
|
|
322
|
+
emissive art such as glows. Canvas2D capture paints these sprites above the lit
|
|
323
|
+
world. An entity `light2d {color, intensity, radius, falloff, offset?}` moves
|
|
324
|
+
with its entity. Each frame adds the entity lights nearest the camera to the
|
|
325
|
+
scene's fixed points, up to 32 lights in total.
|
|
326
|
+
|
|
327
|
+
`renderEffects` is an ordered chain of at most eight effects.
|
|
328
|
+
`brightnessContrast` uses `brightness` from `-1` to `1` and `contrast` from `0`
|
|
329
|
+
to `4`. `bloom` uses `threshold` from `0` to `1`, `softness` from `0` to `0.5`,
|
|
330
|
+
`radius` from `0` to `64` pixels, and `intensity` from `0` to `4`. `lut` refers
|
|
331
|
+
to an opaque packed 2D cube image with dimensions `size² × size` and an
|
|
332
|
+
optional intensity and color domain. Effects run in listed order. Set
|
|
333
|
+
`hudEffectOrder` to `afterEffects` to keep text crisp, or `beforeEffects` to
|
|
334
|
+
process HUD with the world. A bloom chain defaults to `afterEffects`.
|
|
335
|
+
`required: true` makes unsupported GPU effects fail before play or capture.
|
|
336
|
+
Optional effects are omitted with a diagnostic on Canvas 2D.
|
|
337
|
+
|
|
338
|
+
Collider `category` and `mask` are unsigned 32-bit bitsets in 2D. They default
|
|
339
|
+
to `1` and `4294967295`. A pair interacts only when each mask includes a
|
|
340
|
+
category bit from the other collider. Give bullets masks that exclude other
|
|
341
|
+
bullets when bullet-to-bullet interaction is unnecessary.
|
|
342
|
+
|
|
343
|
+
Contacts report `phase: "enter" | "stay" | "exit"` and `normalX`/`normalY`.
|
|
344
|
+
Check both `entityId` and `otherId`, since each pair emits one contact per tick.
|
|
345
|
+
Built-in triggers fire on entry. Use `stay` contacts for repeated overlap logic.
|
|
346
|
+
Swept collisions detect fast movement through thin static walls and sensors.
|
|
347
|
+
Scene entry starts authored entities' animation and lifetime clocks, and saves
|
|
348
|
+
preserve those clocks and active contact pairs.
|
|
349
|
+
|
|
350
|
+
## Particles
|
|
351
|
+
|
|
352
|
+
The particle inspector offers Fire, Smoke, Sparks, Dust, Magic, Rain, Snow and
|
|
353
|
+
Explosion presets in both dimensions. Applying one replaces that entity's
|
|
354
|
+
`particles` component and preserves its other components. Select an emitter
|
|
355
|
+
to edit its fields, lifetime curves and color gradient. Renaming an emitter
|
|
356
|
+
repairs its death-emitter references. Removing one removes references to it.
|
|
357
|
+
The isolated preview uses a fixed seed and restarts when edited. Pause and
|
|
358
|
+
Restart affect the preview only. The 2D preview uses Canvas2D, including sprite
|
|
359
|
+
sheets and blending. The 3D preview uses WebGL2, including stretched and mesh
|
|
360
|
+
particles and depth softness. It does not run gameplay scripts or change the
|
|
361
|
+
play session.
|
|
362
|
+
|
|
363
|
+
Agents and CLI document edits use the same `update_entity` operation as the
|
|
364
|
+
inspector. Set the complete component with
|
|
365
|
+
`{op: "update_entity", entity_id: "effect", set: {particles: {emitters: [...]}}}`.
|
|
366
|
+
There is no preset-name operation. Node.js authoring can also import
|
|
367
|
+
`getGameParticlePreset(name, dimension)` from `@nodetool-ai/protocol` to get
|
|
368
|
+
fresh canonical data. In a code action, use the JSON recipes below with the
|
|
369
|
+
existing game edit API instead of importing that host package.
|
|
370
|
+
|
|
371
|
+
### Particle preset recipes
|
|
372
|
+
|
|
373
|
+
Each payload is a complete `particles` component. Omitted fields take schema
|
|
374
|
+
defaults. These canonical recipes use 3D draw settings. For a 2D entity, remove
|
|
375
|
+
`render` from each emitter and set `shape.size.z` to `0` for box shapes.
|
|
376
|
+
Keep other values unchanged. Both dimensions use world units and +Y upward.
|
|
377
|
+
Place rain and snow emitters above the area they cover. Explosion is a finite
|
|
378
|
+
burst, which can be restarted with `emitParticles` without a count.
|
|
379
|
+
|
|
380
|
+
#### fire
|
|
381
|
+
|
|
382
|
+
```json
|
|
383
|
+
{"emitters":[{"id":"flame","rate":48,"shape":{"kind":"cone","angle":15,"radius":0.15},"lifetime":{"min":0.6,"max":1.2},"speed":{"min":1.2,"max":2.2},"size":{"min":0.15,"max":0.4},"color":"#ff932d","sizeOverLifetime":[{"t":0,"value":0.5},{"t":0.3,"value":1},{"t":1,"value":0.1}],"opacityOverLifetime":[{"t":0,"value":0},{"t":0.12,"value":1},{"t":1,"value":0}],"colorOverLifetime":[{"t":0,"color":"#fff4a0"},{"t":0.5,"color":"#ff8326"},{"t":1,"color":"#a91c14"}],"gravity":{"x":0,"y":1.4,"z":0},"blend":"additive","unlit":true}]}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### smoke
|
|
387
|
+
|
|
388
|
+
```json
|
|
389
|
+
{"emitters":[{"id":"smoke","rate":14,"shape":{"kind":"cone","angle":20,"radius":0.2},"lifetime":{"min":2,"max":3.5},"speed":{"min":0.3,"max":0.7},"size":{"min":0.3,"max":0.6},"color":"#9d9fa8","opacity":0.45,"sizeOverLifetime":[{"t":0,"value":0.4},{"t":1,"value":2.5}],"opacityOverLifetime":[{"t":0,"value":0},{"t":0.12,"value":1},{"t":1,"value":0}],"colorOverLifetime":[{"t":0,"color":"#52545e"},{"t":1,"color":"#b9bac2"}],"gravity":{"x":0.08,"y":0.35,"z":0},"drag":0.3,"unlit":true}]}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
#### sparks
|
|
393
|
+
|
|
394
|
+
```json
|
|
395
|
+
{"emitters":[{"id":"sparks","rate":24,"shape":{"kind":"sphere","radius":0.05,"fromSurface":false},"lifetime":{"min":0.3,"max":0.9},"speed":{"min":2,"max":5},"size":{"min":0.025,"max":0.06},"color":"#ffd37c","opacityOverLifetime":[{"t":0,"value":0},{"t":0.12,"value":1},{"t":1,"value":0}],"colorOverLifetime":[{"t":0,"color":"#fff4c1"},{"t":1,"color":"#e7551d"}],"gravity":{"x":0,"y":-4,"z":0},"blend":"additive","unlit":true,"render":{"kind":"stretched","length":2,"speedScale":0.04}}]}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
#### dust
|
|
399
|
+
|
|
400
|
+
```json
|
|
401
|
+
{"emitters":[{"id":"dust","rate":8,"shape":{"kind":"box","size":{"x":4,"y":2,"z":2}},"lifetime":{"min":3,"max":6},"speed":{"min":0.02,"max":0.08},"size":{"min":0.02,"max":0.05},"color":"#d4c4a0","opacity":0.45,"opacityOverLifetime":[{"t":0,"value":0},{"t":0.12,"value":1},{"t":1,"value":0}],"gravity":{"x":0.01,"y":0.01,"z":0},"unlit":true}]}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
#### magic
|
|
405
|
+
|
|
406
|
+
```json
|
|
407
|
+
{"emitters":[{"id":"magic","rate":30,"shape":{"kind":"sphere","radius":0.7,"fromSurface":true},"lifetime":{"min":0.8,"max":1.8},"speed":{"min":0.1,"max":0.5},"size":{"min":0.04,"max":0.12},"angularVelocity":{"min":-3.141592653589793,"max":3.141592653589793},"color":"#9d79ff","sizeOverLifetime":[{"t":0,"value":0},{"t":0.3,"value":1.5},{"t":1,"value":0}],"opacityOverLifetime":[{"t":0,"value":0},{"t":0.12,"value":1},{"t":1,"value":0}],"colorOverLifetime":[{"t":0,"color":"#63f4ff"},{"t":0.5,"color":"#9970ff"},{"t":1,"color":"#fa91e6"}],"blend":"additive","unlit":true}]}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### rain
|
|
411
|
+
|
|
412
|
+
```json
|
|
413
|
+
{"emitters":[{"id":"rain","rate":120,"maxParticles":512,"shape":{"kind":"box","size":{"x":6,"y":0.2,"z":3}},"lifetime":{"min":0.7,"max":1.1},"speed":0,"size":0.025,"color":"#a2c9e8","opacity":0.65,"opacityOverLifetime":[{"t":0,"value":0},{"t":0.12,"value":1},{"t":1,"value":0}],"gravity":{"x":0.4,"y":-18,"z":0},"unlit":true,"render":{"kind":"stretched","length":2,"speedScale":0.06}}]}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
#### snow
|
|
417
|
+
|
|
418
|
+
```json
|
|
419
|
+
{"emitters":[{"id":"snow","rate":28,"shape":{"kind":"box","size":{"x":6,"y":0.2,"z":3}},"lifetime":{"min":4,"max":6},"speed":0,"size":{"min":0.04,"max":0.1},"angularVelocity":{"min":-0.7853981633974483,"max":0.7853981633974483},"color":"#e8f5ff","opacity":0.9,"opacityOverLifetime":[{"t":0,"value":0},{"t":0.12,"value":1},{"t":1,"value":0}],"gravity":{"x":0.025,"y":-0.25,"z":0},"unlit":true}]}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
#### explosion
|
|
423
|
+
|
|
424
|
+
```json
|
|
425
|
+
{"emitters":[{"id":"explosion","duration":0.1,"loop":false,"rate":0,"bursts":[{"time":0,"count":96,"cycles":1,"interval":0.1}],"shape":{"kind":"sphere","radius":0.1,"fromSurface":false},"lifetime":{"min":0.4,"max":1},"speed":{"min":2,"max":6},"size":{"min":0.1,"max":0.3},"color":"#ff9b30","sizeOverLifetime":[{"t":0,"value":1.5},{"t":1,"value":0}],"opacityOverLifetime":[{"t":0,"value":0},{"t":0.12,"value":1},{"t":1,"value":0}],"colorOverLifetime":[{"t":0,"color":"#fff6c5"},{"t":0.4,"color":"#ff992c"},{"t":1,"color":"#b62c19"}],"drag":2,"blend":"additive","unlit":true}]}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Particle component fields
|
|
429
|
+
|
|
430
|
+
Add particle effects with the `particles` component on any 2D (schema 2 or
|
|
431
|
+
later) or 3D entity. It holds up to 8 `emitters`, each with a unique `id`. An
|
|
432
|
+
emitter sets `rate` (particles per second), `bursts` (`time`, `count`,
|
|
433
|
+
`cycles`, `interval`), `duration`, `loop`, `playOnStart`, `maxParticles` and a
|
|
434
|
+
`shape`: `point`, `circle`, `sphere`, `cone`, `box` or `edge`. Cone, box and
|
|
435
|
+
edge emit along local +Y. Point, circle and sphere emit outward. Per-particle
|
|
436
|
+
values `lifetime`, `speed`, `size`, `rotation` and `angularVelocity` take a
|
|
437
|
+
number or `{ min, max }`. `color` takes `#rrggbb` or `{ min, max }`. The
|
|
438
|
+
`sizeOverLifetime`, `speedOverLifetime` and `opacityOverLifetime` curves are
|
|
439
|
+
lists of `{ t, value }` keys sorted by `t` from 0 to 1. `colorOverLifetime` uses
|
|
440
|
+
`{ t, color }` keys. Add `gravity` (`{ x, y, z }` in units per second squared)
|
|
441
|
+
and `drag`. `space: "local"` makes particles follow the entity.
|
|
442
|
+
`onDeath: [{ emitter, count }]` fires another emitter of the same component
|
|
443
|
+
where each particle dies. Give that emitter `playOnStart: false` and `rate: 0`.
|
|
444
|
+
|
|
445
|
+
Scripts trigger emitters with `{ kind: "emitParticles", emitter?, count? }` on
|
|
446
|
+
their own entity. Without `count`, the emitter restarts its cycle, which suits
|
|
447
|
+
a one-shot explosion with `loop: false`. With `count`, that many particles
|
|
448
|
+
spawn at once. Omitting `emitter` selects the first emitter in the list. The
|
|
449
|
+
command is presentation only and never appears in gameplay events or
|
|
450
|
+
snapshots. Particles cannot affect scores, physics or scripts. Prefer them over
|
|
451
|
+
spawned prefabs for sparks, smoke and dust.
|
|
452
|
+
|
|
453
|
+
Each emitter also sets how its particles draw in 2D. Without `sprite`, a
|
|
454
|
+
particle is a soft round dot tinted by its colour. `sprite: { assetId, columns,
|
|
455
|
+
rows, frameCount?, cycles?, sampling? }` uses an image asset instead. A sheet is
|
|
456
|
+
cut into `columns` by `rows` cells read left to right and top to bottom, and the
|
|
457
|
+
first `frameCount` cells play `cycles` times over each particle's life.
|
|
458
|
+
`sampling` defaults to `nearest`. `blend: "additive"` adds light, which suits
|
|
459
|
+
fire, sparks and magic. The default `normal` alpha-blends, which suits smoke and
|
|
460
|
+
dust. Particles take scene lighting like sprites. Set `unlit: true` so glowing
|
|
461
|
+
particles keep their colour in the dark. `layer` sets the draw layer. It
|
|
462
|
+
defaults to the entity's sprite layer, or 0 without a sprite, and particles
|
|
463
|
+
draw above sprites of the same layer. `size` is the particle's width in world
|
|
464
|
+
units. `rotation` and `angularVelocity` are radians and radians per second.
|
|
465
|
+
|
|
466
|
+
The standalone player, the editor's play view and `capture_native_game_frame`
|
|
467
|
+
draw 2D particles. A capture simulates particles for every tick up to the
|
|
468
|
+
captured one, so a burst at tick 0 is still visible a few ticks later. WebGPU
|
|
469
|
+
draws every live particle. The Canvas2D fallback draws at most 1024 visible
|
|
470
|
+
particles. Each emitter's random stream is seeded from its entity and emitter
|
|
471
|
+
ids, so captures repeat.
|
|
472
|
+
|
|
473
|
+
In 3D, `render` sets how each particle draws. The default `{ kind:
|
|
474
|
+
"billboard" }` is a soft round dot facing the camera, `size` units across and
|
|
475
|
+
turned by `rotation`. `{ kind: "stretched", length?, speedScale? }` lies along
|
|
476
|
+
the particle's direction of travel on screen. It is `size` wide and `size ×
|
|
477
|
+
length + speed × speedScale` long, which suits sparks, rain and streaks.
|
|
478
|
+
`length` defaults to 1 and `speedScale` to 0.1 seconds. `{ kind: "mesh",
|
|
479
|
+
shape, align? }` draws a small solid `box`, `sphere`, `tetrahedron` or
|
|
480
|
+
`octahedron`, `size` units across, for debris and confetti. With `align:
|
|
481
|
+
"world"`, the default, it turns by `rotation` about world +Y. With `align:
|
|
482
|
+
"velocity"` its +Y points along its velocity. Mesh particles take scene
|
|
483
|
+
lighting unless `unlit: true`. Billboards always keep their own colour. Both
|
|
484
|
+
take scene fog and post-processing such as bloom, so additive particles glow.
|
|
485
|
+
`softness` (world units) fades a billboard out where it nears the geometry
|
|
486
|
+
behind it, so smoke and glows do not cut a hard line through the floor. The
|
|
487
|
+
renderer then draws the scene's depth once more per frame, so set it only on
|
|
488
|
+
emitters that touch geometry. That depth pass skips transparent materials,
|
|
489
|
+
keeps alpha-tested cut-outs, and counts toward the draw call and
|
|
490
|
+
triangle budgets. 3D particles ignore `sprite` and `layer`, and
|
|
491
|
+
2D particles ignore `render` and `softness`. Validation warns about `render` or
|
|
492
|
+
`softness` on a 2D emitter, `softness` on a mesh emitter, and `sprite` on a
|
|
493
|
+
3D emitter, without failing. Editor Play, the 3D standalone player,
|
|
494
|
+
`nodetool game capture` and `capture_native_game_frame` draw 3D particles.
|
|
495
|
+
Translucent particles sort across render types without writing depth. Interleaved
|
|
496
|
+
types can increase draw calls. A scene transition clears the particle field.
|
|
497
|
+
Within a scene, detached world particles retain world-space death chains until
|
|
498
|
+
they finish. Storage grows lazily within the scene particle limit.
|
|
499
|
+
|
|
500
|
+
## 3D collision layers
|
|
501
|
+
|
|
502
|
+
`collisionLayers` names up to 16 layers, and the layer at index `i` owns bit
|
|
503
|
+
`i`. `collisionMatrix` lists the layer pairs that do not collide, in either
|
|
504
|
+
order. A layer paired with itself stops its colliders from touching each other.
|
|
505
|
+
Every unlisted pair collides. Set both with `set_game {collision_layers,
|
|
506
|
+
collision_matrix}`, and put a collider on a layer with `update_entity set
|
|
507
|
+
collider3d {layer}`. A layered collider derives `category` from its bit and
|
|
508
|
+
`mask` from the matrix, so leave its raw bits at `1` and `65535`. Validation
|
|
509
|
+
rejects an unknown layer name, a pair listed twice in the matrix and
|
|
510
|
+
conflicting layer bits. Raw `category` and `mask` remain an override for
|
|
511
|
+
colliders without a layer. A collider without a layer keeps the default
|
|
512
|
+
category `1`, which is the bit of `collisionLayers[0]`. It therefore shares the
|
|
513
|
+
first layer's pairs: a layer paired with `collisionLayers[0]` does not touch
|
|
514
|
+
unlayered colliders either. Name the first layer for the world, or give such
|
|
515
|
+
colliders a layer. 2D colliders keep raw bits only.
|
|
516
|
+
|
|
517
|
+
## 3D physics materials
|
|
518
|
+
|
|
519
|
+
`physicsMaterials` names up to 64 surfaces that colliders share. Each material
|
|
520
|
+
has `friction` (0 to 4, default 0.5), `restitution` (0 to 1, default 0),
|
|
521
|
+
`frictionCombine` and `restitutionCombine`. A combine rule is `average`
|
|
522
|
+
(default), `min`, `multiply` or `max`. When two touching colliders use
|
|
523
|
+
different rules, the later rule in that list wins. Use `max` restitution for a
|
|
524
|
+
bouncy ball and `min` friction for ice. Declare materials with `set_game
|
|
525
|
+
{physics_materials: {name: material}}`, and remove them all with
|
|
526
|
+
`physics_materials: null`. Point a collider at one with `update_entity set
|
|
527
|
+
collider3d {physicsMaterial: name}` and clear it with `physicsMaterial: null`.
|
|
528
|
+
A collider with a material takes all four values from it, so leave its inline
|
|
529
|
+
`friction` at 0.5 and `restitution` at 0 and set no inline combine rule.
|
|
530
|
+
Validation rejects an unknown material name and conflicting inline values. A
|
|
531
|
+
collider without a material keeps its inline values and may set
|
|
532
|
+
`frictionCombine` and `restitutionCombine` itself. Existing documents name no
|
|
533
|
+
material and simulate as before. 2D colliders have no friction or
|
|
534
|
+
restitution.
|
|
535
|
+
|
|
536
|
+
## Audio mix and spatial audio
|
|
537
|
+
|
|
538
|
+
Set the document mix with `set_audio {mixer}` in 2D and 3D. `set_audio
|
|
539
|
+
{mixer: null}` removes it. Every mixer has the buses `master`, `music`, `sfx`,
|
|
540
|
+
`voice` and `ui`. Add buses under `mixer.buses` with `parent` (default
|
|
541
|
+
`master`), `volume` (linear, 1 is unity), `muted`, `lowpassHz` and
|
|
542
|
+
`reverbSend`. A bus multiplies its parent's gain, so muting `sfx` silences its
|
|
543
|
+
children. Scene music plays on `music` and other audio on `sfx` unless
|
|
544
|
+
`mixer.assetBuses` maps the asset slot to another bus.
|
|
545
|
+
|
|
546
|
+
The master bus ends in a limiter at `limiter.thresholdDb` (default -1 dB) that
|
|
547
|
+
does not change levels below the threshold. The default ducking rule lowers
|
|
548
|
+
`music` to 0.35 while a voice plays on `voice` or a child of it. Replace
|
|
549
|
+
`ducking` to change or remove that rule.
|
|
550
|
+
|
|
551
|
+
A mixer snapshot under `mixer.snapshots` overrides named fields of named buses.
|
|
552
|
+
`transitions` move to a snapshot over `fadeTicks` when the simulation emits a
|
|
553
|
+
`trigger` event, a `win` event, or enters a scene. The snapshot `base` returns
|
|
554
|
+
to the base mix. Restarting or restoring a session returns to the base mix or
|
|
555
|
+
the scene's snapshot without a fade. The mix is presentation state. It is not
|
|
556
|
+
saved in game snapshots and never changes playtest results or replay.
|
|
557
|
+
|
|
558
|
+
Set `audioSource.spatial: true` to play an entity's effect at its position in
|
|
559
|
+
2D and 3D. The listener is the active camera, so an emitter right of the camera
|
|
560
|
+
plays in the right ear. Effects play at full volume within `minDistance`
|
|
561
|
+
(default 1 world unit) and fall off by `distanceModel` (`linear`, `inverse`
|
|
562
|
+
by default, or `exponential`) and `rolloff` (default 1) out to `maxDistance`
|
|
563
|
+
(default 50), which must exceed `minDistance`. The `linear` model clamps
|
|
564
|
+
`rolloff` to 1 and is silent at `maxDistance`. A 3D `cone {innerAngle,
|
|
565
|
+
outerAngle, outerGain}` points along the entity's -Z axis. 2D emitters ignore
|
|
566
|
+
the cone. `doppler` (default 0, 1 is physical) shifts pitch with emitter and
|
|
567
|
+
camera motion. Set a field to `null` in `update_entity` to return it to its
|
|
568
|
+
default. The event that starts the effect carries the emitter position, so a
|
|
569
|
+
collectible that despawns as it plays still sounds where it was. Spatial
|
|
570
|
+
settings never change simulation.
|
|
571
|
+
|
|
572
|
+
## 3D animation graphs
|
|
573
|
+
|
|
574
|
+
A 3D animation graph lives in the document's `animationGraphs` map. An
|
|
575
|
+
entity's `animator3d.graph` names one, and its `animator3d.clips` maps the
|
|
576
|
+
graph's clip aliases to prepared clip IDs, so one graph can drive several rigs.
|
|
577
|
+
Store a graph with `set_animation_graph {graph_id, graph}` and delete it with
|
|
578
|
+
`remove_animation_graph {graph_id}`. An animator with a graph cannot also set
|
|
579
|
+
`initialClip`.
|
|
580
|
+
|
|
581
|
+
A graph declares `parameters` (`float` with a default, `bool` with a default,
|
|
582
|
+
or `trigger`) and one to eight `layers`. Each layer has an `initialState`,
|
|
583
|
+
`states` and `transitions`. A state plays a `motion`: one `clip`, a `blend1d`
|
|
584
|
+
over a float parameter with points in increasing `value` order, or a `blend2d`
|
|
585
|
+
over two float parameters. `speed` scales the animator's `playbackRate`, and
|
|
586
|
+
`loop` defaults to true. The first layer is the unmasked override base. Later
|
|
587
|
+
layers can be `additive` and can set `weight` and a `mask` of prepared model
|
|
588
|
+
node IDs. A mask covers each listed node and its descendants.
|
|
589
|
+
|
|
590
|
+
A transition names `from` (a state ID, or `*` for any state), `to`,
|
|
591
|
+
`conditions`, optional `exitTicks` and `durationTicks` for the crossfade. Every
|
|
592
|
+
condition must hold. Float conditions use `gt`, `gte`, `lt`, `lte`, `eq` or
|
|
593
|
+
`neq` with a `value`. Bool conditions use `true` or `false`. A trigger uses
|
|
594
|
+
`set` and resets once a transition consumes it. `exitTicks` is how long the
|
|
595
|
+
source state must run before the transition may fire. Each transition needs a
|
|
596
|
+
condition or `exitTicks`. A crossfading layer does not start another
|
|
597
|
+
transition. Transitions are checked in document order.
|
|
598
|
+
|
|
599
|
+
Scripts set parameters with `setAnimParam {name, value}`. Pass `true` to fire
|
|
600
|
+
a trigger. `playAnimation {clip}` on a graph entity switches the base layer
|
|
601
|
+
directly to the state with that ID, or else to the first state that plays that
|
|
602
|
+
clip alias, using `animator3d.transitionTicks`. Example locomotion: a `speed`
|
|
603
|
+
float, a `move` state with `blend1d` points idle at 0, walk at 2 and run at 6,
|
|
604
|
+
and a script that sends `setAnimParam` with the character's planar speed.
|
|
605
|
+
|
|
606
|
+
Graph state (current state, entry tick, crossfade source and parameter values)
|
|
607
|
+
is simulation state, saved in snapshots. Check it with a playtest that sets
|
|
608
|
+
`restore_at_tick` and asserts `replay_matches`. The renderer samples the
|
|
609
|
+
resolved pose at the interpolated tick. Blended clips share one normalized
|
|
610
|
+
phase. The pose sampler blends node translation, rotation and scale. It does
|
|
611
|
+
not blend morph targets.
|
|
612
|
+
|
|
613
|
+
Model preparation derives `clipDurations` from verified GLB time accessors.
|
|
614
|
+
Bindings with this metadata retain a normalized simulation phase when blend
|
|
615
|
+
parameters change, including across save and restore. Do not invent or edit
|
|
616
|
+
durations. For a legacy binding without them, regenerate a model candidate from
|
|
617
|
+
its existing source with `generate_game_asset`, then explicitly install it with
|
|
618
|
+
`install_native_game_asset`. Installation rederives the durations from its bytes.
|
|
619
|
+
Until then, blends keep legacy elapsed-time sampling and validation reports
|
|
620
|
+
`legacy_animation_phase`.
|
|
621
|
+
|
|
622
|
+
## 3D rendering
|
|
623
|
+
|
|
624
|
+
Set every `environment` field with `update_scene`. It replaces the whole
|
|
625
|
+
`environment`, so send the existing `background`, `ambient`, `fog`, `shadows`,
|
|
626
|
+
`sky` and `postProcessing` with the changed field. Capture the scene and view
|
|
627
|
+
the frames to review a change.
|
|
628
|
+
|
|
629
|
+
`environment.fog` accepts either linear `{ color, near, far }` or height
|
|
630
|
+
`{ kind: "height", color, density, baseHeight, heightFalloff, maxOpacity }`.
|
|
631
|
+
Height fog replaces linear fog and integrates exponential density along the
|
|
632
|
+
camera ray. `density` (0.000001 to 10, default 0.02) is extinction per world
|
|
633
|
+
unit at `baseHeight` (-10000 to 10000, default 0). `heightFalloff` (0.000001
|
|
634
|
+
to 100, default 0.2) reduces density above that height. `maxOpacity` (0 to 1,
|
|
635
|
+
default 1) limits coverage. Omit `fog` to disable it. Both camera projections,
|
|
636
|
+
rigid and animated models, and 3D particles receive scene fog.
|
|
637
|
+
|
|
638
|
+
Add an image projection with `decal3d: { assetId, receiverId, size: { x, y, z },
|
|
639
|
+
opacity, renderOrder }`. `assetId` names a verified PNG, JPEG or WebP image
|
|
640
|
+
slot. Browse a staged image with `browse_native_game_assets`, then use its
|
|
641
|
+
binding with `install_native_game_asset`. `receiverId` names one existing
|
|
642
|
+
non-template primitive or rigid model entity in the same scene. Use a separate
|
|
643
|
+
projector entity whose `transform3d` positions and rotates the projection box.
|
|
644
|
+
Its scale multiplies `size`. Parent the projector to the receiver to keep the
|
|
645
|
+
decal attached as it moves. Otherwise the projection volume stays in world
|
|
646
|
+
space. Each size axis is 0.001 to 1000 world units,
|
|
647
|
+
`opacity` is 0 to 1 (default 1), and integer `renderOrder` is 0 to 31 (default
|
|
648
|
+
0). Higher order draws later. Ties follow entity order.
|
|
649
|
+
|
|
650
|
+
A scene accepts at most 32 authored decals. Projection follows moving rigid
|
|
651
|
+
receivers and selected visible model nodes, respects foreground depth, and
|
|
652
|
+
supports image transparency. Skinned, morphed or instanced receivers, and
|
|
653
|
+
transparent or alpha-masked receiver materials, report a diagnostic instead of
|
|
654
|
+
projecting. A receiver with `animator3d` is invalid. Projection also reports a
|
|
655
|
+
diagnostic when its bounded node, triangle or output work exceeds the renderer
|
|
656
|
+
limit. Decals affect presentation only and never add collision surfaces.
|
|
657
|
+
|
|
658
|
+
`environment.shadows` is `{ enabled, mapSize, extent, cascades? }`. Without
|
|
659
|
+
`cascades`, the one shadow-casting directional light draws a single map that
|
|
660
|
+
covers `extent` meters around the light. For scenes deeper than about 30 m, add
|
|
661
|
+
`cascades: { count, split, maxDistance }`. It splits the camera view into
|
|
662
|
+
`count` maps (2 to 4, default 3) up to `maxDistance` meters (default 200), so
|
|
663
|
+
near shadows stay sharp and far shadows stay continuous. `split` (0 to 1,
|
|
664
|
+
default 0.5) gives nearby cascades more resolution as it rises. `extent` is
|
|
665
|
+
ignored while cascades are on. Only one directional light can cast shadows.
|
|
666
|
+
|
|
667
|
+
Point and spot lights cast shadows when their `light3d.castShadow` is `true`.
|
|
668
|
+
At most 4 of them may cast per scene. Validation rejects a fifth. If spawned
|
|
669
|
+
prefabs push a frame over the budget, the extra lights render unshadowed and
|
|
670
|
+
the capture stats report one diagnostic. Each local shadow re-renders the scene
|
|
671
|
+
(six times for a point light), so enable it only on lights that need it.
|
|
672
|
+
Every light accepts `shadowBias` (-0.01 to 0.01) and `shadowNormalBias` (0 to
|
|
673
|
+
1), both default 0. Raise `shadowNormalBias` to about 0.02 to 0.05, or set
|
|
674
|
+
`shadowBias` to about -0.0005, when lit surfaces show striped shadow acne. Too
|
|
675
|
+
much bias detaches shadows from their casters. Set lights with `update_entity`.
|
|
676
|
+
|
|
677
|
+
`environment.sky` sets the background and image-based lighting. Omit it, or use
|
|
678
|
+
`{ kind: "color" }`, for the solid `background` color without environment
|
|
679
|
+
lighting. `{ kind: "procedural", sunEntityId?, turbidity, rayleigh,
|
|
680
|
+
groundColor, intensity }` renders a physical sky. Its sun follows
|
|
681
|
+
`sunEntityId`, which must name a directional light in the same scene. Without
|
|
682
|
+
it, the sun follows the scene's first directional light. Rotate that light to
|
|
683
|
+
move the sun. `turbidity` (1 to 20, default 10) adds haze. `rayleigh` (0 to 4,
|
|
684
|
+
default 2) deepens blue and sunset tones. `groundColor` fills the lower
|
|
685
|
+
hemisphere. `intensity` (0 to 8, default 1) scales both the sky background and
|
|
686
|
+
its reflections. Metal and glossy materials reflect the sky. Ambient light
|
|
687
|
+
still adds on top, so lower `ambient.intensity` when the sky lights the scene.
|
|
688
|
+
HDRI skies are not available yet: an HDRI asset cannot be installed.
|
|
689
|
+
|
|
690
|
+
`environment.postProcessing` grades the rendered image. It is presentation only
|
|
691
|
+
and never changes simulation or snapshots. `enabled: false` keeps the settings
|
|
692
|
+
and renders as if they were absent. The fields are:
|
|
693
|
+
|
|
694
|
+
- `exposure` (1/32 to 16, default 1) multiplies scene light before tone mapping.
|
|
695
|
+
- `toneMapping` is `aces` (default), `agx`, `neutral` or `linear`. AgX keeps
|
|
696
|
+
saturated highlights from turning white. Neutral keeps base colors closest to
|
|
697
|
+
their authored values. Linear scales by exposure and clips.
|
|
698
|
+
- `bloom` `{ threshold, softness, radius, intensity }` adds glow around light
|
|
699
|
+
above `threshold` (0 to 8, default 0.85). Emissive materials and bright
|
|
700
|
+
highlights bloom. `softness` (0 to 0.5) widens the threshold transition.
|
|
701
|
+
`radius` (0 to 1) spreads the glow, and `intensity` (0 to 4) sets its strength.
|
|
702
|
+
- `vignette` `{ intensity, radius, softness }` darkens the corners. `intensity`
|
|
703
|
+
(0 to 1) of 1 reaches black. Darkening starts at `radius` (0 at the center,
|
|
704
|
+
1 at the corner) and reaches full strength over `softness`.
|
|
705
|
+
- `antialias` is `msaa` (default), `fxaa`, `smaa` or `none`. `smaa` gives the
|
|
706
|
+
cleanest edges after the other effects. `fxaa` is cheaper and softer.
|
|
707
|
+
|
|
708
|
+
The passes run in this order: bloom, exposure and tone mapping, vignette,
|
|
709
|
+
antialiasing. Exposure and tone mapping alone cost nothing extra. Bloom,
|
|
710
|
+
vignette, or an `antialias` other than `msaa` renders through extra full-screen
|
|
711
|
+
passes, and the background color then passes through tone mapping too. SSAO,
|
|
712
|
+
depth of field, LUT grading and chromatic aberration are not available in 3D.
|
|
713
|
+
|
|
714
|
+
## Input bindings
|
|
715
|
+
|
|
716
|
+
Action and axis names come from `inputActions` and `inputAxes`. The optional
|
|
717
|
+
document-level `inputBindings` maps each name to physical controls. Set it with
|
|
718
|
+
`set_game {input_bindings}`, or pass `input_bindings: null` to return to the
|
|
719
|
+
defaults. The op replaces the whole map:
|
|
720
|
+
|
|
721
|
+
- `actions`: `{ action: [binding] }`. A binding is `{kind: "key", code}` with a
|
|
722
|
+
`KeyboardEvent.code` such as `KeyJ`, `{kind: "keyValue", key}` with a
|
|
723
|
+
`KeyboardEvent.key` such as `z` or `+` (letter case is ignored, and a key
|
|
724
|
+
that a `key` binding names by code does not also match),
|
|
725
|
+
`{kind: "mouseButton", button}`, `{kind: "gamepadButton", button}` in the
|
|
726
|
+
standard gamepad layout, `{kind: "gamepadAxis", axis, direction:
|
|
727
|
+
"negative"|"positive", threshold?}`, `{kind: "touchButton", label?}` or
|
|
728
|
+
`{kind: "touchStick", direction}`.
|
|
729
|
+
- `axes` (3D only): `{ axis: [binding] }` with `{kind: "keys", negative, positive}`
|
|
730
|
+
code lists, `{kind: "gamepadAxis", axis, deadZone?, invert?}`,
|
|
731
|
+
`{kind: "gamepadButtons", negative, positive}` or
|
|
732
|
+
`{kind: "touchStick", axis: "x"|"y", invert?}`. The binding with the largest
|
|
733
|
+
magnitude sets the value.
|
|
734
|
+
- `look` (3D only): a list of `{kind: "mouse", sensitivity?, invertY?}`,
|
|
735
|
+
`{kind: "gamepadStick", xAxis?, yAxis?, deadZone?, speed?, invertY?}` and
|
|
736
|
+
`{kind: "touchDrag", sensitivity?, invertY?}`.
|
|
737
|
+
|
|
738
|
+
A listed action or axis uses exactly the listed bindings, and an empty list
|
|
739
|
+
unbinds it. An unlisted one keeps the defaults generated from its name. In 2D,
|
|
740
|
+
arrows and WASD are `left`/`right`/`up`/`down` and Space is `space`, by key
|
|
741
|
+
position. Any other action is a `keyValue` binding of its name, so it follows
|
|
742
|
+
the player's keyboard layout. In 3D, `fire` is the left mouse button and F,
|
|
743
|
+
`jump` is Space, `respawn` is R, and `moveX`/`moveZ` read WASD and arrows.
|
|
744
|
+
Defaults add the gamepad d-pad and left stick for directions and movement,
|
|
745
|
+
button 0 for `jump`/`space`, the right trigger for `fire`, the right stick for
|
|
746
|
+
look, and touch buttons for every other action. Binding an undeclared action or
|
|
747
|
+
axis is a validation error.
|
|
748
|
+
|
|
749
|
+
Bindings only turn physical input into input frames. They never enter the
|
|
750
|
+
simulation, so playtests, replays and snapshots use the same `pressed`,
|
|
751
|
+
`justPressed`, `axes` and `look` frames. Gamepads are polled once per tick. The
|
|
752
|
+
3D players request mouse capture on click only when mouse look is bound.
|
|
753
|
+
Mouse actions work on the acquisition click and when capture is unavailable or
|
|
754
|
+
rejected. Explicit `look: []` disables capture. HUD buttons own their presses
|
|
755
|
+
before gameplay mouse or touch controls, and letterbox bars do not hit the HUD.
|
|
756
|
+
On a touch screen, editor play, public players and both standalone players
|
|
757
|
+
show a stick when an action or axis has a touch stick
|
|
758
|
+
binding and one button per action with a touch button binding. The 3D player
|
|
759
|
+
also turns the camera by dragging on the right half. Input pressed while play
|
|
760
|
+
is paused is dropped. Player-facing rebinding is not stored yet.
|
|
761
|
+
|
|
762
|
+
## HUD widget tree
|
|
763
|
+
|
|
764
|
+
`ui` on the document holds HUD widgets drawn in every scene. `ui` on a scene
|
|
765
|
+
holds widgets drawn above them in that scene. Set either with
|
|
766
|
+
`set_ui {scene_id?, ui}`, or `ui: null` to remove it. The op replaces the whole
|
|
767
|
+
tree: `{safeArea?, focusNavigation?, nodes: [node]}`. 2D and 3D documents
|
|
768
|
+
take the same tree and draw it with the same layout.
|
|
769
|
+
|
|
770
|
+
Every node has `kind`, `id`, and optionally `parent`, `anchor`, `pivot`,
|
|
771
|
+
`offset`, `width`, `height`, `visible` and `opacity`. `anchor` is a point
|
|
772
|
+
`{x, y}` in 0..1 of the parent's rectangle, or of the HUD rectangle for a root
|
|
773
|
+
node, so `{x: 1, y: 0}` is the top-right corner. `pivot` is the node's own point
|
|
774
|
+
placed there and defaults to `anchor`. `offset` moves the node in HUD pixels.
|
|
775
|
+
The 2D HUD rectangle is the camera view at `pixelsPerUnit` pixels per unit, so
|
|
776
|
+
a 16 by 9 camera at 32 pixels per unit gives 512 by 288. The 3D HUD rectangle
|
|
777
|
+
is `presentation.hudWidth` by `hudHeight`, stretched over the viewport. With
|
|
778
|
+
`safeArea` (default true) root anchors stay inside the device's safe-area
|
|
779
|
+
insets.
|
|
780
|
+
|
|
781
|
+
- `panel {color?, cornerRadius?}` fills its rectangle and holds children placed
|
|
782
|
+
by their anchors.
|
|
783
|
+
- `image {assetId}` draws an image asset. 3D documents bind no images, so a 3D
|
|
784
|
+
image node fails validation.
|
|
785
|
+
- `text {text, size?, color?, align?, fontId?}` sizes itself from its text
|
|
786
|
+
unless it has a width.
|
|
787
|
+
- `bar {source?, value?, max?, color?, background?, direction?}` fills
|
|
788
|
+
`value / max` (max defaults to 1). `source: {kind: "health", entityId}` reads
|
|
789
|
+
the entity's health and its health behavior's maximum.
|
|
790
|
+
- `button {action, text?, background?, size?, color?, fontId?}` presses a
|
|
791
|
+
declared input action while a mouse button or a finger holds it.
|
|
792
|
+
- `stack {direction?, gap?, padding?, align?}` lays children in a column or a
|
|
793
|
+
row and sizes itself from them.
|
|
794
|
+
- `grid {columns, gap?, padding?}` lays children in cells as large as the
|
|
795
|
+
largest child.
|
|
796
|
+
|
|
797
|
+
A child must come after its parent, and only panels, stacks and grids hold
|
|
798
|
+
children. Panels, images, bars and buttons need `width` and `height`. Ids are
|
|
799
|
+
unique across the document tree and each scene tree.
|
|
800
|
+
|
|
801
|
+
Scripts change nodes with `{kind: "ui", id, text?, value?, max?, visible?}`.
|
|
802
|
+
`text` applies to text and buttons, `value` and `max` to bars, and `visible`
|
|
803
|
+
to any node. A change lasts until the scene changes and is saved in snapshots.
|
|
804
|
+
A command for a missing node, or a field the node does not have, fails the
|
|
805
|
+
step. An `onDestroy` hook may return `ui` commands.
|
|
806
|
+
|
|
807
|
+
A button press reaches scripts as its action in `pressed` and `justPressed`,
|
|
808
|
+
exactly like a key, so replays and snapshots need nothing extra. In 3D a click
|
|
809
|
+
on a button reaches the HUD only while the mouse is not captured. With
|
|
810
|
+
`focusNavigation: true`, the gamepad D-pad moves focus between visible buttons
|
|
811
|
+
and button 0 presses the focused one. While the tree shows a button, those
|
|
812
|
+
gamepad buttons drive the HUD instead of the game's bindings. Existing `hud`
|
|
813
|
+
labels still draw, above the tree.
|
|
814
|
+
|
|
815
|
+
## Distance culling and frame budgets (3D)
|
|
816
|
+
|
|
817
|
+
Both are presentation only. They never change simulation, snapshots or replay.
|
|
818
|
+
|
|
819
|
+
- `set_performance {performance}` replaces document `performance`. `null`
|
|
820
|
+
removes it. `cullLayers` maps a layer name to `{maxDistance}` (at most 32
|
|
821
|
+
layers). `budgets` sets `drawCalls`, `triangles`, `particles` and `voices`.
|
|
822
|
+
- `update_entity` with `set: {renderCulling: {layer?, maxDistance?}}` hides
|
|
823
|
+
that entity and its children when the game camera is farther than the
|
|
824
|
+
distance from the entity's origin. The nearest setting in the parent chain
|
|
825
|
+
wins, and an entity's own `maxDistance` wins over its layer's. A layer must
|
|
826
|
+
be declared, or validation reports `missing_cull_layer`.
|
|
827
|
+
- Distance is measured to the entity origin, not to its nearest surface. Cull
|
|
828
|
+
props and small decoration. Do not cull large scenery such as terrain or
|
|
829
|
+
buildings, because they disappear while the camera is still close to their
|
|
830
|
+
edges. Never cull the player or anything the player must see to win. The
|
|
831
|
+
editor camera shows every entity.
|
|
832
|
+
- Budgets left out use the player defaults: 1000 draw calls, 1,000,000
|
|
833
|
+
triangles, 4096 particles and 24 voices. The standalone player warns in the
|
|
834
|
+
browser console once each time a budget is exceeded. The standalone players
|
|
835
|
+
and 3D capture count live particles. The 2D player uses the default particle
|
|
836
|
+
budget because 2D documents have no `performance`.
|
|
837
|
+
|
|
838
|
+
## Editor panels the user can open
|
|
839
|
+
|
|
840
|
+
Point the user to these when they work in the game editor alongside you.
|
|
841
|
+
|
|
842
|
+
- **Run 10 s** diagnoses the draft in an independent session and reports the
|
|
843
|
+
first failing tick and script time by entity. **Replay to tick** rewinds the
|
|
844
|
+
active play session after a script error. **Ask the assistant** passes the
|
|
845
|
+
script context to the game assistant.
|
|
846
|
+
- Editing an existing script source during play recompiles it without resetting
|
|
847
|
+
the tick or script state. A compilation failure keeps the previous source.
|
|
848
|
+
Changing script form between a function and lifecycle object requires
|
|
849
|
+
**Restart from tick 0**. Other draft changes also require a restart.
|
|
850
|
+
- The **Console** panel lists play-session errors, script errors with their
|
|
851
|
+
tick, reload compilation errors, script log lines, **Run 10 s** failures and
|
|
852
|
+
validation errors.
|
|
853
|
+
- The **Assets** panel shows the `browse_native_game_assets` catalog with
|
|
854
|
+
thumbnails. **Generate** and **Regenerate** run `generate_game_asset` for
|
|
855
|
+
image, speech and music slots. **Use** binds an older candidate and moves the
|
|
856
|
+
slot's frame and tile bindings onto it.
|
|
857
|
+
- Ctrl+K (Cmd+K on macOS) opens the command palette, where **Edit keyboard
|
|
858
|
+
shortcuts** rebinds editor shortcuts.
|
|
859
|
+
- When the script pane reports a draft conflict, local typing stays unsaved
|
|
860
|
+
until the user picks **Keep my version** or **Use draft version**.
|
|
861
|
+
- Restoring a revision asks for confirmation because it replaces the draft and
|
|
862
|
+
clears its undo history. The newest 100 published revisions are retained.
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
## 3D forces and torques
|
|
866
|
+
|
|
867
|
+
Return `{kind: "addForce", force: {x, y, z}}` to accelerate the issuing
|
|
868
|
+
entity's dynamic body, `{kind: "addTorque", torque: {x, y, z}}` to turn it,
|
|
869
|
+
or `{kind: "addForceAtPoint", force: {x, y, z}, point: {x, y, z}}` to apply
|
|
870
|
+
a force at a world-space point. An offset point also produces torque. All
|
|
871
|
+
vectors use world space, even when the body is rotated. Each component must
|
|
872
|
+
be finite and between -1000000 and 1000000.
|
|
873
|
+
|
|
874
|
+
Loads accumulate within the current simulation tick and clear after its
|
|
875
|
+
physics step. Return the commands on subsequent ticks to keep applying them.
|
|
876
|
+
Use `impulse` for an immediate velocity change. Forces require the issuing
|
|
877
|
+
entity's active dynamic body. Static, kinematic, missing and inactive bodies
|
|
878
|
+
reject them. A nonzero load wakes the body, while a zero load keeps a sleeping
|
|
879
|
+
body asleep. These commands are available only in 3D scripts.
|