incanto 0.28.0 → 0.29.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/README.md +44 -0
- package/bin/incanto-editor.mjs +118 -1
- package/dist/2d.d.ts +38 -5
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +319 -15
- package/dist/3d.js +5 -5
- package/dist/{behavior-CPibUfnH.d.ts → behavior-BAc0erXF.d.ts} +21 -0
- package/dist/{create-game-BRgWpNsa.js → create-game-CNKXGfpr.js} +270 -47
- package/dist/{create-game-71TJjW1T.js → create-game-CbuLWorm.js} +53 -14
- package/dist/debug.d.ts +51 -6
- package/dist/debug.js +242 -19
- package/dist/editor-switch-B0wB_DSr.d.ts +59 -0
- package/dist/editor-switch-CAKlJMIY.js +161 -0
- package/dist/editor.d.ts +49 -0
- package/dist/editor.js +8439 -0
- package/dist/{gameplay-DEG-TP7D.js → gameplay-L05WgWd1.js} +207 -37
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +22 -3
- package/dist/index.js +2 -2
- package/dist/{loader-DILt9PGC.d.ts → loader-B242FF6N.d.ts} +1 -1
- package/dist/net.d.ts +1 -1
- package/dist/net.js +2 -2
- package/dist/{pathfinding-RWYkNKx9.d.ts → pathfinding-BwD974Ss.d.ts} +1 -1
- package/dist/{physics-2d-DiVFFlH3.js → physics-2d-CCVTrKOd.js} +62 -2
- package/dist/{physics-3d--y5clE2j.js → physics-3d-BZZLtwJu.js} +439 -8
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-nObreUQR.js → register-BTg0EM7s.js} +35 -3
- package/dist/{register-6R75AC7-.js → register-C44aSduO.js} +5550 -5073
- package/dist/{register-CvpSUU3O.js → register-DWcWq4QG.js} +22 -2
- package/dist/{register-BFFE1Mh1.js → register-MelqEdza.js} +1 -1
- package/dist/test.d.ts +35 -3
- package/dist/test.js +104 -10
- package/dist/vite.d.ts +53 -1
- package/dist/vite.js +94 -2
- package/editor/assets/{agent8-CojUfCXN.js → agent8-DEVkEa3d.js} +1 -1
- package/editor/assets/debug-zGAtpDF0.js +2 -0
- package/editor/assets/index-Bx4UtWYY.js +10586 -0
- package/editor/index.html +3 -157
- package/package.json +3 -2
- package/schemas/scene.schema.json +102 -4
- package/skills/incanto-3d-models.md +38 -0
- package/skills/incanto-assets.md +13 -0
- package/skills/incanto-building-3d-games.md +83 -6
- package/skills/incanto-editor.md +151 -0
- package/skills/incanto-environment.md +65 -1
- package/skills/incanto-node-reference.md +33 -1
- package/skills/incanto-physics-and-input.md +45 -6
- package/skills/incanto-verifying-your-game.md +51 -2
- package/templates-app/beacon-isle-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/editor/assets/index-D6RQgROR.js +0 -8330
- package/editor/assets/index-D8QvwvOm.css +0 -1
package/README.md
CHANGED
|
@@ -53,6 +53,50 @@ engine.start();
|
|
|
53
53
|
- `schemas/scene.schema.json` — generated JSON Schema for scene files (the agent contract)
|
|
54
54
|
- `templates/agent8-server.js` — the multiplayer server kernel for the agent8 platform
|
|
55
55
|
|
|
56
|
+
## Debug mode
|
|
57
|
+
|
|
58
|
+
Off by default, with **no URL toggle** — a deployed build can never be switched on by a player.
|
|
59
|
+
Gate it to your own dev server (every template already ships the line):
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
createGame3D({ ..., debug: import.meta.env.VITE_INCANTO_DEBUG === '1' });
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
VITE_INCANTO_DEBUG=1 bun run dev # overlay ON; plain `bun run dev` and prod builds = off
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A **☰ debug** menu appears top-left: **Explorer** (live scene tree; the selected node's bounds
|
|
70
|
+
light up orange in-game), **Inspector** (its props, editable while running), **Logs**
|
|
71
|
+
(`engine.log` tail with filters), **Stats** (fps · frame ms · nodes · tris · draw calls), and
|
|
72
|
+
**Colliders** (physics shapes as wireframe; one click cycles off → all → **selected**, and
|
|
73
|
+
`selected` draws only the collider of whatever the Explorer has picked — the way to find one shape
|
|
74
|
+
in a world full of them). In code: `physics.debugDraw = true` + `physics.debugScope = node`, or
|
|
75
|
+
`(await import('incanto/debug')).attachDebugOverlay(engine)` to boot the overlay by hand.
|
|
76
|
+
|
|
77
|
+
## The agent8 asset library
|
|
78
|
+
|
|
79
|
+
`bunx incanto-editor --token <v8 access token>` (or `INCANTO_V8_TOKEN`) adds a
|
|
80
|
+
**📚** button to every field that takes a resource — model, texture, sprite
|
|
81
|
+
sheet, sound. Shelves, search, real previews (a GLB is rendered by the engine
|
|
82
|
+
itself), and one pick writes the URL, or the scene `assets{}` entry plus its
|
|
83
|
+
`$ref` for fields that take one. Without a token the panel asks for one and
|
|
84
|
+
keeps it in the browser; the editor server proxies the catalog because it is
|
|
85
|
+
per-account and not CORS-open to localhost.
|
|
86
|
+
|
|
87
|
+
## Play ⇄ edit, in one window
|
|
88
|
+
|
|
89
|
+
The menu's last item, **✎ edit this scene**, turns the running game into the scene editor in the
|
|
90
|
+
page you are already in — same window, no reload, and the edit camera starts where the player's
|
|
91
|
+
eye was. The editor's **▶ play** offers the way back as a choice: **preview here** (simulate in
|
|
92
|
+
the viewport, scripts stripped) or **run as the game** (this page becomes the real game again,
|
|
93
|
+
with your edits). **✕ exit** returns without playing.
|
|
94
|
+
|
|
95
|
+
It follows `debug`, so an overlay build already has it. `editor: false` opts out;
|
|
96
|
+
`editor: { save }` gives the editor a save button that writes your file; `game.openEditor()` is
|
|
97
|
+
the same switch as a function. The editor loads lazily (`incanto/editor`) the first time it is
|
|
98
|
+
asked for — a game that never opens it never downloads it.
|
|
99
|
+
|
|
56
100
|
Docs, agent skills, examples, and architecture: **https://github.com/rareboe/Incanto**
|
|
57
101
|
|
|
58
102
|
© 2026 Verse8. All rights reserved. Proprietary — see [LICENSE](./LICENSE). Bundled third-party OSS notices: [THIRD-PARTY-NOTICES.md](./THIRD-PARTY-NOTICES.md).
|
package/bin/incanto-editor.mjs
CHANGED
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
* POST /api/scenes → { path } creates a minimal scene (project mode)
|
|
14
14
|
* GET /api/scene[?file=] → scene JSON (single mode ignores ?file=)
|
|
15
15
|
* PUT /api/scene[?file=] → validates the scene shape, writes the file
|
|
16
|
+
* GET /api/library?tags=… → proxies the agent8 asset catalog (see below)
|
|
17
|
+
*
|
|
18
|
+
* The library proxy exists because the catalog cannot be reached from a page:
|
|
19
|
+
* it wants a Verse8 access token and its CORS allow-list has no localhost in
|
|
20
|
+
* it. This server has neither problem. The token comes from --token, from
|
|
21
|
+
* INCANTO_V8_TOKEN / V8_ACCESS_TOKEN, or from the page's own
|
|
22
|
+
* `x-incanto-v8-token` header (what the browser stores when you paste one) —
|
|
23
|
+
* it is forwarded upstream and never written to disk.
|
|
16
24
|
*
|
|
17
25
|
* Security: on loopback binds, the Host header must be loopback (defeats DNS
|
|
18
26
|
* rebinding); writes additionally require Origin (when present) to match the
|
|
@@ -60,6 +68,7 @@ function parseArgs(argv) {
|
|
|
60
68
|
for (let i = 0; i < argv.length; i++) {
|
|
61
69
|
const a = argv[i];
|
|
62
70
|
if (a === '--output') args.output = argv[++i];
|
|
71
|
+
else if (a === '--token') args.token = argv[++i];
|
|
63
72
|
else if (a === '--port') args.port = Number(argv[++i]);
|
|
64
73
|
else if (a === '--host') args.host = argv[++i];
|
|
65
74
|
else if (a === '--help' || a === '-h') args.help = true;
|
|
@@ -79,7 +88,11 @@ if (args.help || args.error || Number.isNaN(args.port) || (args.output && !args.
|
|
|
79
88
|
Without a file: PROJECT mode — discovers every *.scene.json under the current
|
|
80
89
|
directory (a picker opens; a single scene auto-opens; new scenes can be created).
|
|
81
90
|
With a file: SINGLE mode — that file only; saving writes to --output (default:
|
|
82
|
-
the input file). --output requires an input file. Use --host 0.0.0.0 inside containers
|
|
91
|
+
the input file). --output requires an input file. Use --host 0.0.0.0 inside containers.
|
|
92
|
+
|
|
93
|
+
--token <v8 access token> (or INCANTO_V8_TOKEN) unlocks the agent8 asset library
|
|
94
|
+
inside the editor. Without it the 📚 button asks for a token and remembers it in
|
|
95
|
+
the browser instead.`);
|
|
83
96
|
process.exit(args.help && !args.error ? 0 : 1);
|
|
84
97
|
}
|
|
85
98
|
|
|
@@ -91,6 +104,102 @@ const VERSION = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf-8')
|
|
|
91
104
|
|
|
92
105
|
const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', 'out', 'coverage']);
|
|
93
106
|
|
|
107
|
+
// --- agent8 asset library -------------------------------------------------
|
|
108
|
+
const LIBRARY_URL =
|
|
109
|
+
process.env.INCANTO_LIBRARY_URL ?? 'https://agent8-backend.verse8.io/api/resources';
|
|
110
|
+
const ENV_TOKEN = args.token ?? process.env.INCANTO_V8_TOKEN ?? process.env.V8_ACCESS_TOKEN ?? '';
|
|
111
|
+
const LIBRARY_TIMEOUT_MS = 15_000;
|
|
112
|
+
|
|
113
|
+
/** Upstream row → the shape the editor consumes (ids are strings here). */
|
|
114
|
+
function libraryItem(row) {
|
|
115
|
+
return {
|
|
116
|
+
id: String(row?.id ?? row?.url ?? ''),
|
|
117
|
+
url: String(row?.url ?? ''),
|
|
118
|
+
description: typeof row?.description === 'string' ? row.description : '',
|
|
119
|
+
metadata: row?.metadata && typeof row.metadata === 'object' ? row.metadata : {},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function proxyLibrary(req, res, url) {
|
|
124
|
+
// the page's token wins over the server's: a user who pasted one is telling
|
|
125
|
+
// us which account to search as
|
|
126
|
+
const token = (req.headers['x-incanto-v8-token'] || '').toString().trim() || ENV_TOKEN;
|
|
127
|
+
if (!token) {
|
|
128
|
+
return send(
|
|
129
|
+
res,
|
|
130
|
+
401,
|
|
131
|
+
JSON.stringify({
|
|
132
|
+
error:
|
|
133
|
+
'no Verse8 access token. Start the editor with --token <t> (or INCANTO_V8_TOKEN), or paste one into the library panel.',
|
|
134
|
+
needsToken: true,
|
|
135
|
+
}),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
const params = new URLSearchParams({
|
|
139
|
+
tags: url.searchParams.get('tags') ?? '',
|
|
140
|
+
page: url.searchParams.get('page') ?? '1',
|
|
141
|
+
limit: url.searchParams.get('limit') ?? '24',
|
|
142
|
+
});
|
|
143
|
+
const keyword = url.searchParams.get('keyword');
|
|
144
|
+
if (keyword) params.set('keyword', keyword);
|
|
145
|
+
|
|
146
|
+
const controller = new AbortController();
|
|
147
|
+
const timer = setTimeout(() => controller.abort(), LIBRARY_TIMEOUT_MS);
|
|
148
|
+
try {
|
|
149
|
+
const upstream = await fetch(`${LIBRARY_URL}?${params}`, {
|
|
150
|
+
headers: { authorization: `Bearer ${token}`, accept: 'application/json' },
|
|
151
|
+
signal: controller.signal,
|
|
152
|
+
});
|
|
153
|
+
const text = await upstream.text();
|
|
154
|
+
if (upstream.status === 401 || upstream.status === 403) {
|
|
155
|
+
return send(
|
|
156
|
+
res,
|
|
157
|
+
401,
|
|
158
|
+
JSON.stringify({
|
|
159
|
+
error: 'the asset library rejected that token (expired or wrong account)',
|
|
160
|
+
needsToken: true,
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (!upstream.ok) {
|
|
165
|
+
return send(res, 502, JSON.stringify({ error: `asset library: HTTP ${upstream.status}` }));
|
|
166
|
+
}
|
|
167
|
+
let body;
|
|
168
|
+
try {
|
|
169
|
+
body = JSON.parse(text);
|
|
170
|
+
} catch {
|
|
171
|
+
return send(res, 502, JSON.stringify({ error: 'asset library returned non-JSON' }));
|
|
172
|
+
}
|
|
173
|
+
if (body?.success === false) {
|
|
174
|
+
return send(res, 502, JSON.stringify({ error: String(body.error ?? 'asset library error') }));
|
|
175
|
+
}
|
|
176
|
+
const rows = Array.isArray(body?.data) ? body.data : [];
|
|
177
|
+
return send(
|
|
178
|
+
res,
|
|
179
|
+
200,
|
|
180
|
+
JSON.stringify({
|
|
181
|
+
items: rows.map(libraryItem).filter((i) => i.url),
|
|
182
|
+
page: Number(body?.pagination?.page ?? params.get('page') ?? 1),
|
|
183
|
+
hasNext: body?.pagination?.hasNext === true,
|
|
184
|
+
total: typeof body?.pagination?.total === 'number' ? body.pagination.total : undefined,
|
|
185
|
+
}),
|
|
186
|
+
);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
const aborted = error?.name === 'AbortError';
|
|
189
|
+
return send(
|
|
190
|
+
res,
|
|
191
|
+
502,
|
|
192
|
+
JSON.stringify({
|
|
193
|
+
error: aborted
|
|
194
|
+
? `asset library timed out after ${LIBRARY_TIMEOUT_MS / 1000}s`
|
|
195
|
+
: `asset library unreachable: ${String(error?.message ?? error)}`,
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
} finally {
|
|
199
|
+
clearTimeout(timer);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
94
203
|
function discoverScenes() {
|
|
95
204
|
const found = [];
|
|
96
205
|
const walk = (dir, depth) => {
|
|
@@ -217,6 +326,14 @@ const server = createServer((req, res) => {
|
|
|
217
326
|
);
|
|
218
327
|
}
|
|
219
328
|
|
|
329
|
+
if (url.pathname === '/api/library') {
|
|
330
|
+
if (req.method !== 'GET') {
|
|
331
|
+
return send(res, 405, JSON.stringify({ error: 'method not allowed' }));
|
|
332
|
+
}
|
|
333
|
+
proxyLibrary(req, res, url);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
220
337
|
if (url.pathname === '/api/scenes') {
|
|
221
338
|
if (MODE !== 'project') {
|
|
222
339
|
return send(res, 400, JSON.stringify({ error: 'scene listing is project-mode only' }));
|
package/dist/2d.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { s as JsonObject } from "./schema-CcoWb32N.js";
|
|
2
|
-
import { $ as Node, C as RendererStats, S as GameStats, b as Scheduler, l as PropSchema, n as BehaviorCtor, v as Engine, w as Scene$1 } from "./behavior-
|
|
3
|
-
import { t as
|
|
1
|
+
import { i as SceneJson, s as JsonObject } from "./schema-CcoWb32N.js";
|
|
2
|
+
import { $ as Node, C as RendererStats, S as GameStats, b as Scheduler, l as PropSchema, n as BehaviorCtor, v as Engine, w as Scene$1 } from "./behavior-BAc0erXF.js";
|
|
3
|
+
import { t as EditorSwitchOptions } from "./editor-switch-B0wB_DSr.js";
|
|
4
|
+
import { t as LoadSceneOptions } from "./loader-B242FF6N.js";
|
|
4
5
|
import { t as ParticleSim } from "./particle-sim-CwJ5rI_P.js";
|
|
5
6
|
import { Group, Mesh, Object3D, Scene, Texture } from "three";
|
|
6
7
|
import * as RapierNs from "@dimforge/rapier2d-compat";
|
|
@@ -160,6 +161,8 @@ declare class Physics2D {
|
|
|
160
161
|
private readonly engine;
|
|
161
162
|
/** Render collider outlines in the GAME view (renderers pick this up). */
|
|
162
163
|
debugDraw: boolean;
|
|
164
|
+
/** Narrow those outlines to one node's subtree (see DebugLineSource). */
|
|
165
|
+
debugScope: object | null;
|
|
163
166
|
readonly dimension = "2d";
|
|
164
167
|
private readonly unregisterDebug;
|
|
165
168
|
private readonly warnedNoCollider;
|
|
@@ -179,6 +182,13 @@ declare class Physics2D {
|
|
|
179
182
|
moveAndSlide(node: CharacterBody2D): void;
|
|
180
183
|
/** Rapier debug segments, scaled back to pixels. Null while debugDraw is off. */
|
|
181
184
|
debugLines(): Float32Array | null;
|
|
185
|
+
/**
|
|
186
|
+
* Only the selected node's colliders (meters — the caller scales to pixels).
|
|
187
|
+
* Rapier draws the whole world or nothing, so a chosen few are outlined from
|
|
188
|
+
* the same shape data the solver holds. 2D colliders are rect/circle/capsule,
|
|
189
|
+
* which is the whole switch.
|
|
190
|
+
*/
|
|
191
|
+
private scopedLines;
|
|
182
192
|
/** Create newly-arrived joints; tear down departed ones. */
|
|
183
193
|
private syncJoints;
|
|
184
194
|
/**
|
|
@@ -249,6 +259,17 @@ interface CreateGame2DOptions {
|
|
|
249
259
|
* to development, e.g. `debug: import.meta.env.VITE_INCANTO_DEBUG === '1'`.
|
|
250
260
|
*/
|
|
251
261
|
debug?: boolean;
|
|
262
|
+
/**
|
|
263
|
+
* LIVE EDITING: the ☰ debug menu gains "edit this scene", which turns this
|
|
264
|
+
* page into the scene editor — same window, no reload — and the editor's play
|
|
265
|
+
* button turns it back into the game.
|
|
266
|
+
*
|
|
267
|
+
* DEFAULTS TO `debug`, costs nothing until clicked (the editor is a lazy
|
|
268
|
+
* chunk), and is never reachable without it — the ☰ menu is its only
|
|
269
|
+
* entrance. `false` opts out; `{ open }` loads the editor yourself (apps that
|
|
270
|
+
* alias incanto to source); `{ save }` makes its save button write your file.
|
|
271
|
+
*/
|
|
272
|
+
editor?: boolean | EditorSwitchOptions;
|
|
252
273
|
/** @internal Test seam — replaces `document` for the debug overlay. */
|
|
253
274
|
_debugDoc?: {
|
|
254
275
|
createElement(tag: string): HTMLElement;
|
|
@@ -272,10 +293,22 @@ interface GameRenderer {
|
|
|
272
293
|
interface Game2D {
|
|
273
294
|
engine: Engine;
|
|
274
295
|
scene: Scene$1;
|
|
296
|
+
/**
|
|
297
|
+
* The AUTHORED scene this game booted from — the JSON, not the live tree.
|
|
298
|
+
* A running tree has moved; the editor gets what was written down.
|
|
299
|
+
*/
|
|
300
|
+
sourceJson: SceneJson;
|
|
275
301
|
renderer: GameRenderer;
|
|
276
302
|
physics: Physics2D | null;
|
|
277
303
|
/** Engine + renderer perf counters in one read (fps/nodes/triangles/…). */
|
|
278
304
|
stats(): GameStats;
|
|
305
|
+
/**
|
|
306
|
+
* Turn this page into the scene editor — what the ☰ debug menu's "edit this
|
|
307
|
+
* scene" calls. The game is disposed on the way out and rebooted when the
|
|
308
|
+
* editor hands it back, so THIS handle is dead afterwards. `null` when the
|
|
309
|
+
* game was booted with `editor: false`.
|
|
310
|
+
*/
|
|
311
|
+
openEditor: (() => Promise<void>) | null;
|
|
279
312
|
/** Tear the whole game down (renderer, loop, scene, listeners). */
|
|
280
313
|
dispose(): void;
|
|
281
314
|
}
|
|
@@ -724,8 +757,8 @@ declare class Renderer2D {
|
|
|
724
757
|
/** The world view used by the LAST render (center/zoom/canvas size). */
|
|
725
758
|
view(): View2D;
|
|
726
759
|
/**
|
|
727
|
-
* GPU counters for the LAST rendered frame
|
|
728
|
-
*
|
|
760
|
+
* GPU counters for the LAST rendered frame — world + UI passes combined (see
|
|
761
|
+
* the `info.reset()` in the render pass), plus GPU memory.
|
|
729
762
|
*/
|
|
730
763
|
stats(): RendererStats;
|
|
731
764
|
worldFromScreen(sx: number, sy: number): {
|
package/dist/2d.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
2
|
-
import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-
|
|
3
|
-
import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-
|
|
4
|
-
import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-
|
|
2
|
+
import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-CbuLWorm.js";
|
|
3
|
+
import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-DWcWq4QG.js";
|
|
4
|
+
import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-CCVTrKOd.js";
|
|
5
5
|
//#region src/2d/library-sprite.ts
|
|
6
6
|
/**
|
|
7
7
|
* Turn a library sprite-animation JSON into a scene-ready spritesheet asset
|