@aethervtt/shard-project 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs.js +239 -1
- package/dist/host.js +21 -3
- package/dist/manifest.d.ts +6 -0
- package/dist/manifest.js +7 -1
- package/package.json +35 -29
package/dist/docs.js
CHANGED
|
@@ -747,6 +747,9 @@ whose path no longer exists is kept in the file and logged as \`prefab/stale-ove
|
|
|
747
747
|
`,
|
|
748
748
|
"add-sound.md": `# Add sound
|
|
749
749
|
|
|
750
|
+
For authored events, mixers, snapshots, acoustics, adaptive music and offline previews, read
|
|
751
|
+
[the audio guide](../audio.md).
|
|
752
|
+
|
|
750
753
|
Add \`"audio"\` to \`plugins\` in \`shard.json\`. Drop \`.ogg\`, \`.mp3\`, \`.wav\`, or \`.flac\` files under
|
|
751
754
|
\`assets/\` and run \`shard import\`: each is an \`AudioClip\` (MCP \`get_asset\` shows duration, channels,
|
|
752
755
|
sample rate, codec). Files stay compressed. Clips under \`music/\` stream; set \`"mode": "stream"\` in
|
|
@@ -765,7 +768,7 @@ the \`.meta\` for other long files, and \`loopStart\`/\`loopEnd\` (seconds) for
|
|
|
765
768
|
|
|
766
769
|
- Sources autoplay; set \`playing\` to start or stop one. A clip that doesn't loop clears
|
|
767
770
|
\`playing\` at its end and sends \`audio/AudioFinished\`. Spatial sources follow their transform;
|
|
768
|
-
the listener
|
|
771
|
+
the fallback listener has the lowest entity ID; setAudioListener selects one explicitly.
|
|
769
772
|
- \`rolloff\`: \`inverse\` (default, natural), \`linear\` (silent at \`maxDistance\`; past it the source
|
|
770
773
|
goes virtual and holds no voice), \`exponential\`. \`panning\`: \`equal-power\` or \`hrtf\`.
|
|
771
774
|
- One-shots from code: \`playSound(world, weapon.sound, { position: muzzle, bus: 'sfx' })\` from
|
|
@@ -1165,6 +1168,81 @@ orbit down to 0.4 m vertex spacing on the ground. Put the camera in the planet's
|
|
|
1165
1168
|
\`describe_terrain\` shows \`detail.lodBias\` above 1 while it's coarsening.
|
|
1166
1169
|
- Give it air: \`"render/Atmosphere": {}\` on the planet entity takes the planet's radius, draws
|
|
1167
1170
|
the sky from the ground to orbit, and hazes distant terrain (tune-an-atmosphere.md).
|
|
1171
|
+
- Rocks, trees and grass: give biomes (or the planet) a scatter set (scatter-props-and-foliage.md).
|
|
1172
|
+
- A bounded landscape you shape (roads, valleys) rather than a whole planet: make-a-terrain.md.
|
|
1173
|
+
`,
|
|
1174
|
+
"make-a-terrain.md": `# Make a heightfield terrain (an open world)
|
|
1175
|
+
|
|
1176
|
+
A bounded landscape, 256 m to 64 km a side, shaped on purpose: a \`*.terrain.json\` file stacks
|
|
1177
|
+
height layers (noise, heightmap images, splines that flatten, raise or carve) and paint layers, and an
|
|
1178
|
+
entity with \`terrain/Terrain\` (it brings \`transform/Grid\`) draws, streams and collides with it. Add
|
|
1179
|
+
\`terrain\` to \`shard.json\`'s plugins (with \`render/forward\` and \`physics3d\`). The file's schema is
|
|
1180
|
+
\`.shard/schemas/terrain.schema.json\`; coordinates are metres from the terrain's corner.
|
|
1181
|
+
|
|
1182
|
+
\`\`\`json
|
|
1183
|
+
{ "size": [4096, 4096], "spacing": 1, "heightRange": [-200, 800], "seed": 7,
|
|
1184
|
+
"splines": { "road": { "points": [[120, "ground", 300], [900, "ground", 420]], "width": 8, "falloff": 14 } },
|
|
1185
|
+
"height": [
|
|
1186
|
+
{ "noise": { "path": "assets/noise/hills.noise.json" }, "scale": 260 },
|
|
1187
|
+
{ "image": { "path": "assets/terrain/valley.height.png" }, "at": [2048, 1800], "size": [1200, 900],
|
|
1188
|
+
"rotation": 20, "range": [0, 140], "blend": "max", "falloff": 80 },
|
|
1189
|
+
{ "spline": "road", "mode": "flatten" } ],
|
|
1190
|
+
"layers": [{ "name": "grass" }, { "name": "rock", "triplanar": true }, { "name": "gravel" }],
|
|
1191
|
+
"paint": [{ "layer": "grass" }, { "layer": "rock", "slope": [32, 90], "blend": 6 },
|
|
1192
|
+
{ "layer": "gravel", "spline": "road", "blend": 2 }] }
|
|
1193
|
+
\`\`\`
|
|
1194
|
+
|
|
1195
|
+
- Sizes are whole numbers of roots: 64 \xD7 spacing \xD7 2^k a side, at most 64 roots (2048, 4096 or
|
|
1196
|
+
16384 m at 1 m spacing). \`heightRange\` is what the bake stores in 16 bits: keep it close to the
|
|
1197
|
+
real range (a clipped height is \`terrain/out-of-range\`).
|
|
1198
|
+
- Heightmaps are 16-bit grayscale PNGs (name them \`*.height.png\`, or set \`"importer": "heightmap"\`
|
|
1199
|
+
in the \`.meta\`) or \`.r16\`/\`.r32\` raw files with \`width\` and \`height\` in the \`.meta\` settings.
|
|
1200
|
+
- \`shard import\` bakes stale terrains into \`.shard/cache/terrain\` (only the blocks an edit touches);
|
|
1201
|
+
\`shard terrain bake\` forces it, \`shard terrain stats\` shows size on disk and time per block.
|
|
1202
|
+
\`shard dev\` rebakes after you save the file.
|
|
1203
|
+
- Standing on it: characters, dynamic bodies, \`NavAgent\`s and \`terrain/TerrainAnchor\`s get
|
|
1204
|
+
heightfield colliders within \`colliderRadius\`, exactly the drawn ground. \`terrainHeightAt(world,
|
|
1205
|
+
terrain, x, z)\` is the ground under a point; \`await loadTerrainRegion(world, terrain, rect)\` first
|
|
1206
|
+
makes it exact away from anchors (spawn points). A plain \`nav/NavMesh\` paths on it.
|
|
1207
|
+
- Rocks and trees: \`terrain/Terrain.scatter\` takes a \`*.scatter.json\` (scatter-props-and-foliage.md).
|
|
1208
|
+
- Check it: MCP \`terrain_map\` (\`{ "mode": "layers" }\`, or \`"bake"\` to see which blocks an edit
|
|
1209
|
+
rebuilt), \`sample_terrain\` (\`{ "points": [[120, 300], [124, 300]] }\`: is the road flat across?),
|
|
1210
|
+
and \`describe_terrain\` (bake, pages, collider tiles). Overlays \`terrain-lod\`, \`terrain-pages\`
|
|
1211
|
+
and \`terrain-colliders\`.
|
|
1212
|
+
`,
|
|
1213
|
+
"scatter-props-and-foliage.md": `# Scatter rocks, trees and grass
|
|
1214
|
+
|
|
1215
|
+
Add \`scatter\` to \`shard.json\`'s plugins. A \`*.scatter.json\` (a \`scatter/ScatterSet\`) holds rules;
|
|
1216
|
+
put it on a biome (\`"scatter": { "path": "assets/scatter/forest.scatter.json" }\` in a \`*.biome.json\`:
|
|
1217
|
+
it applies where that biome dominates), on the planet (\`terrain/Planet.scatter\`: everywhere), or on
|
|
1218
|
+
a flat level's ground mesh (\`scatter/ScatterSurface\` next to its \`render/Mesh3d\`).
|
|
1219
|
+
|
|
1220
|
+
\`\`\`json
|
|
1221
|
+
{ "rules": [
|
|
1222
|
+
{ "name": "trees", "items": [{ "generator": "shard/Tree", "variants": 6 }],
|
|
1223
|
+
"density": 0.02, "spacing": 4, "masks": { "slope": [0, 25] }, "scale": [0.7, 1.3],
|
|
1224
|
+
"collider": "ball", "range": 400 },
|
|
1225
|
+
{ "name": "grass", "kind": "foliage", "items": [{ "generator": "shard/GrassClump", "variants": 4 }],
|
|
1226
|
+
"density": 5, "avoid": ["trees"], "range": 50 } ] }
|
|
1227
|
+
\`\`\`
|
|
1228
|
+
|
|
1229
|
+
- Two kinds. \`prop\` rules spawn entities (\`scatter/Prop\`, with LODs and an optional collider) in
|
|
1230
|
+
chunks around cameras and \`TerrainAnchor\`s; \`foliage\` rules are GPU instances near cameras only,
|
|
1231
|
+
millions of them, no entities. Grass, flowers and small pebbles are foliage; anything you bump into is a prop.
|
|
1232
|
+
- Items are mesh generators (\`shard/Rock\`, \`shard/Tree\`, \`shard/Bush\`, \`shard/GrassClump\`,
|
|
1233
|
+
\`shard/Crystal\`, or a project's) with \`params\` and \`variants\` (4\u20138 shapes read as unique), or
|
|
1234
|
+
prefabs. Without a \`material\`, engine generators get \`scatter/Vegetation\` tinted for their kind.
|
|
1235
|
+
- \`density\` is per m\xB2 where the masks pass, \`spacing\` the least distance between items. Too dense
|
|
1236
|
+
for the spacing is \`scatter/density-too-high\` (it says the most that fits). Rule order is priority:
|
|
1237
|
+
\`avoid\` names earlier rules. \`align\` 0 stands items upright, 1 along the slope. \`sink\` buries
|
|
1238
|
+
them by a share of their height.
|
|
1239
|
+
- Placement is a global jittered lattice: the same seed puts the same tree in the same place on every
|
|
1240
|
+
visit and machine. A prop the game despawns stays gone (saved in \`scatter/Removed\`).
|
|
1241
|
+
- Check it: MCP \`preview_generator\` with \`{ "generator": "assets/scatter/forest.scatter.json" }\`
|
|
1242
|
+
shows the set on a 64 m patch from above and at eye level; \`describe_scatter\` gives per-rule
|
|
1243
|
+
counts; \`sample_scatter\` lists props near a point (\`{ "entity": "landing-pad", "radius": 10 }\`).
|
|
1244
|
+
Overlays \`scatter\` and \`foliage-chunks\` show placements in a screenshot.
|
|
1245
|
+
- \`scatter/Budget\` caps props spawned per frame; \`scatter/Wind\` sways foliage and leaves.
|
|
1168
1246
|
`,
|
|
1169
1247
|
"tune-an-atmosphere.md": `# Tune an alien sky
|
|
1170
1248
|
|
|
@@ -1264,6 +1342,27 @@ browser, and so do real frame times.
|
|
|
1264
1342
|
|
|
1265
1343
|
With the MCP server attached to a running page: \`metrics_reset\`, exercise the scene, then
|
|
1266
1344
|
\`metrics_record\` returns the same record the scripts write.
|
|
1345
|
+
`,
|
|
1346
|
+
"find-what-is-slow.md": `# Find what's slow, or what stutters
|
|
1347
|
+
|
|
1348
|
+
The profiler is always on. Systems, schedules, the frame, render-graph encodes (\`render/<node>\`),
|
|
1349
|
+
GPU passes (\`gpu:<node>\`), worker jobs and asset loads are spans.
|
|
1350
|
+
|
|
1351
|
+
- On average: \`describe_perf\` (or \`shard profile\`'s top spans) lists last, avg, p95 and max ms per
|
|
1352
|
+
span over 120 frames. \`{ "spans": ["render"] }\` keeps the spans a key covers.
|
|
1353
|
+
- Against a budget: \`describe_budgets\` names the machine, lists every budget with its latest p95
|
|
1354
|
+
(over budget first), and splits a scenario's frame into slices, each measured share next to
|
|
1355
|
+
its budget share. Where \`describe_perf\` says \`"overlapping": true\` (Apple and other tile GPUs),
|
|
1356
|
+
pass times don't add up: \`ablate_passes\` measures what each pass costs.
|
|
1357
|
+
- A stutter: \`capture_perf { "frames": 300 }\`, or a flight recorder that waits for one:
|
|
1358
|
+
\`{ "until": { "frameMs": 30 } }\`. Read \`worst\`: each slow frame lists \`over\`, the spans that ran
|
|
1359
|
+
longest above their median there. The first one is the lead.
|
|
1360
|
+
- Your own code: \`const SPAN = defineSpan('<project>/spawn')\` once, then in a system
|
|
1361
|
+
\`const t = profiler.begin(SPAN)\` \u2026 \`profiler.end(t)\` with \`world.resource(ProfilerResource)\`.
|
|
1362
|
+
- The line: \`shard profile --cpu-prof\` (or \`"sample": true\`) adds the hottest functions and
|
|
1363
|
+
writes a \`.cpuprofile\`. Traces go to \`.shard/captures/*.trace.json\`: open them in Perfetto.
|
|
1364
|
+
- In a browser, \`shard dev\`'s pages are cross-origin isolated, so timings are to 5 \xB5s. Turn on the
|
|
1365
|
+
\`perf\` overlay (\`debug_overlays\`) to watch frame time and the top spans live.
|
|
1267
1366
|
`
|
|
1268
1367
|
};
|
|
1269
1368
|
function agentsBlock(manifest) {
|
|
@@ -1281,6 +1380,7 @@ shard import --json # import new and changed asset files
|
|
|
1281
1380
|
shard mv <from> <to> # move an asset and rewrite references to it
|
|
1282
1381
|
shard tiles read <map> # tilemap cells as rows; tiles edit <map> --cell x,y=name
|
|
1283
1382
|
shard run --frames 600 # headless run
|
|
1383
|
+
shard profile --frames 600 # capture spans: the worst frames and what grew in them
|
|
1284
1384
|
shard screenshot ${manifest.startScene} --out shot.png
|
|
1285
1385
|
shard test --json # gameplay tests in tests/
|
|
1286
1386
|
shard check --json # type-check scripts: file, line, column
|
|
@@ -1301,6 +1401,9 @@ shard mcp # MCP server for this project (see .mcp.json)
|
|
|
1301
1401
|
- \`.agents/components.md\`: every component and field; \`.agents/assets.md\`: importers and
|
|
1302
1402
|
project data types;
|
|
1303
1403
|
\`.agents/errors.md\`: error codes
|
|
1404
|
+
- \`.agents/audio.md\`: authored sound, music, mixing and measured previews
|
|
1405
|
+
- \`.agents/effects.md\`: reusable effect timelines, target bindings and owned lifetimes
|
|
1406
|
+
- \`.agents/vfx.md\`: trails, beams, projected decals and receiver masks
|
|
1304
1407
|
- \`.agents/skills/\`: recipes for common tasks
|
|
1305
1408
|
|
|
1306
1409
|
Engine plugins available: ${Object.entries(BUILTIN_PLUGINS).map(([n, d]) => `\`${n}\` (${d})`).join("; ")}.
|
|
@@ -1322,8 +1425,143 @@ Project-specific notes for agents go here; \`shard docs\` keeps them.
|
|
|
1322
1425
|
${existing}`;
|
|
1323
1426
|
return `${existing.slice(0, start)}${block}${existing.slice(end + GENERATED_END.length)}`;
|
|
1324
1427
|
}
|
|
1428
|
+
const AUDIO_GUIDE = `# Authoring audio
|
|
1429
|
+
|
|
1430
|
+
Enable \`audio/events\` for layered events and parameters, \`audio/mixer\` for effects and snapshots,
|
|
1431
|
+
\`audio/acoustics\` for zones/obstruction, and \`audio/music\` for adaptive music. Their dependencies
|
|
1432
|
+
are enabled automatically. Base \`audio\` keeps AudioSource/playSound for simple clips.
|
|
1433
|
+
|
|
1434
|
+
Assets are ordinary JSON: \`.audio-event.json\`, \`.audio-parameters.json\`, \`.audio-collection.json\`,
|
|
1435
|
+
\`.audio-mixer.json\`, \`.audio-snapshot.json\` and \`.audio-music.json\`. Use their generated schemas
|
|
1436
|
+
under \`.shard/schemas\`. Import edits with \`shard import\`; references can use paths or stable GUIDs.
|
|
1437
|
+
Authored gains use dB and pitches use semitones. Legacy volume/gain use linear multipliers.
|
|
1438
|
+
|
|
1439
|
+
Prepare a collection with \`await prepareAudio(world, ref)\`, play its events with \`playAudioEvent\`,
|
|
1440
|
+
and release the returned lease when its scope ends. Active instances retain their own pins.
|
|
1441
|
+
Use local \`parameters\` for surface labels, RPM or intensity; shared parameter assets connect
|
|
1442
|
+
weather/environment values to events, music and mixer curves. Built-in distance/speed are readonly.
|
|
1443
|
+
Sequence/no-repeat selectors retain bounded session history. Layers have independent RNG streams.
|
|
1444
|
+
|
|
1445
|
+
Simulation event decisions follow timeScale and pause. Their samples normally follow audio time;
|
|
1446
|
+
\`pitchLinked\` opts into scaled playback. Realtime UI/ambience can continue without visual frames.
|
|
1447
|
+
Transient blocked events expire after 250 ms by default. \`wait\` preserves their phase; \`restart\`
|
|
1448
|
+
starts their timeline again after unlock. Choose an explicit AudioListener with setAudioListener;
|
|
1449
|
+
the fallback is the lowest live listener entity ID. There is one audio session per app/protocol target.
|
|
1450
|
+
|
|
1451
|
+
Mixer domain buses (music/sfx/ui/voice) are direct children of master. Put submixes below them.
|
|
1452
|
+
Each event layer can declare up to eight \`sends\` with \`target\`, \`gainDb\`, and an optional
|
|
1453
|
+
parameter/points curve. Declare those routes in its mixer bus first. Per-instance sends tap after
|
|
1454
|
+
spatialization and preserve the originating category's user gate. Event \`tags\` can drive ducking;
|
|
1455
|
+
only physically rendering voices trigger it by default. Duck rules also include descendant buses.
|
|
1456
|
+
|
|
1457
|
+
Parent inserts process the summed children once. Pre/post sends feed named returns, and user
|
|
1458
|
+
category mute gates that category's dry and wet output. Apply imported mixer edits with
|
|
1459
|
+
\`await configureAudioMixer(world, ref)\`: scalar edits preserve effect history; topology/IR
|
|
1460
|
+
changes crossfade within the overlap budget. Delay and convolution tails are finite;
|
|
1461
|
+
decoded PCM and generated DSP kernels have separate bounded memory budgets. Snapshots compose
|
|
1462
|
+
by priority and stable identity; they do not overwrite user volume settings.
|
|
1463
|
+
Player controls apply to master and ordinary direct-child categories; nested/return controls reject.
|
|
1464
|
+
Use \`setAudioUserBus(world, 'music', { volume: 0.5, muted: false })\` for player settings;
|
|
1465
|
+
\`setBus\` remains the independent gameplay fader. Effect target fields
|
|
1466
|
+
are \`effectId/parameter\` (for example \`water/frequency\`), and send fields are \`send/returnBus\`.
|
|
1467
|
+
Zones use spheres or world-axis boxes and at most four smoothly handed-off returns. Obstruction
|
|
1468
|
+
comes from an injected bounded query provider; headless tests can supply deterministic queries.
|
|
1469
|
+
Optional \`gainCurve\` and \`lowpassCurve\` map transmission to dB and Hz.
|
|
1470
|
+
|
|
1471
|
+
Music programs use decoded stems, fixed tempo/meter, source sample markers and a 960-PPQ grid.
|
|
1472
|
+
Rate conform is explicit and pitch changing, bounded by authored cents. Prepare then start a
|
|
1473
|
+
program, request beat/bar/marker transitions, and set intensity parameters. Current, incoming and
|
|
1474
|
+
crossfading sections own PCM; retired sections are released. Entire stem groups reserve voices.
|
|
1475
|
+
Pause/seek cancel future sources, and resume creates fresh scheduled sources at the saved phase.
|
|
1476
|
+
Cancelling an already audible destination pauses it and releases both sides of its crossfade.
|
|
1477
|
+
Replacing a stinger reserves overlap capacity first; failure preserves the existing stinger.
|
|
1478
|
+
Persist only intentional sustained events/music; save restoration diagnoses changed revisions.
|
|
1479
|
+
|
|
1480
|
+
Use \`audio.describe\` and \`audio.log\` (cursor plus truncation metadata), \`audio.events.inspect\`,
|
|
1481
|
+
\`audio.mix.inspect\`, \`audio.music.inspect\`, and \`audio.meters\`. Headless gains are modeled; meters
|
|
1482
|
+
must say unavailable there. Gameplay tests expose \`game.audio.describe/events/music/mix/log\`.
|
|
1483
|
+
CLI: \`shard audio call audio.parameter.set --attach --params '{"instance":1,"name":"rpm","value":0.8}'\`.
|
|
1484
|
+
Without --attach, the CLI uses a temporary headless target. MCP exposes matching audio_* tools.
|
|
1485
|
+
|
|
1486
|
+
\`shard audio preview plans/showcase.json --out captures/mix.wav --json\` runs Chromium's offline
|
|
1487
|
+
renderer through the same event/music/mixer implementations. The preview schema accepts clips,
|
|
1488
|
+
authored events/music, seed, controls, movement, duration, rate and tails. Controls evaluate at
|
|
1489
|
+
60 Hz aligned upward to 128-sample render quanta; the trace includes requested/actual times.
|
|
1490
|
+
Music timestamps are scheduled natively. Output includes WAV, decisions, peak/RMS/clipping,
|
|
1491
|
+
backend and capabilities. Limits: 60 seconds including tails and 64 MiB each for input/PCM/output.
|
|
1492
|
+
No audio device, microphone or GPU is needed. Install Playwright Chromium in the CLI package.
|
|
1493
|
+
See \`examples/audio-lab\` for a complete authored showcase and original synthetic assets.
|
|
1494
|
+
`;
|
|
1495
|
+
const VFX_GUIDE = `# Trails, beams and projected decals
|
|
1496
|
+
|
|
1497
|
+
"render/forward" includes ribbonsPlugin and decalsPlugin. SDK hosts can use forwardCorePlugin
|
|
1498
|
+
plus just the optional plugins they need. Components are schema-authored; use render.describe
|
|
1499
|
+
for ribbons/decals records, points/triangles, age, allocation sizes and structured error codes.
|
|
1500
|
+
|
|
1501
|
+
Trail follows its Transform (including parent/bone attachments) and local offset. Old points
|
|
1502
|
+
stay in world space. capacity bounds the history, lifetime expires points, minDistance rejects
|
|
1503
|
+
duplicates, and breakDistance clears history after teleporting. playing=false drains the trail;
|
|
1504
|
+
paused=true freezes its clock. Beam uses local start/end, or end relative to its target entity;
|
|
1505
|
+
local points override endpoints. segments/noise/seed control deterministic endpoint-beam shape.
|
|
1506
|
+
Both accept width, ordered widthCurve [{at,value}], color/endColor, opacity, repeat, scroll,
|
|
1507
|
+
blend alpha/additive, layers and a Texture handle. Width curves use trail age or beam distance.
|
|
1508
|
+
Colors are display-referred unlit overlays, not PBR lighting inputs. Alpha compositing uses
|
|
1509
|
+
stable creation order; intersecting transparent ribbons are not sorted per segment.
|
|
1510
|
+
|
|
1511
|
+
Decal projects along local -Z, with local X/Y as UVs and size as the clipping box. Mark eligible
|
|
1512
|
+
rigid Mesh3d entities with DecalReceiver; its mask must overlap receiverMask. The projector clips
|
|
1513
|
+
real triangles and caches them until geometry/transforms/masks change. maxAngle rejects grazing
|
|
1514
|
+
and back-facing surfaces; offset avoids z fighting. lifetime=0 persists; fadeOut fades the end
|
|
1515
|
+
of a finite lifetime. paused freezes age, playing=false hides it. maxVertices and a 32768 input
|
|
1516
|
+
triangle budget bound projection; overflow gives a diagnostic and no partial mark. Skinned,
|
|
1517
|
+
morph/compute-deformed and transparent receivers are unsupported. No PBR normal/roughness decals.
|
|
1518
|
+
|
|
1519
|
+
All texture handles resolve through the host AssetServer catalog (GUIDs work). No file picker,
|
|
1520
|
+
URL loading or database dependency is introduced. Prefabs carry reusable VFX component settings;
|
|
1521
|
+
0081 visual tracks own activation and cleanup. A beam visual track's endpoint binds another
|
|
1522
|
+
named sequence target. Use property tracks for opacity/width changes. Effects are presentation;
|
|
1523
|
+
the host remains responsible for damage, collision and persistent game state.
|
|
1524
|
+
|
|
1525
|
+
Preview: playground/vfx.html. Toggle each feature to isolate its pixels; replay a seed, pause,
|
|
1526
|
+
clear, and switch perspective/orthographic cameras. The host registers the original textures.
|
|
1527
|
+
`;
|
|
1528
|
+
const EFFECTS_GUIDE = `# Coordinate reusable effect sequences
|
|
1529
|
+
|
|
1530
|
+
Enable "effects" in shard.json and the plugins used by your tracks: "animation", "particles",
|
|
1531
|
+
"audio/events" and "render/forward" for visual tracks. The scene plugin handles owned prefab targets. Author *.effect-sequence.json
|
|
1532
|
+
against .shard/schemas/effect-sequence.schema.json. Hosts may supply the same JSON directly;
|
|
1533
|
+
all dependencies resolve through the host's AssetServer catalog, including GUID-only assets.
|
|
1534
|
+
|
|
1535
|
+
A sequence has duration, parameters, named targets and tracks. Targets with a prefab handle
|
|
1536
|
+
are spawned/owned; other targets are borrowed entity bindings. Property tracks interpolate
|
|
1537
|
+
float/vector/color component fields (from/to arrays, linear or smoothstep, optional parameter
|
|
1538
|
+
multiplier). Animation and particle tracks require owned targets. Audio tracks use authored
|
|
1539
|
+
AudioEvent assets. Visual tracks require owned targets and name render/Trail, render/Beam or
|
|
1540
|
+
render/Decal in component. They activate playing, pause their clocks, and stop at track end;
|
|
1541
|
+
trails drain their remaining history. Beam tracks may bind an endpoint by target name.
|
|
1542
|
+
Marker tracks record presentation milestones; gameplay applies damage itself.
|
|
1543
|
+
|
|
1544
|
+
Use prepareEffect(world, source), then playEffect(world, preparation, { bindings, parameters,
|
|
1545
|
+
seed, parent, ownerEntity }). The optional parent anchors owned targets in a local frame, so
|
|
1546
|
+
one prepared spell can play at different positions/orientations. Removing the parent cancels
|
|
1547
|
+
the effect unless another ownerEntity was supplied. Release the preparation when no more plays are needed: active instances
|
|
1548
|
+
retain it. cancelEffect restores borrowed fields, stops audio and removes owned prefab trees.
|
|
1549
|
+
pauseEffect pauses only that instance. Keep claimed fields free of other writers while playing.
|
|
1550
|
+
No runtime file picker, URL fetcher or persistence backend is assumed.
|
|
1551
|
+
|
|
1552
|
+
Protocol/MCP: effects.prepare, effects.play, effects.pause, effects.cancel, effects.release,
|
|
1553
|
+
effects.inspect (trace cursor, active instances and preparation references). The inspect trace
|
|
1554
|
+
reports late windows skipped after a large frame step. A missing target fails and cleans up the
|
|
1555
|
+
whole instance. Calls return structured effects/* errors. Dispose the app to release all plans.
|
|
1556
|
+
|
|
1557
|
+
Preview at playground/effects.html; its spell asset and host catalog adapter demonstrate the
|
|
1558
|
+
complete flow. Replay by cancelling and playing again; backward scrubbing is not implemented.
|
|
1559
|
+
`;
|
|
1325
1560
|
function generateDocs(manifest, errors) {
|
|
1326
1561
|
const files = {
|
|
1562
|
+
".agents/audio.md": AUDIO_GUIDE,
|
|
1563
|
+
".agents/effects.md": EFFECTS_GUIDE,
|
|
1564
|
+
".agents/vfx.md": VFX_GUIDE,
|
|
1327
1565
|
".agents/components.md": renderComponentCatalog(),
|
|
1328
1566
|
".agents/errors.md": renderErrorCatalog(errors),
|
|
1329
1567
|
".agents/generators.md": renderGeneratorCatalog(),
|
package/dist/host.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { animationPlugin } from "@aethervtt/shard-animation";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
audioAcousticsPlugin,
|
|
4
|
+
audioEventsPlugin,
|
|
5
|
+
audioMixerPlugin,
|
|
6
|
+
audioMusicPlugin,
|
|
7
|
+
audioPlugin
|
|
8
|
+
} from "@aethervtt/shard-audio";
|
|
3
9
|
import { controlsPlugin } from "@aethervtt/shard-controls";
|
|
4
10
|
import { dicePlugin } from "@aethervtt/shard-dice";
|
|
11
|
+
import { effectsPlugin } from "@aethervtt/shard-effects";
|
|
5
12
|
import { gltfPlugin } from "@aethervtt/shard-gltf";
|
|
6
13
|
import { gesturesPlugin, inputPlugin } from "@aethervtt/shard-input";
|
|
7
14
|
import { loadNavCache, Nav, navGridPlugin, navPlugin } from "@aethervtt/shard-nav";
|
|
@@ -13,6 +20,7 @@ import { forwardPlugin, renderPlugin, Shaders } from "@aethervtt/shard-render";
|
|
|
13
20
|
import { materialNoisePlugin } from "@aethervtt/shard-render/noise";
|
|
14
21
|
import { App, LogResource } from "@aethervtt/shard-runtime";
|
|
15
22
|
import { savePlugin } from "@aethervtt/shard-save";
|
|
23
|
+
import { scatterPlugin } from "@aethervtt/shard-scatter";
|
|
16
24
|
import { loadScene, ScenePlugin } from "@aethervtt/shard-scene";
|
|
17
25
|
import { spritePlugin } from "@aethervtt/shard-sprite";
|
|
18
26
|
import { terrainPlugin } from "@aethervtt/shard-terrain";
|
|
@@ -28,12 +36,15 @@ function buildApp(options) {
|
|
|
28
36
|
names.add("gestures");
|
|
29
37
|
names.add("render/forward");
|
|
30
38
|
}
|
|
39
|
+
if (names.has("audio/acoustics")) names.add("audio/mixer");
|
|
40
|
+
if (names.has("audio/music") || names.has("audio/mixer")) names.add("audio/events");
|
|
41
|
+
if (names.has("audio/events")) names.add("audio");
|
|
31
42
|
if (names.has("gestures")) names.add("input");
|
|
32
43
|
if (names.has("render/forward")) {
|
|
33
44
|
names.add("render");
|
|
34
45
|
names.add("core/transform");
|
|
35
46
|
}
|
|
36
|
-
if (names.has("physics3d") || names.has("physics2d") || names.has("audio") || names.has("ui") || names.has("nav") || names.has("nav/grid") || names.has("terrain"))
|
|
47
|
+
if (names.has("physics3d") || names.has("physics2d") || names.has("audio") || names.has("ui") || names.has("nav") || names.has("nav/grid") || names.has("terrain") || names.has("scatter"))
|
|
37
48
|
names.add("core/transform");
|
|
38
49
|
const app = new App({ seed: manifest.seed });
|
|
39
50
|
app.addPlugin(gltfPlugin);
|
|
@@ -63,12 +74,19 @@ function buildApp(options) {
|
|
|
63
74
|
if (names.has("gestures")) app.addPlugin(gesturesPlugin);
|
|
64
75
|
if (names.has("controls")) app.addPlugin(controlsPlugin);
|
|
65
76
|
if (names.has("audio")) app.addPlugin(audioPlugin({ backend: options.audio }));
|
|
77
|
+
if (names.has("audio/events")) app.addPlugin(audioEventsPlugin());
|
|
78
|
+
if (names.has("audio/mixer")) app.addPlugin(audioMixerPlugin());
|
|
79
|
+
if (names.has("audio/acoustics")) app.addPlugin(audioAcousticsPlugin());
|
|
80
|
+
if (names.has("audio/music")) app.addPlugin(audioMusicPlugin());
|
|
66
81
|
if (names.has("ui")) app.addPlugin(uiPlugin);
|
|
67
82
|
if (names.has("nav")) app.addPlugin(navPlugin);
|
|
68
83
|
else if (names.has("nav/grid")) app.addPlugin(navGridPlugin);
|
|
69
|
-
if (names.has("terrain"))
|
|
84
|
+
if (names.has("terrain"))
|
|
85
|
+
app.addPlugin(terrainPlugin({ workers: options.platform?.workers, fs: options.platform?.fs }));
|
|
86
|
+
if (names.has("scatter")) app.addPlugin(scatterPlugin({ workers: options.platform?.workers }));
|
|
70
87
|
app.addPlugin(noisePlugin);
|
|
71
88
|
app.addPlugin(ScenePlugin);
|
|
89
|
+
if (names.has("effects")) app.addPlugin(effectsPlugin);
|
|
72
90
|
app.addPlugin(
|
|
73
91
|
procgenPlugin({
|
|
74
92
|
fs: options.platform?.fs,
|
package/dist/manifest.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export type ManifestValue = Infer<typeof Manifest>;
|
|
|
28
28
|
export declare function validateManifest(json: unknown): ShardError[];
|
|
29
29
|
/** Built-in engine plugins a manifest can name, with what they bring. */
|
|
30
30
|
export declare const BUILTIN_PLUGINS: {
|
|
31
|
+
readonly effects: 'Prepared effect sequences: coordinated properties, animation, particles, audio and markers; add the feature plugins used by tracks';
|
|
31
32
|
readonly render: 'GPU device, render graph, views, shaders';
|
|
32
33
|
readonly 'render/forward': 'cameras, meshes, standard material, lights (includes render and core/transform)';
|
|
33
34
|
readonly sprite: 'sprites, atlases, frame animation, tilemaps (includes render/forward)';
|
|
@@ -40,10 +41,15 @@ export declare const BUILTIN_PLUGINS: {
|
|
|
40
41
|
readonly gestures: 'taps, long presses, drags, pinches, twists and the wheel as Gesture events, with claims and Escape to cancel (includes input)';
|
|
41
42
|
readonly controls: 'OrbitControls and MapControls on cameras, syncViews, PlaneDrag to drag entities along a plane, controls.describe (includes gestures, render/forward)';
|
|
42
43
|
readonly audio: 'AudioSource, AudioListener, playSound, buses and ducking, spatial audio, voice limits (includes core/transform)';
|
|
44
|
+
readonly 'audio/events': 'Authored layered events, parameters, collections and prepared voice lifetimes (includes audio)';
|
|
45
|
+
readonly 'audio/mixer': 'Hierarchical effects, sends and composable snapshots (includes audio/events)';
|
|
46
|
+
readonly 'audio/acoustics': 'Listener reverb zones and bounded injected obstruction queries (includes audio/mixer)';
|
|
47
|
+
readonly 'audio/music': 'Prepared stems, beat/bar/marker transitions and stingers (includes audio/events)';
|
|
43
48
|
readonly ui: 'UiNode trees under a UiRoot: flexbox layout, text, images, buttons, sliders, text fields, world-anchored markers, themes (includes core/transform)';
|
|
44
49
|
readonly nav: 'navigation: NavGrid A* (2D), NavMesh baked by Recast from NavSource geometry, OffMeshLink, path queries, NavAgent steering (includes core/transform)';
|
|
45
50
|
readonly 'nav/grid': 'navigation on NavGrids only, without loading the Recast WASM (includes core/transform)';
|
|
46
51
|
readonly terrain: 'planets: Planet cube-sphere terrain from noise graphs with LOD, geomorphing, oceans, biomes, collider chunks around characters, navmeshes on the surface; terrain.* methods (includes core/transform; add render/forward to draw it, physics3d for colliders)';
|
|
52
|
+
readonly scatter: 'props and foliage from ScatterSet rules (*.scatter.json) on planets (Planet.scatter, Biome.scatter) and ScatterSurface meshes: props as entities around cameras and anchors, GPU foliage near cameras; scatter.* methods; the engine generators shard/Rock, Tree, Bush, GrassClump and Crystal make their meshes (includes core/transform; add terrain for planets, render/forward to draw)';
|
|
47
53
|
readonly dice: 'the dice table (0054): dice skins, face layouts and effect recipes as data, dice.play and dice.describe, dice/RollRequest in scenes; tracks record on the calling thread (includes render/forward)';
|
|
48
54
|
readonly 'core/transform': 'Transform and hierarchy propagation';
|
|
49
55
|
};
|
package/dist/manifest.js
CHANGED
|
@@ -50,7 +50,7 @@ const Manifest = defineSchema(
|
|
|
50
50
|
),
|
|
51
51
|
plugins: t.list(t.string, {
|
|
52
52
|
default: ["render/forward", "input"],
|
|
53
|
-
description: "Engine plugins to enable, by name: render, render/forward, sprite, text, particles, animation, physics3d, physics2d, input, gestures, controls, audio, ui, nav, nav/grid, dice, core/transform."
|
|
53
|
+
description: "Engine plugins to enable, by name: render, render/forward, sprite, text, particles, animation, physics3d, physics2d, input, gestures, controls, audio, ui, nav, nav/grid, terrain, scatter, dice, core/transform."
|
|
54
54
|
})
|
|
55
55
|
},
|
|
56
56
|
{ description: "shard.json: the project manifest." }
|
|
@@ -89,6 +89,7 @@ function validateManifest(json) {
|
|
|
89
89
|
return errors;
|
|
90
90
|
}
|
|
91
91
|
const BUILTIN_PLUGINS = {
|
|
92
|
+
effects: "Prepared effect sequences: coordinated properties, animation, particles, audio and markers; add the feature plugins used by tracks",
|
|
92
93
|
render: "GPU device, render graph, views, shaders",
|
|
93
94
|
"render/forward": "cameras, meshes, standard material, lights (includes render and core/transform)",
|
|
94
95
|
sprite: "sprites, atlases, frame animation, tilemaps (includes render/forward)",
|
|
@@ -101,10 +102,15 @@ const BUILTIN_PLUGINS = {
|
|
|
101
102
|
gestures: "taps, long presses, drags, pinches, twists and the wheel as Gesture events, with claims and Escape to cancel (includes input)",
|
|
102
103
|
controls: "OrbitControls and MapControls on cameras, syncViews, PlaneDrag to drag entities along a plane, controls.describe (includes gestures, render/forward)",
|
|
103
104
|
audio: "AudioSource, AudioListener, playSound, buses and ducking, spatial audio, voice limits (includes core/transform)",
|
|
105
|
+
"audio/events": "Authored layered events, parameters, collections and prepared voice lifetimes (includes audio)",
|
|
106
|
+
"audio/mixer": "Hierarchical effects, sends and composable snapshots (includes audio/events)",
|
|
107
|
+
"audio/acoustics": "Listener reverb zones and bounded injected obstruction queries (includes audio/mixer)",
|
|
108
|
+
"audio/music": "Prepared stems, beat/bar/marker transitions and stingers (includes audio/events)",
|
|
104
109
|
ui: "UiNode trees under a UiRoot: flexbox layout, text, images, buttons, sliders, text fields, world-anchored markers, themes (includes core/transform)",
|
|
105
110
|
nav: "navigation: NavGrid A* (2D), NavMesh baked by Recast from NavSource geometry, OffMeshLink, path queries, NavAgent steering (includes core/transform)",
|
|
106
111
|
"nav/grid": "navigation on NavGrids only, without loading the Recast WASM (includes core/transform)",
|
|
107
112
|
terrain: "planets: Planet cube-sphere terrain from noise graphs with LOD, geomorphing, oceans, biomes, collider chunks around characters, navmeshes on the surface; terrain.* methods (includes core/transform; add render/forward to draw it, physics3d for colliders)",
|
|
113
|
+
scatter: "props and foliage from ScatterSet rules (*.scatter.json) on planets (Planet.scatter, Biome.scatter) and ScatterSurface meshes: props as entities around cameras and anchors, GPU foliage near cameras; scatter.* methods; the engine generators shard/Rock, Tree, Bush, GrassClump and Crystal make their meshes (includes core/transform; add terrain for planets, render/forward to draw)",
|
|
108
114
|
dice: "the dice table (0054): dice skins, face layouts and effect recipes as data, dice.play and dice.describe, dice/RollRequest in scenes; tracks record on the calling thread (includes render/forward)",
|
|
109
115
|
"core/transform": "Transform and hierarchy propagation"
|
|
110
116
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aethervtt/shard-project",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./manifest": {
|
|
13
|
+
"types": "./dist/manifest.d.ts",
|
|
14
|
+
"default": "./dist/manifest.js"
|
|
11
15
|
}
|
|
12
16
|
},
|
|
13
17
|
"files": [
|
|
@@ -16,33 +20,35 @@
|
|
|
16
20
|
"README.md"
|
|
17
21
|
],
|
|
18
22
|
"dependencies": {
|
|
19
|
-
"@aethervtt/shard-animation": "0.
|
|
20
|
-
"@aethervtt/shard-assets": "0.
|
|
21
|
-
"@aethervtt/shard-audio": "0.
|
|
22
|
-
"@aethervtt/shard-controls": "0.
|
|
23
|
-
"@aethervtt/shard-core": "0.
|
|
24
|
-
"@aethervtt/shard-dice": "0.
|
|
25
|
-
"@aethervtt/shard-fog": "0.
|
|
26
|
-
"@aethervtt/shard-gltf": "0.
|
|
27
|
-
"@aethervtt/shard-gpu": "0.
|
|
28
|
-
"@aethervtt/shard-input": "0.
|
|
29
|
-
"@aethervtt/shard-nav": "0.
|
|
30
|
-
"@aethervtt/shard-noise": "0.
|
|
31
|
-
"@aethervtt/shard-particles": "0.
|
|
32
|
-
"@aethervtt/shard-physics": "0.
|
|
33
|
-
"@aethervtt/shard-platform": "0.
|
|
34
|
-
"@aethervtt/shard-procgen": "0.
|
|
35
|
-
"@aethervtt/shard-protocol": "0.
|
|
36
|
-
"@aethervtt/shard-render": "0.
|
|
37
|
-
"@aethervtt/shard-runtime": "0.
|
|
38
|
-
"@aethervtt/shard-save": "0.
|
|
39
|
-
"@aethervtt/shard-
|
|
40
|
-
"@aethervtt/shard-
|
|
41
|
-
"@aethervtt/shard-
|
|
42
|
-
"@aethervtt/shard-
|
|
43
|
-
"@aethervtt/shard-
|
|
44
|
-
"@aethervtt/shard-
|
|
45
|
-
"@aethervtt/shard-
|
|
23
|
+
"@aethervtt/shard-animation": "0.2.0",
|
|
24
|
+
"@aethervtt/shard-assets": "0.2.0",
|
|
25
|
+
"@aethervtt/shard-audio": "0.2.0",
|
|
26
|
+
"@aethervtt/shard-controls": "0.2.0",
|
|
27
|
+
"@aethervtt/shard-core": "0.2.0",
|
|
28
|
+
"@aethervtt/shard-dice": "0.2.0",
|
|
29
|
+
"@aethervtt/shard-fog": "0.2.0",
|
|
30
|
+
"@aethervtt/shard-gltf": "0.2.0",
|
|
31
|
+
"@aethervtt/shard-gpu": "0.2.0",
|
|
32
|
+
"@aethervtt/shard-input": "0.2.0",
|
|
33
|
+
"@aethervtt/shard-nav": "0.2.0",
|
|
34
|
+
"@aethervtt/shard-noise": "0.2.0",
|
|
35
|
+
"@aethervtt/shard-particles": "0.2.0",
|
|
36
|
+
"@aethervtt/shard-physics": "0.2.0",
|
|
37
|
+
"@aethervtt/shard-platform": "0.2.0",
|
|
38
|
+
"@aethervtt/shard-procgen": "0.2.0",
|
|
39
|
+
"@aethervtt/shard-protocol": "0.2.0",
|
|
40
|
+
"@aethervtt/shard-render": "0.2.0",
|
|
41
|
+
"@aethervtt/shard-runtime": "0.2.0",
|
|
42
|
+
"@aethervtt/shard-save": "0.2.0",
|
|
43
|
+
"@aethervtt/shard-scatter": "0.2.0",
|
|
44
|
+
"@aethervtt/shard-scene": "0.2.0",
|
|
45
|
+
"@aethervtt/shard-sprite": "0.2.0",
|
|
46
|
+
"@aethervtt/shard-terrain": "0.2.0",
|
|
47
|
+
"@aethervtt/shard-text": "0.2.0",
|
|
48
|
+
"@aethervtt/shard-transform": "0.2.0",
|
|
49
|
+
"@aethervtt/shard-ui": "0.2.0",
|
|
50
|
+
"@aethervtt/shard-verify": "0.2.0",
|
|
51
|
+
"@aethervtt/shard-effects": "0.2.0"
|
|
46
52
|
},
|
|
47
53
|
"repository": {
|
|
48
54
|
"type": "git",
|
|
@@ -54,6 +60,6 @@
|
|
|
54
60
|
"provenance": true
|
|
55
61
|
},
|
|
56
62
|
"shard": {
|
|
57
|
-
"buildSha": "
|
|
63
|
+
"buildSha": "32c22a3944ed23f5d1baa90bf7cc9fae724f5fd8"
|
|
58
64
|
}
|
|
59
65
|
}
|