incanto 0.27.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 +496 -150
- package/dist/3d.js +5 -5
- package/dist/{behavior-CPibUfnH.d.ts → behavior-BAc0erXF.d.ts} +21 -0
- package/dist/{create-game-DUAtTqID.js → create-game-CNKXGfpr.js} +270 -47
- package/dist/{create-game-C0bPoURs.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-Ddk13pQ6.js → gameplay-L05WgWd1.js} +220 -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-4mGzOZ2J.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-DcHXS1MA.js → register-C44aSduO.js} +5694 -5108
- 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-DElIPozC.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 +110 -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 +88 -1
- package/skills/incanto-node-reference.md +35 -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-BrneVqN0.js +0 -8330
- package/editor/assets/index-D8QvwvOm.css +0 -1
|
@@ -30,11 +30,43 @@ independent; enable the one matching your scene.
|
|
|
30
30
|
|
|
31
31
|
2D collider shapes: `{"shape":"rect","size":[w,h]}` · `{"shape":"circle","radius":r}` ·
|
|
32
32
|
`{"shape":"capsule","radius":r,"height":h}`.
|
|
33
|
-
3D: `{"shape":"box","size":[x,y,z]}` · `{"shape":"sphere","radius":r}` ·
|
|
34
|
-
`{"shape":"trimesh","vertices":[...],"indices":[...]}` ·
|
|
35
|
-
reads the grid from a `Terrain3D` child — see the
|
|
33
|
+
3D: `{"shape":"auto"}` · `{"shape":"box","size":[x,y,z]}` · `{"shape":"sphere","radius":r}` ·
|
|
34
|
+
`{"shape":"capsule",...}` · `{"shape":"trimesh","vertices":[...],"indices":[...]}` ·
|
|
35
|
+
`{"shape":"heightfield"}` (STATIC-only; reads the grid from a `Terrain3D` child — see the
|
|
36
|
+
incanto-environment skill).
|
|
36
37
|
Malformed colliders are hard `BAD_FORMAT` errors at enable time.
|
|
37
38
|
|
|
39
|
+
**`{"shape":"auto"}` is the one to reach for on props.** It shapes the collider like the
|
|
40
|
+
`MeshInstance3D` the body CARRIES — box→cuboid, sphere→ball, cylinder/capsule→their own
|
|
41
|
+
shapes, `gem`→a convex hull of its real vertices — so a blocker can never disagree with what
|
|
42
|
+
the player sees. Hand-typed extents drift: a box "around" a bridge ends up wider than the
|
|
43
|
+
bridge and you stop in mid-air; a sphere "for" a stone ends up inside it and you clip through
|
|
44
|
+
the corners. Give the body its mesh as a CHILD and let the shape follow:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{ "name": "DeckBody", "type": "StaticBody3D",
|
|
48
|
+
"props": { "position": [0, 3.2, -8], "rotation": [0, 34, 0], "collider": { "shape": "auto" } },
|
|
49
|
+
"children": [ { "name": "Deck", "type": "MeshInstance3D",
|
|
50
|
+
"props": { "mesh": "box", "size": [14, 0.22, 2.2] } } ] }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Bodies ignore an ANCESTOR's rotation, so a yawed structure puts each body at top level with
|
|
54
|
+
its own `rotation` — the child mesh then turns with it, visual and collider together.
|
|
55
|
+
|
|
56
|
+
**`auto` works on a `ModelInstance3D` (GLB/glTF/VRM) too** — the shape comes from the model's
|
|
57
|
+
real triangles, baked through their own transforms, so an offset or `targetHeight`-fitted model
|
|
58
|
+
collides where it is DRAWN:
|
|
59
|
+
|
|
60
|
+
- on a **static** body (`StaticBody3D`, `Area3D`) → a triangle mesh: the model keeps its holes,
|
|
61
|
+
so you walk THROUGH an archway instead of into it. Level art can be dense; a collider over
|
|
62
|
+
~20k triangles logs a note suggesting a low-poly collision model.
|
|
63
|
+
- on a **moving** body (`RigidBody3D`, `CharacterBody3D`) → the convex hull. Rapier's triangle
|
|
64
|
+
meshes are hollow shells; a dynamic one falls through the world.
|
|
65
|
+
|
|
66
|
+
The body simply does not exist until the model has downloaded — nothing is invented in the
|
|
67
|
+
meantime — and it appears the step the asset lands. A body is ONE shape, so `auto` fits one
|
|
68
|
+
mesh or one model and warns when the body carries more.
|
|
69
|
+
|
|
38
70
|
- **`StaticBody2D/3D`** — immovable (ground, walls). Props: `collider`.
|
|
39
71
|
- **`RigidBody2D/3D`** — simulated. Props: `collider`, `mass 1`, `gravityScale 1`,
|
|
40
72
|
`fixedRotation false`, `friction 0.5`, `restitution 0`, `linearVelocity` (write to launch,
|
|
@@ -200,9 +232,16 @@ change scan was removed for performance).
|
|
|
200
232
|
## Debug drawing
|
|
201
233
|
|
|
202
234
|
`physics.debugDraw = true` (the instance `enablePhysics2D/3D` returns) renders
|
|
203
|
-
every collider as light-green wireframe lines IN THE GAME VIEW —
|
|
204
|
-
|
|
205
|
-
|
|
235
|
+
every collider as light-green wireframe lines IN THE GAME VIEW — the shape data
|
|
236
|
+
the solver holds, so what you see is what it collides with. Default OFF; toggle
|
|
237
|
+
it live whenever physics feels wrong.
|
|
238
|
+
|
|
239
|
+
`physics.debugScope = node` narrows those lines to ONE node: its own colliders,
|
|
240
|
+
its descendants', or the nearest body above it when it owns none (so a visual
|
|
241
|
+
mesh child shows the body it belongs to). Null again for the whole world. Without
|
|
242
|
+
it, a map with terrain is a wall of grid lines and the shape you came to check is
|
|
243
|
+
somewhere inside it. The debug overlay's **Colliders** menu item cycles
|
|
244
|
+
off → all → selected over exactly these two fields.
|
|
206
245
|
|
|
207
246
|
## Placement rules
|
|
208
247
|
|
|
@@ -168,12 +168,61 @@ VITE_INCANTO_DEBUG=1 bun run dev # overlay on; plain `bun run dev` / prod buil
|
|
|
168
168
|
A ☰ debug menu (top-left) toggles three draggable/resizable panels over the
|
|
169
169
|
live game: **Explorer** (the scene tree — click any node), **Inspector** (the
|
|
170
170
|
selected node's current state; numbers/strings/booleans/vectors editable
|
|
171
|
-
in-place
|
|
171
|
+
in-place, and an OBJECT prop like `collider` opens into a row per key with the
|
|
172
|
+
`shape` as a dropdown — picking a shape writes a complete valid collider, never
|
|
173
|
+
a box's `size` under a sphere's tag; bulky values like a 54-row `transforms`
|
|
174
|
+
fold behind `▸ Array(54)` and expand on click, one row per element), **Logs** (an `engine.log` tail with level filters and an optional
|
|
172
175
|
browser-console capture toggle) — plus **Stats**, an always-on-top chip pinned
|
|
173
176
|
top-right (`60 fps · 12.1 ms` / `nodes 34 · tris 18.2k · calls 21`, refreshed
|
|
174
|
-
~2×/s; the same numbers any code can read via `game.stats()`)
|
|
177
|
+
~2×/s; the same numbers any code can read via `game.stats()`) — and
|
|
178
|
+
**Colliders**, a switch that draws every physics shape as light-green wireframe
|
|
179
|
+
in the game view. Manual boots:
|
|
175
180
|
`(await import('incanto/debug')).attachDebugOverlay(engine)`.
|
|
176
181
|
|
|
182
|
+
The Inspector HOLDS STILL while you use it: it stops refreshing whenever an
|
|
183
|
+
input has focus or the pointer is inside the panel, and it keeps your scroll
|
|
184
|
+
position across refreshes. Live values resume the moment you move away.
|
|
185
|
+
|
|
186
|
+
**Colliders is the answer to "why did I stop in mid-air / fall through the
|
|
187
|
+
floor".** What you see is the shape data the solver holds — put it next to the
|
|
188
|
+
art and a collider that disagrees with its mesh is obvious in one glance.
|
|
189
|
+
|
|
190
|
+
One click cycles **off → all → selected** (the menu stays open while you cycle —
|
|
191
|
+
the item's label shows the state you landed on):
|
|
192
|
+
|
|
193
|
+
- `all` is every collider in the world. On a real map that means a terrain
|
|
194
|
+
heightfield's grid over everything, which is why the third state exists.
|
|
195
|
+
- `selected` draws only what the Explorer has picked: that node's colliders, its
|
|
196
|
+
descendants', or — if it owns none — the nearest BODY above it, so clicking a
|
|
197
|
+
visual `MeshInstance3D` shows the collider it hangs under rather than nothing.
|
|
198
|
+
A scatter field gives you all of its rows.
|
|
199
|
+
|
|
200
|
+
From code: `physics.debugDraw = true` and `physics.debugScope = node` (null for
|
|
201
|
+
everything).
|
|
202
|
+
|
|
203
|
+
**✎ edit this scene** is the last item in that menu, and it is the fastest loop
|
|
204
|
+
in the engine: the page you are playing in BECOMES the scene editor, from the
|
|
205
|
+
camera you were standing at — no reload, no second tab. Drag a boulder out of
|
|
206
|
+
the path, then **▶ play ▸ ⤢ run as the game** and the page is the game again,
|
|
207
|
+
running your edit. `game.openEditor()` is the same switch from code.
|
|
208
|
+
|
|
209
|
+
It follows `debug` (so a build with the overlay has it) and loads lazily, so a
|
|
210
|
+
game that never opens it never downloads it. `editor: false` opts out;
|
|
211
|
+
`editor: { save }` adds the editor's save button, wired to your own writer:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
createGame3D({
|
|
215
|
+
...,
|
|
216
|
+
debug: import.meta.env.VITE_INCANTO_DEBUG === '1',
|
|
217
|
+
editor: { save: (json) => fetch('/api/scene', { method: 'PUT', body: JSON.stringify(json) }) },
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
What crosses the switch is the AUTHORED scene JSON, not the live tree — physics
|
|
222
|
+
has moved the live one, and "edit this scene" must not mean "edit wherever the
|
|
223
|
+
simulation happened to be". Unsaved edits survive the round trip both ways; a
|
|
224
|
+
page reload is what drops them.
|
|
225
|
+
|
|
177
226
|
**Security — `debug` defaults to `false` and there is NO URL/query toggle**, so
|
|
178
227
|
a DEPLOYED build can never be switched on by an end user (no more `?incanto_debug=1`).
|
|
179
228
|
Enable it only from your own dev environment via the env-var gate above. The
|
|
@@ -35,4 +35,9 @@ YOU MUST follow these rules EXACTLY when vibe-coding on this Incanto template:
|
|
|
35
35
|
11. ✅ VERIFY LIKE A USER: `bun run check` validates the scene, `bun run verify`
|
|
36
36
|
proves the loop headlessly, then `bun run dev` and actually PLAY what you
|
|
37
37
|
changed (move, shoot, clear a wave) before declaring it done.
|
|
38
|
+
12. 🔍 X-RAY WHEN IT LOOKS WRONG: `VITE_INCANTO_DEBUG=1 bun run dev` adds a ☰
|
|
39
|
+
debug menu — Explorer/Inspector (click a node, see and edit its live props),
|
|
40
|
+
Logs, Stats (fps · tris · draw calls) and Colliders (every physics shape as
|
|
41
|
+
wireframe). Blocked in mid-air or falling through the floor is a Colliders
|
|
42
|
+
question; a mystery frame-rate is a Stats question. Never ship the flag on.
|
|
38
43
|
</userRequest>
|
|
@@ -35,4 +35,9 @@ YOU MUST follow these rules EXACTLY when vibe-coding on this Incanto template:
|
|
|
35
35
|
11. ✅ VERIFY LIKE A USER: `bun run check` validates the scene, `bun run verify`
|
|
36
36
|
proves the loop headlessly, then `bun run dev` and actually PLAY what you
|
|
37
37
|
changed (move, shoot, clear a wave) before declaring it done.
|
|
38
|
+
12. 🔍 X-RAY WHEN IT LOOKS WRONG: `VITE_INCANTO_DEBUG=1 bun run dev` adds a ☰
|
|
39
|
+
debug menu — Explorer/Inspector (click a node, see and edit its live props),
|
|
40
|
+
Logs, Stats (fps · tris · draw calls) and Colliders (every physics shape as
|
|
41
|
+
wireframe). Blocked in mid-air or falling through the floor is a Colliders
|
|
42
|
+
question; a mystery frame-rate is a Stats question. Never ship the flag on.
|
|
38
43
|
</userRequest>
|
|
@@ -35,4 +35,9 @@ YOU MUST follow these rules EXACTLY when vibe-coding on this Incanto template:
|
|
|
35
35
|
11. ✅ VERIFY LIKE A USER: `bun run check` validates the scene, `bun run verify`
|
|
36
36
|
proves the loop headlessly, then `bun run dev` and actually PLAY what you
|
|
37
37
|
changed (move, shoot, clear a wave) before declaring it done.
|
|
38
|
+
12. 🔍 X-RAY WHEN IT LOOKS WRONG: `VITE_INCANTO_DEBUG=1 bun run dev` adds a ☰
|
|
39
|
+
debug menu — Explorer/Inspector (click a node, see and edit its live props),
|
|
40
|
+
Logs, Stats (fps · tris · draw calls) and Colliders (every physics shape as
|
|
41
|
+
wireframe). Blocked in mid-air or falling through the floor is a Colliders
|
|
42
|
+
question; a mystery frame-rate is a Stats question. Never ship the flag on.
|
|
38
43
|
</userRequest>
|