incanto 0.29.0 → 0.31.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.
Files changed (55) hide show
  1. package/bin/incanto-editor.mjs +101 -28
  2. package/dist/2d.d.ts +28 -6
  3. package/dist/2d.js +3 -3
  4. package/dist/3d.d.ts +81 -7
  5. package/dist/3d.js +4 -4
  6. package/dist/{audit-C6rMyict.js → audit-C4kmDK0o.js} +16 -0
  7. package/dist/{behavior-BAc0erXF.d.ts → behavior-D_jMpFh8.d.ts} +117 -55
  8. package/dist/{create-game-CNKXGfpr.js → create-game-DqOjMBUS.js} +61 -48
  9. package/dist/{create-game-CbuLWorm.js → create-game-z5XaB1p5.js} +27 -15
  10. package/dist/debug-draw-BM3DsvtT.js +18 -0
  11. package/dist/debug.d.ts +1 -1
  12. package/dist/debug.js +8 -3
  13. package/dist/{duplicate-BgtFrFo4.js → duplicate-KRPtUtzl.js} +1 -1
  14. package/dist/{editor-switch-B0wB_DSr.d.ts → editor-switch-BJb-CWfA.d.ts} +12 -1
  15. package/dist/editor.js +1346 -1231
  16. package/dist/{register-C44aSduO.js → environment-presets--DigHNg4.js} +42 -11
  17. package/dist/{gameplay-L05WgWd1.js → gameplay-BftxM_It.js} +14 -5
  18. package/dist/gameplay.d.ts +1 -1
  19. package/dist/gameplay.js +1 -1
  20. package/dist/index.d.ts +12 -3
  21. package/dist/index.js +6 -6
  22. package/dist/{loader-B242FF6N.d.ts → loader-BYBrqTxP.d.ts} +1 -1
  23. package/dist/{loader-BZqOKfI2.js → loader-BlaRQGaA.js} +482 -6
  24. package/dist/net.d.ts +1 -1
  25. package/dist/net.js +3 -3
  26. package/dist/{pathfinding-BwD974Ss.d.ts → pathfinding-BwhqPD3i.d.ts} +1 -1
  27. package/dist/{physics-2d-CCVTrKOd.js → physics-2d-D9wquBvK.js} +30 -4
  28. package/dist/{physics-3d-BZZLtwJu.js → physics-3d-CnPygVGo.js} +41 -5
  29. package/dist/react.d.ts +1 -1
  30. package/dist/react.js +1 -1
  31. package/dist/{register-MelqEdza.js → register-CUY284Is.js} +3 -3
  32. package/dist/{register-BTg0EM7s.js → register-Dzkd6-os.js} +56 -380
  33. package/dist/{register-DWcWq4QG.js → register-tkR_8tWg.js} +4 -4
  34. package/dist/{registry-BVJ2HbCn.js → registry-CJdGpT2V.js} +6 -2
  35. package/dist/{editor-switch-CAKlJMIY.js → teardown-ByzfDPyu.js} +83 -4
  36. package/dist/test.d.ts +2 -7
  37. package/dist/test.js +43 -14
  38. package/dist/vite.d.ts +87 -1
  39. package/dist/vite.js +262 -3
  40. package/editor/assets/{agent8-DEVkEa3d.js → agent8-o27_Y1xN.js} +1 -1
  41. package/editor/assets/{debug-zGAtpDF0.js → debug-DfcWX3uW.js} +3 -2
  42. package/editor/assets/index-C9fb5QcT.js +10696 -0
  43. package/editor/index.html +1 -1
  44. package/package.json +1 -1
  45. package/skills/incanto-editor.md +61 -8
  46. package/skills/incanto-physics-and-input.md +15 -0
  47. package/skills/incanto-verifying-your-game.md +68 -0
  48. package/templates-app/beacon-isle-3d/package.json +1 -1
  49. package/templates-app/beacon-isle-3d/vite.config.ts +7 -0
  50. package/templates-app/tps-3d/package.json +1 -1
  51. package/templates-app/tps-3d/vite.config.ts +7 -0
  52. package/templates-app/village-quest-3d/package.json +1 -1
  53. package/templates-app/village-quest-3d/vite.config.ts +7 -0
  54. package/dist/debug-draw-CZmOYjL2.js +0 -13
  55. package/editor/assets/index-Bx4UtWYY.js +0 -10586
package/editor/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Incanto Scene Editor</title>
7
7
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='3' fill='%236ee7dc'/><text x='8' y='12' text-anchor='middle' font-size='11' font-family='monospace' fill='%230e1018'>i</text></svg>" />
8
- <script type="module" crossorigin src="./assets/index-Bx4UtWYY.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-C9fb5QcT.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="./assets/GameServer-C56iOUgF.js">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incanto",
3
- "version": "0.29.0",
3
+ "version": "0.31.0",
4
4
  "description": "Vibe-coding-first web game engine SDK — JSON-driven scenes on three.js",
5
5
  "keywords": [
6
6
  "game-engine",
@@ -18,9 +18,50 @@ bunx incanto-editor level.scene.json --output build/level.scene.json --port 5179
18
18
 
19
19
  **Project mode** (no file argument — the usual way) scans the current directory for
20
20
  `*.scene.json` (skipping node_modules/dist/hidden dirs): one scene auto-opens; several
21
- show a picker; the picker can also CREATE a new scene (`levels/arena.scene.json`
22
- minimal 2D scene, parent dirs made). A `scenes` button switches files anytime. The page
23
- can only ever address `*.scene.json` files under the launch directory.
21
+ open the scene browser. The page can only ever address `*.scene.json` files under the
22
+ launch directory.
23
+
24
+ ### The scene browser (`scenes`)
25
+
26
+ The `scenes` button opens the project as a tree, not a list of paths:
27
+
28
+ - **Every `.json` in the project is listed** — a scene is JSON by definition, so
29
+ nothing else could be one — and each row says whether it IS a scene, read from
30
+ the file rather than guessed from its name. A scene declares itself:
31
+ `{"format": 1, "type": "scene", "name", "root"}`, the same four keys the loader
32
+ demands. Scene rows show `3d · 24 nodes`; anything else is dimmed and marked
33
+ `not a scene`, and opening it fails with the reason (`"type" must be "scene",
34
+ and this file has no "type"`). So a scene you saved as `level.json` is right
35
+ there and opens; `package.json` is right there and tells you why it will not.
36
+ (Non-scene contents never leave the server — only the reason does.)
37
+ - **Folders**, sorted, with a scene count each; a chain that holds nothing else
38
+ collapses into one row (`src / scenes`). Small projects open fully expanded.
39
+ - **The scene you are editing** is marked and revealed, and the cursor starts on it.
40
+ - **A filter box** — type any part of a path; matches show wherever they are hiding.
41
+ Keyboard from that box: `↑↓` move · `→` open a folder · `←` close it · `⏎` load ·
42
+ `Esc` close (backdrop and ✕ work too).
43
+ - Each row carries **when it was last written and how big it is**, which is usually
44
+ how you recognise the file you were just in.
45
+ - **create** makes a new scene at the path in the box — left empty it uses the
46
+ placeholder, which tracks the folder you are standing in, so a scene lands beside
47
+ its siblings rather than at the project root. Parent dirs are created.
48
+ - Loading another scene while you have unsaved EDITS asks first.
49
+
50
+ **The same browser opens inside a running game.** When the game's dev server serves
51
+ the project's scenes, `☰ debug ▸ ✎ edit this scene` gives you the whole project: the
52
+ editor opens on the scene the game is running, and `scenes` switches to any other one,
53
+ saves back to ITS file, and `play ▸ run as the game` boots whichever scene you loaded.
54
+ That needs one plugin in `vite.config.ts` (already there in every `incanto-new`
55
+ project):
56
+
57
+ ```ts
58
+ import { incantoScenes } from 'incanto/vite';
59
+ export default defineConfig({ plugins: [incantoScenes()] });
60
+ ```
61
+
62
+ Without it the `scenes` button is still there and says what to add — a missing
63
+ one-line plugin should be a sentence you can act on, not a button that quietly
64
+ is not there. The editor still edits the scene the game booted with meanwhile.
24
65
 
25
66
  Opens a local page (default `http://127.0.0.1:5179/`) with three panes:
26
67
 
@@ -45,9 +86,16 @@ Opens a local page (default `http://127.0.0.1:5179/`) with three panes:
45
86
  (dimension, gravity with real inputs, environment/input/assets/multiplayer).
46
87
  - **Viewport with DIRECT MANIPULATION** — a mode toolbar (top left) + the
47
88
  Unity/Godot keys: **W move · E rotate · R scale** (active mode highlighted).
48
- A collider-visibility toggle sits beside the mode tools: light-green
49
- wireframes for every body (2D shapes AND 3D box/sphere/capsule), persisted,
50
- and carried into play mode as the engine's physics debugDraw.
89
+ A collider-visibility toggle sits beside the mode tools, and it shows the
90
+ colliders the ENGINE builds, not a drawing of the `collider` prop: it attaches
91
+ Rapier to the edit view with `simulate: false` and turns on the same
92
+ `debugDraw` the play view uses. So a mesh-fitted body, an InstancedMesh3D
93
+ scatter's hulls, a terrain and a character capsule all appear — none of them
94
+ are in the scene JSON — and rotation, scale and offset are wherever physics
95
+ actually puts them. Nothing is simulated: bodies are posed from the tree each
96
+ tick (an outline follows a gizmo drag) and the solver never runs, so the scene
97
+ does not fall over while you edit it. Persisted, and carried into play mode
98
+ unchanged.
51
99
  Move: axis arrows (X red, Y green, Z blue in 3D) + center square for free
52
100
  movement. Rotate: drag the ring(s) — one ring in 2D, three world-axis rings
53
101
  in 3D — with a live degree readout at the cursor. Scale: the same arrows with
@@ -56,8 +104,13 @@ and carried into play mode as the engine's physics debugDraw.
56
104
  SNAP** — rotations to 15°, positions to the grid (10 px in 2D, 0.5 u in 3D),
57
105
  scales to 0.25 steps (the readout shows the snapped value).
58
106
  2D additionally supports click-pick, body drag, wheel zoom-at-cursor,
59
- right/middle/Shift-drag pan, Alt+wheel scale, and collider wireframes;
60
- `F` frames the scene in both dimensions. The add-node dropdown is grouped by
107
+ right/middle/Shift-drag pan, Alt+wheel scale, and collider wireframes.
108
+ **`F` frames the SELECTED node** (its whole subtree; a light or empty node has
109
+ no bounds, so the camera goes to it at a readable distance) — and the whole
110
+ scene when nothing is selected, which is the Maya/Unity/Unreal meaning of the
111
+ key. Same as the ⌖ frame button, and as `frame in view` in the tree's
112
+ right-click menu. This is how you find a node you selected in the tree that is
113
+ nowhere near the camera; double-click in the tree stays RENAME. The add-node dropdown is grouped by
61
114
  category (Core / 2D / 2D Physics / 3D / 3D Physics / Network, plus a trailing
62
115
  Other catch-all so an unclaimed type can never vanish from the list). The
63
116
  dropdown, inspector and in-editor docs are registry/schema-driven — newly
@@ -243,6 +243,21 @@ it, a map with terrain is a wall of grid lines and the shape you came to check i
243
243
  somewhere inside it. The debug overlay's **Colliders** menu item cycles
244
244
  off → all → selected over exactly these two fields.
245
245
 
246
+ ### Colliders without a simulation
247
+
248
+ `enablePhysics3D(engine, { simulate: false })` (and the 2D twin) builds the world
249
+ and never advances it: the body set is kept in step with the tree, every body is
250
+ posed FROM the tree each tick and propagated to its colliders, and then it stops
251
+ — no solver, no collision events, no write-back into node props. `debugDraw`
252
+ works exactly as above.
253
+
254
+ This is how the scene editor shows colliders while you edit: the real shapes,
255
+ including the ones that are not in the scene JSON at all (a mesh-fitted body, an
256
+ InstancedMesh3D scatter's hulls, a terrain, a character capsule), sitting where
257
+ the tree says, without your scene falling over while you look at it. Use it for
258
+ any "show me the collision geometry" tool. Do NOT use it to freeze a running
259
+ game — that is `engine.timeScale = 0`, which keeps the world coherent.
260
+
246
261
  ## Placement rules
247
262
 
248
263
  `CharacterController2D` MUST be a direct child of a `CharacterBody2D` — the
@@ -155,6 +155,74 @@ export default { plugins: [incantoScenes()] };
155
155
  Invalid `*.scene.json` saves keep the last good scene running and surface the
156
156
  IncantoError in the terminal AND as a browser overlay.
157
157
 
158
+ The same plugin serves this project's scenes to the editor, so the one you switch
159
+ into from the running game (`☰ debug ▸ ✎ edit this scene`) can open, create and save
160
+ ANY scene in the project — not just the one the game booted with. `incantoScenes({
161
+ root })` bounds that to a directory other than vite's; nothing outside it is readable
162
+ or writable, and none of it exists in a build (`configureServer`).
163
+
164
+ ## 5b. What a green `incanto-check` now covers
165
+
166
+ `validateScene` / `incanto-check` hard-fail on all of these, so they never reach
167
+ a browser:
168
+
169
+ - **Every prop's type AND its enum values** — `"mesh": "crystal"` names what is
170
+ valid instead of printing `ok` and throwing at boot. All 31 enum props.
171
+ - **`input{}`** — a declaration's shape (`"keys": "Space"` instead of
172
+ `["Space"]`, an unknown `type`, a wrong `touch`). This used to be checked only
173
+ by `Engine.setScene`, i.e. never headlessly.
174
+ - **`assets{}`** — every entry an object with a non-empty `url`.
175
+ - **`environment.preset`** — resolved only inside Renderer3D before, so a typo
176
+ survived every check and died at boot.
177
+
178
+ And `auditScene` (the warnings `incanto-check` prints) gained the loudest
179
+ "why is my screen black" of all: **nothing lights this 3D scene** — no light
180
+ node, no `environment` sky/hdri/preset, no ambient intensity. Ambient defaults to
181
+ 0, so such a scene renders nothing, throws nothing and logs nothing.
182
+
183
+ ## 6c. Engine diagnostics you can actually read
184
+
185
+ Every internal warning the engine produces — a GLB that 404'd, a body with no
186
+ collider, a retarget that dropped bones, an exception swallowed inside a physics
187
+ trigger handler — used to go to `console.*` and nowhere else, which is the one
188
+ channel a headless tool cannot read. The symptom was always the same: the model
189
+ simply never appears, and `logs` comes back empty.
190
+
191
+ They now go to **`engine.log` as well as the console**, so:
192
+
193
+ - `runScript().logs` contains them, and `describe()` prints them under the
194
+ failures (`! warn: …`).
195
+ - The debug overlay's **Logs** panel shows them live.
196
+ - `game.assetErrors()` lists every asset that failed **with its reason and ref**
197
+ — the direct answer to "why is my model not there". The store itself is
198
+ reachable as `renderer.assets` in both dimensions.
199
+
200
+ So the after-every-edit check is: `ok`, then **`stats().errors`**, then
201
+ **`assetErrors()`**.
202
+
203
+ ## 6b. A thrown error no longer stops the game
204
+
205
+ One exception used to end everything: `requestAnimationFrame` reschedules AFTER
206
+ your callback returns, so a throw in any behavior's `update` killed the loop
207
+ permanently — no message, `stats().running` still `true`, a game that just
208
+ stopped. It no longer can.
209
+
210
+ - The thing that threw is **reported once** (which node, which script, which
211
+ phase) to `engine.log` AND the console, then **skipped**. Its children and
212
+ every other node keep running.
213
+ - **Your SCRIPT and the engine's NODE are skipped separately.** A typo in a
214
+ behavior attached to a `CharacterController3D` stops that behavior — the
215
+ character still walks and the camera still turns. Only a throw from the node's
216
+ own update quarantines the node.
217
+ - `engine.stats().errors` counts everything swallowed to stay alive. **A game
218
+ that looks fine with `errors: 3` is a broken game** — read `engine.log`.
219
+ - `engine.erroredNodes()` lists them; `engine.resumeErroredNodes()` puts them
220
+ back after you fix the code.
221
+ - `runScript()` FAILS the run when the count is non-zero. "It finished" must not
222
+ mean "nothing broke" — that is right for a player and wrong for a harness.
223
+
224
+ So the check after every edit is still `ok`, plus: **read `stats().errors`**.
225
+
158
226
  ## 7. See inside the RUNNING game (debug overlay)
159
227
 
160
228
  ```ts
@@ -14,7 +14,7 @@
14
14
  "@dimforge/rapier2d-compat": "0.19.3",
15
15
  "@dimforge/rapier3d-compat": "0.19.3",
16
16
  "@pixiv/three-vrm": "^3.5.3",
17
- "incanto": "^0.29.0",
17
+ "incanto": "^0.31.0",
18
18
  "three": "^0.184.0"
19
19
  },
20
20
  "devDependencies": {
@@ -1,5 +1,12 @@
1
+ import { incantoLibrary, incantoScenes } from 'incanto/vite';
1
2
  import { defineConfig } from 'vite';
2
3
 
3
4
  export default defineConfig({
4
5
  base: './',
6
+ // Dev-server only, and both are why the editor is useful here:
7
+ // - incantoScenes(): validates every *.scene.json the moment you save it,
8
+ // AND serves this project's scenes, so the editor (☰ debug ▸ edit this
9
+ // scene ▸ scenes) can open, create and save any scene in the project.
10
+ // - incantoLibrary(): the agent8 asset catalog behind the 📚 buttons.
11
+ plugins: [incantoScenes(), incantoLibrary()],
5
12
  });
@@ -13,7 +13,7 @@
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
14
  "@dimforge/rapier3d-compat": "0.19.3",
15
15
  "@pixiv/three-vrm": "^3.5.3",
16
- "incanto": "^0.29.0",
16
+ "incanto": "^0.31.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {
@@ -1,5 +1,12 @@
1
+ import { incantoLibrary, incantoScenes } from 'incanto/vite';
1
2
  import { defineConfig } from 'vite';
2
3
 
3
4
  export default defineConfig({
4
5
  base: './',
6
+ // Dev-server only, and both are why the editor is useful here:
7
+ // - incantoScenes(): validates every *.scene.json the moment you save it,
8
+ // AND serves this project's scenes, so the editor (☰ debug ▸ edit this
9
+ // scene ▸ scenes) can open, create and save any scene in the project.
10
+ // - incantoLibrary(): the agent8 asset catalog behind the 📚 buttons.
11
+ plugins: [incantoScenes(), incantoLibrary()],
5
12
  });
@@ -13,7 +13,7 @@
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
14
  "@dimforge/rapier3d-compat": "0.19.3",
15
15
  "@pixiv/three-vrm": "^3.5.3",
16
- "incanto": "^0.29.0",
16
+ "incanto": "^0.31.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {
@@ -1,5 +1,12 @@
1
+ import { incantoLibrary, incantoScenes } from 'incanto/vite';
1
2
  import { defineConfig } from 'vite';
2
3
 
3
4
  export default defineConfig({
4
5
  base: './',
6
+ // Dev-server only, and both are why the editor is useful here:
7
+ // - incantoScenes(): validates every *.scene.json the moment you save it,
8
+ // AND serves this project's scenes, so the editor (☰ debug ▸ edit this
9
+ // scene ▸ scenes) can open, create and save any scene in the project.
10
+ // - incantoLibrary(): the agent8 asset catalog behind the 📚 buttons.
11
+ plugins: [incantoScenes(), incantoLibrary()],
5
12
  });
@@ -1,13 +0,0 @@
1
- //#region src/core/debug-draw.ts
2
- const sources = /* @__PURE__ */ new Set();
3
- /** @internal physics runtimes self-register on enable. */
4
- function registerDebugSource(source) {
5
- sources.add(source);
6
- return () => sources.delete(source);
7
- }
8
- /** @internal renderers pull their dimension's sources. */
9
- function debugSources(dimension) {
10
- return [...sources].filter((s) => s.dimension === dimension);
11
- }
12
- //#endregion
13
- export { registerDebugSource as n, debugSources as t };