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.
Files changed (58) hide show
  1. package/README.md +44 -0
  2. package/bin/incanto-editor.mjs +118 -1
  3. package/dist/2d.d.ts +38 -5
  4. package/dist/2d.js +3 -3
  5. package/dist/3d.d.ts +496 -150
  6. package/dist/3d.js +5 -5
  7. package/dist/{behavior-CPibUfnH.d.ts → behavior-BAc0erXF.d.ts} +21 -0
  8. package/dist/{create-game-DUAtTqID.js → create-game-CNKXGfpr.js} +270 -47
  9. package/dist/{create-game-C0bPoURs.js → create-game-CbuLWorm.js} +53 -14
  10. package/dist/debug.d.ts +51 -6
  11. package/dist/debug.js +242 -19
  12. package/dist/editor-switch-B0wB_DSr.d.ts +59 -0
  13. package/dist/editor-switch-CAKlJMIY.js +161 -0
  14. package/dist/editor.d.ts +49 -0
  15. package/dist/editor.js +8439 -0
  16. package/dist/{gameplay-Ddk13pQ6.js → gameplay-L05WgWd1.js} +220 -37
  17. package/dist/gameplay.d.ts +1 -1
  18. package/dist/gameplay.js +1 -1
  19. package/dist/index.d.ts +22 -3
  20. package/dist/index.js +2 -2
  21. package/dist/{loader-DILt9PGC.d.ts → loader-B242FF6N.d.ts} +1 -1
  22. package/dist/net.d.ts +1 -1
  23. package/dist/net.js +2 -2
  24. package/dist/{pathfinding-RWYkNKx9.d.ts → pathfinding-BwD974Ss.d.ts} +1 -1
  25. package/dist/{physics-2d-DiVFFlH3.js → physics-2d-CCVTrKOd.js} +62 -2
  26. package/dist/{physics-3d-4mGzOZ2J.js → physics-3d-BZZLtwJu.js} +439 -8
  27. package/dist/react.d.ts +1 -1
  28. package/dist/react.js +1 -1
  29. package/dist/{register-nObreUQR.js → register-BTg0EM7s.js} +35 -3
  30. package/dist/{register-DcHXS1MA.js → register-C44aSduO.js} +5694 -5108
  31. package/dist/{register-CvpSUU3O.js → register-DWcWq4QG.js} +22 -2
  32. package/dist/{register-BFFE1Mh1.js → register-MelqEdza.js} +1 -1
  33. package/dist/test.d.ts +35 -3
  34. package/dist/test.js +104 -10
  35. package/dist/vite.d.ts +53 -1
  36. package/dist/vite.js +94 -2
  37. package/editor/assets/{agent8-DElIPozC.js → agent8-DEVkEa3d.js} +1 -1
  38. package/editor/assets/debug-zGAtpDF0.js +2 -0
  39. package/editor/assets/index-Bx4UtWYY.js +10586 -0
  40. package/editor/index.html +3 -157
  41. package/package.json +3 -2
  42. package/schemas/scene.schema.json +110 -4
  43. package/skills/incanto-3d-models.md +38 -0
  44. package/skills/incanto-assets.md +13 -0
  45. package/skills/incanto-building-3d-games.md +83 -6
  46. package/skills/incanto-editor.md +151 -0
  47. package/skills/incanto-environment.md +88 -1
  48. package/skills/incanto-node-reference.md +35 -1
  49. package/skills/incanto-physics-and-input.md +45 -6
  50. package/skills/incanto-verifying-your-game.md +51 -2
  51. package/templates-app/beacon-isle-3d/docs/project-3d-rules.md +5 -0
  52. package/templates-app/beacon-isle-3d/package.json +1 -1
  53. package/templates-app/tps-3d/docs/project-3d-rules.md +5 -0
  54. package/templates-app/tps-3d/package.json +1 -1
  55. package/templates-app/village-quest-3d/docs/project-3d-rules.md +5 -0
  56. package/templates-app/village-quest-3d/package.json +1 -1
  57. package/editor/assets/index-BrneVqN0.js +0 -8330
  58. package/editor/assets/index-D8QvwvOm.css +0 -1
package/editor/index.html CHANGED
@@ -5,165 +5,11 @@
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-BrneVqN0.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-Bx4UtWYY.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="./assets/GameServer-C56iOUgF.js">
10
- <link rel="stylesheet" crossorigin href="./assets/index-D8QvwvOm.css">
11
10
  </head>
12
11
  <body>
13
- <header id="topbar">
14
- <div class="brand">
15
- <span class="brand-mark">in</span>
16
- <strong>incanto</strong><span class="brand-sub">editor</span>
17
- </div>
18
- <div id="file-chip" title="">
19
- <svg aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>
20
- <span id="file-path"></span>
21
- </div>
22
- <span id="engine-version" class="pill"></span>
23
- <span class="spacer"></span>
24
- <button type="button" id="lang-btn" class="ghost" title="Language / 언어">
25
- <span id="lang-current">EN</span>
26
- <svg aria-hidden="true" width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="m6 9 6 6 6-6"/></svg>
27
- </button>
28
- <button type="button" id="docs-btn" class="ghost" title="Node documentation">
29
- <svg aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
30
- docs
31
- </button>
32
- <button type="button" id="scenes-btn" class="ghost" hidden>
33
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2"/></svg>
34
- scenes
35
- </button>
36
- <button type="button" id="fit-btn" class="ghost" title="Zoom to fit (F)">⌖ fit</button>
37
- <button type="button" id="undo-btn" class="ghost" title="Undo (Ctrl/Cmd+Z)" disabled>↩ undo</button>
38
- <button type="button" id="play-btn" class="play" title="Run the scene with physics + input">
39
- <svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
40
- play
41
- </button>
42
- <button type="button" id="save-btn" class="primary" disabled>save</button>
43
- </header>
44
- <div id="error-banner" hidden></div>
45
- <div id="play-notice" hidden></div>
46
- <main>
47
- <aside id="tree-pane">
48
- <button type="button" class="pane-title explorer-section section-toggle" data-section="assets">
49
- <span class="chev"><svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m6 9 6 6 6-6"/></svg></span> assets
50
- <span class="spacer"></span>
51
- <span id="group-add-btn" class="ghost section-add" title="Add a group (folder)" role="button">
52
- <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M12 11v6M9 14h6"/></svg>
53
- </span>
54
- <span id="asset-add-btn" class="ghost section-add" title="Add an asset" role="button">+</span>
55
- </button>
56
- <div id="asset-tree" data-body="assets"></div>
57
- <button type="button" class="pane-title explorer-section section-toggle" data-section="scene">
58
- <span class="chev"><svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m6 9 6 6 6-6"/></svg></span> scene
59
- </button>
60
- <div id="scene-body" data-body="scene">
61
- <div id="tree"></div>
62
- <div id="tree-actions">
63
- <select id="add-type" title="Node type to add"></select>
64
- <button type="button" id="add-btn" class="ghost" title="Add child to the selected node">+</button>
65
- <button type="button" id="generate-btn" class="ghost" title="Generate an environment under the selected node">
66
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M18.4 5.6l-2.8 2.8M8.4 15.6l-2.8 2.8"/></svg>
67
- </button>
68
- <button type="button" id="delete-btn" class="ghost danger" title="Delete the selected node">✕</button>
69
- </div>
70
- </div>
71
- </aside>
72
- <section id="viewport-pane">
73
- <div id="mode-toolbar">
74
- <button type="button" id="tool-move" class="tool active" title="Move (W)">
75
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v20M2 12h20M8 6l4-4 4 4M8 18l4 4 4-4M6 8l-4 4 4 4M18 8l4 4-4 4"/></svg>
76
- </button>
77
- <button type="button" id="tool-rotate" class="tool" title="Rotate (E)">
78
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 3v5h-5"/></svg>
79
- </button>
80
- <button type="button" id="tool-scale" class="tool" title="Scale (R)">
81
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 3 9 15"/><path d="M21 8V3h-5"/><rect x="3" y="13" width="8" height="8" rx="1"/></svg>
82
- </button>
83
- <span class="tool-divider"></span>
84
- <button type="button" id="tool-colliders" class="tool active" title="Show colliders">
85
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-dasharray="3 2"><rect x="4" y="4" width="16" height="16" rx="2"/></svg>
86
- </button>
87
- </div>
88
- <canvas id="viewport"></canvas>
89
- <canvas id="overlay"></canvas>
90
- <canvas id="play-canvas" hidden></canvas>
91
- <div id="hints-dock">
92
- <div id="hints" class="hints">
93
- <span><b>click</b> select</span><span><b>drag</b> move</span><span><b>wheel</b> zoom</span><span><b>W/E/R</b> move/rotate/scale</span><span><b>⇧</b> snap</span><span><b>right-drag</b> pan</span>
94
- </div>
95
- <div id="hints-3d" class="hints" hidden>
96
- <span><b>drag</b> orbit</span><span><b>right-drag</b> pan</span><span><b>wheel</b> zoom</span><span><b>W/E/R</b> mode</span><span><b>⇧</b> snap</span><span><b>F</b> frame</span><span><b>0</b> game cam</span>
97
- </div>
98
- <button type="button" id="hints-toggle" title="Controls">
99
- <svg aria-hidden="true" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 10h.01M10 10h.01M14 10h.01M18 10h.01M6 14h.01M18 14h.01M9 14h6"/></svg>
100
- </button>
101
- </div>
102
- <button type="button" id="gameview-btn" class="ghost" title="View through the game camera — exactly what running the scene shows (0)">
103
- <svg aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 7h12v10H3z"/><path d="M15 10l6-3v10l-6-3"/></svg>
104
- game cam
105
- </button>
106
- </section>
107
- <aside id="inspector-pane">
108
- <div class="pane-title">inspector</div>
109
- <div id="inspector"></div>
110
- </aside>
111
- </main>
112
- <div id="picker" hidden>
113
- <div id="picker-card">
114
- <div class="pane-title picker-title">
115
- open a scene
116
- <button type="button" id="picker-close" title="Close (Esc)">✕</button>
117
- </div>
118
- <div id="picker-list"></div>
119
- <div id="picker-new">
120
- <input id="picker-path" type="text" placeholder="src/new-level.scene.json" />
121
- <button type="button" id="picker-create" class="primary">create</button>
122
- </div>
123
- </div>
124
- </div>
125
- <div id="docs" hidden>
126
- <div id="docs-card">
127
- <div class="pane-title picker-title">
128
- incanto nodes
129
- <button type="button" id="docs-close" title="Close (Esc)">✕</button>
130
- </div>
131
- <div id="docs-layout">
132
- <nav id="docs-tabs"></nav>
133
- <div id="docs-body"></div>
134
- </div>
135
- </div>
136
- </div>
137
- <div id="confirm" hidden>
138
- <div id="confirm-card">
139
- <div class="pane-title picker-title">
140
- <span id="confirm-title">still referenced</span>
141
- <button type="button" id="confirm-close" title="Close (Esc)">✕</button>
142
- </div>
143
- <div id="confirm-body">
144
- <div id="confirm-text"></div>
145
- <div id="confirm-list"></div>
146
- </div>
147
- <div id="confirm-actions">
148
- <button type="button" id="confirm-cancel" class="ghost">cancel</button>
149
- <button type="button" id="confirm-delete">unlink &amp; delete</button>
150
- </div>
151
- </div>
152
- </div>
153
- <div id="generate" hidden>
154
- <div id="generate-card">
155
- <div class="pane-title picker-title">
156
- generate environment
157
- <button type="button" id="generate-close" title="Close (Esc)">✕</button>
158
- </div>
159
- <div id="generate-body"></div>
160
- <div id="generate-actions">
161
- <span id="generate-status"></span>
162
- <button type="button" id="generate-cancel" class="ghost">cancel</button>
163
- <button type="button" id="generate-insert" class="primary">insert</button>
164
- </div>
165
- </div>
166
- </div>
167
- <div id="popover" hidden></div>
12
+ <!-- The shell is built by src/shell.ts (generated from this file by
13
+ scripts/bundle-assets.mjs) so a running GAME can mount the same editor. -->
168
14
  </body>
169
15
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incanto",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Vibe-coding-first web game engine SDK — JSON-driven scenes on three.js",
5
5
  "keywords": [
6
6
  "game-engine",
@@ -48,7 +48,8 @@
48
48
  "./vite": "./dist/vite.js",
49
49
  "./package.json": "./package.json",
50
50
  "./schemas/*": "./schemas/*",
51
- "./assets/*": "./assets/*"
51
+ "./assets/*": "./assets/*",
52
+ "./editor": "./dist/editor.js"
52
53
  },
53
54
  "scripts": {
54
55
  "build": "tsdown",
@@ -588,6 +588,9 @@
588
588
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
589
589
  "default": "default"
590
590
  },
591
+ "snapToGround": {
592
+ "default": null
593
+ },
591
594
  "texture": {
592
595
  "type": "string",
593
596
  "default": ""
@@ -855,6 +858,9 @@
855
858
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
856
859
  "default": "default"
857
860
  },
861
+ "snapToGround": {
862
+ "default": null
863
+ },
858
864
  "collider": {
859
865
  "type": "object",
860
866
  "default": {}
@@ -1073,6 +1079,9 @@
1073
1079
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
1074
1080
  "default": "default"
1075
1081
  },
1082
+ "snapToGround": {
1083
+ "default": null
1084
+ },
1076
1085
  "mode": {
1077
1086
  "type": "string",
1078
1087
  "enum": ["screen", "y", "none"],
@@ -1173,6 +1182,9 @@
1173
1182
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
1174
1183
  "default": "default"
1175
1184
  },
1185
+ "snapToGround": {
1186
+ "default": null
1187
+ },
1176
1188
  "target": {
1177
1189
  "type": "string",
1178
1190
  "default": ""
@@ -1276,6 +1288,9 @@
1276
1288
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
1277
1289
  "default": "default"
1278
1290
  },
1291
+ "snapToGround": {
1292
+ "default": null
1293
+ },
1279
1294
  "target": {
1280
1295
  "type": "string",
1281
1296
  "default": ""
@@ -1506,6 +1521,9 @@
1506
1521
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
1507
1522
  "default": "default"
1508
1523
  },
1524
+ "snapToGround": {
1525
+ "default": null
1526
+ },
1509
1527
  "fov": {
1510
1528
  "type": "number",
1511
1529
  "default": 60
@@ -1730,6 +1748,10 @@
1730
1748
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
1731
1749
  "default": "default"
1732
1750
  },
1751
+ "snapToGround": {
1752
+ "type": "boolean",
1753
+ "default": true
1754
+ },
1733
1755
  "collider": {
1734
1756
  "type": "object",
1735
1757
  "default": {}
@@ -1743,10 +1765,6 @@
1743
1765
  "maxItems": 3,
1744
1766
  "default": [0, 0, 0]
1745
1767
  },
1746
- "snapToGround": {
1747
- "type": "boolean",
1748
- "default": true
1749
- },
1750
1768
  "slopeLimitDeg": {
1751
1769
  "type": "number",
1752
1770
  "default": 45
@@ -1919,6 +1937,9 @@
1919
1937
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
1920
1938
  "default": "default"
1921
1939
  },
1940
+ "snapToGround": {
1941
+ "default": null
1942
+ },
1922
1943
  "view": {
1923
1944
  "type": "string",
1924
1945
  "enum": ["free", "firstPerson", "quarter", "side"],
@@ -2219,6 +2240,9 @@
2219
2240
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
2220
2241
  "default": "default"
2221
2242
  },
2243
+ "snapToGround": {
2244
+ "default": null
2245
+ },
2222
2246
  "color": {
2223
2247
  "type": "string",
2224
2248
  "default": "#ffffff"
@@ -2338,6 +2362,9 @@
2338
2362
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
2339
2363
  "default": "default"
2340
2364
  },
2365
+ "snapToGround": {
2366
+ "default": null
2367
+ },
2341
2368
  "density": {
2342
2369
  "type": "string",
2343
2370
  "enum": ["lush", "sparse", "none"],
@@ -2379,6 +2406,10 @@
2379
2406
  "drape": {
2380
2407
  "default": null
2381
2408
  },
2409
+ "avoidWater": {
2410
+ "type": "boolean",
2411
+ "default": true
2412
+ },
2382
2413
  "terrain": {
2383
2414
  "type": "string",
2384
2415
  "default": ""
@@ -2478,6 +2509,9 @@
2478
2509
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
2479
2510
  "default": "default"
2480
2511
  },
2512
+ "snapToGround": {
2513
+ "default": null
2514
+ },
2481
2515
  "kind": {
2482
2516
  "type": "string",
2483
2517
  "enum": ["grass", "flowers", "reeds"],
@@ -2566,6 +2600,10 @@
2566
2600
  "drape": {
2567
2601
  "default": null
2568
2602
  },
2603
+ "avoidWater": {
2604
+ "type": "boolean",
2605
+ "default": true
2606
+ },
2569
2607
  "terrain": {
2570
2608
  "type": "string",
2571
2609
  "default": ""
@@ -2724,6 +2762,9 @@
2724
2762
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
2725
2763
  "default": "default"
2726
2764
  },
2765
+ "snapToGround": {
2766
+ "default": null
2767
+ },
2727
2768
  "mesh": {
2728
2769
  "type": "string",
2729
2770
  "enum": ["box", "sphere", "capsule", "plane", "cylinder", "gem"],
@@ -2753,6 +2794,9 @@
2753
2794
  "receiveShadow": {
2754
2795
  "type": "boolean",
2755
2796
  "default": false
2797
+ },
2798
+ "collider": {
2799
+ "default": null
2756
2800
  }
2757
2801
  },
2758
2802
  "additionalProperties": false
@@ -2978,6 +3022,9 @@
2978
3022
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
2979
3023
  "default": "default"
2980
3024
  },
3025
+ "snapToGround": {
3026
+ "default": null
3027
+ },
2981
3028
  "type": {
2982
3029
  "type": "string",
2983
3030
  "enum": ["fixed", "spherical", "rope", "spring"],
@@ -3222,6 +3269,9 @@
3222
3269
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
3223
3270
  "default": "default"
3224
3271
  },
3272
+ "snapToGround": {
3273
+ "default": null
3274
+ },
3225
3275
  "sections": {
3226
3276
  "type": "array",
3227
3277
  "default": []
@@ -3341,6 +3391,9 @@
3341
3391
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
3342
3392
  "default": "default"
3343
3393
  },
3394
+ "snapToGround": {
3395
+ "default": null
3396
+ },
3344
3397
  "mesh": {
3345
3398
  "type": "string",
3346
3399
  "enum": ["box", "sphere", "capsule", "plane", "cylinder", "gem"],
@@ -3463,6 +3516,9 @@
3463
3516
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
3464
3517
  "default": "default"
3465
3518
  },
3519
+ "snapToGround": {
3520
+ "default": null
3521
+ },
3466
3522
  "model": {
3467
3523
  "type": "string",
3468
3524
  "default": ""
@@ -3483,6 +3539,14 @@
3483
3539
  "type": "boolean",
3484
3540
  "default": true
3485
3541
  },
3542
+ "animationSpeed": {
3543
+ "type": "number",
3544
+ "default": 1
3545
+ },
3546
+ "animationOffset": {
3547
+ "type": "number",
3548
+ "default": 0
3549
+ },
3486
3550
  "animationUpper": {
3487
3551
  "type": "string",
3488
3552
  "default": ""
@@ -3809,6 +3873,9 @@
3809
3873
  "type": "string",
3810
3874
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
3811
3875
  "default": "default"
3876
+ },
3877
+ "snapToGround": {
3878
+ "default": null
3812
3879
  }
3813
3880
  },
3814
3881
  "additionalProperties": false
@@ -3905,6 +3972,9 @@
3905
3972
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
3906
3973
  "default": "default"
3907
3974
  },
3975
+ "snapToGround": {
3976
+ "default": null
3977
+ },
3908
3978
  "color": {
3909
3979
  "type": "string",
3910
3980
  "default": "#ffffff"
@@ -4213,6 +4283,9 @@
4213
4283
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
4214
4284
  "default": "effects"
4215
4285
  },
4286
+ "snapToGround": {
4287
+ "default": null
4288
+ },
4216
4289
  "preset": {
4217
4290
  "type": "string",
4218
4291
  "enum": [
@@ -4546,6 +4619,9 @@
4546
4619
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
4547
4620
  "default": "default"
4548
4621
  },
4622
+ "snapToGround": {
4623
+ "default": null
4624
+ },
4549
4625
  "collider": {
4550
4626
  "type": "object",
4551
4627
  "default": {}
@@ -4674,6 +4750,9 @@
4674
4750
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
4675
4751
  "default": "default"
4676
4752
  },
4753
+ "snapToGround": {
4754
+ "default": null
4755
+ },
4677
4756
  "path": {
4678
4757
  "type": "array",
4679
4758
  "default": []
@@ -4961,6 +5040,9 @@
4961
5040
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
4962
5041
  "default": "default"
4963
5042
  },
5043
+ "snapToGround": {
5044
+ "default": null
5045
+ },
4964
5046
  "texture": {
4965
5047
  "type": "string",
4966
5048
  "default": ""
@@ -5208,6 +5290,9 @@
5208
5290
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
5209
5291
  "default": "default"
5210
5292
  },
5293
+ "snapToGround": {
5294
+ "default": null
5295
+ },
5211
5296
  "collider": {
5212
5297
  "type": "object",
5213
5298
  "default": {}
@@ -5307,6 +5392,9 @@
5307
5392
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
5308
5393
  "default": "terrain"
5309
5394
  },
5395
+ "snapToGround": {
5396
+ "default": null
5397
+ },
5310
5398
  "size": {
5311
5399
  "type": "array",
5312
5400
  "items": {
@@ -5656,6 +5744,9 @@
5656
5744
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
5657
5745
  "default": "default"
5658
5746
  },
5747
+ "snapToGround": {
5748
+ "default": null
5749
+ },
5659
5750
  "width": {
5660
5751
  "type": "number",
5661
5752
  "default": 0.3
@@ -5779,6 +5870,9 @@
5779
5870
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
5780
5871
  "default": "default"
5781
5872
  },
5873
+ "snapToGround": {
5874
+ "default": null
5875
+ },
5782
5876
  "tier": {
5783
5877
  "type": "string",
5784
5878
  "enum": ["simple", "medium", "high"],
@@ -6444,6 +6538,9 @@
6444
6538
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
6445
6539
  "default": "default"
6446
6540
  },
6541
+ "snapToGround": {
6542
+ "default": null
6543
+ },
6447
6544
  "voxels": {
6448
6545
  "type": "array",
6449
6546
  "default": []
@@ -6544,6 +6641,9 @@
6544
6641
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
6545
6642
  "default": "default"
6546
6643
  },
6644
+ "snapToGround": {
6645
+ "default": null
6646
+ },
6547
6647
  "size": {
6548
6648
  "type": "array",
6549
6649
  "items": {
@@ -6602,6 +6702,9 @@
6602
6702
  "type": "boolean",
6603
6703
  "default": true
6604
6704
  },
6705
+ "reflectivity": {
6706
+ "default": null
6707
+ },
6605
6708
  "interaction": {
6606
6709
  "type": "boolean",
6607
6710
  "default": true
@@ -6767,6 +6870,9 @@
6767
6870
  "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
6768
6871
  "default": "default"
6769
6872
  },
6873
+ "snapToGround": {
6874
+ "default": null
6875
+ },
6770
6876
  "size": {
6771
6877
  "type": "array",
6772
6878
  "items": {
@@ -150,6 +150,44 @@ model file, or an animation-asset reference:
150
150
  Models need LIGHT (standard materials): add a DirectionalLight3D or scene
151
151
  `environment.ambient` or you will see black silhouettes.
152
152
 
153
+ ## A model that just animates — no script
154
+
155
+ Setting `animation` is the whole thing. The clip starts the moment the model
156
+ mounts and keeps looping; nothing has to call play(). That is how you dress a
157
+ scene with a torch that flickers, a flag that waves, a fish that swims, or an
158
+ idling NPC:
159
+
160
+ ```json
161
+ { "name": "Torch", "type": "ModelInstance3D",
162
+ "props": { "model": "$torch", "animation": "Flicker" } }
163
+ ```
164
+
165
+ Four props shape it, and they are all live (a behavior or the editor can change
166
+ them mid-play without restarting the clip):
167
+
168
+ | prop | default | what it does |
169
+ |---|---|---|
170
+ | `animation` | `""` | embedded clip name, or `$key` of an animation asset. `""` = still |
171
+ | `animationLoop` | `true` | `false` = play once, hold the last pose, emit `animationFinished` |
172
+ | `animationSpeed` | `1` | playback rate. `0.5` half, `2` double, `0` freezes the pose, **negative plays backwards** (a closing door is an opening one reversed) |
173
+ | `animationOffset` | `0` | seconds into the clip to start at, wrapped into its duration |
174
+
175
+ **`animationOffset` is what stops a scene looking like copies.** Ten torches all
176
+ starting at 0 flicker in perfect unison and the eye reads it instantly. Give each
177
+ a different offset and the same clip becomes a crowd:
178
+
179
+ ```json
180
+ { "name": "Torch2", "type": "ModelInstance3D",
181
+ "props": { "model": "$torch", "animation": "Flicker",
182
+ "animationOffset": 0.7, "animationSpeed": 0.9 } }
183
+ ```
184
+
185
+ A generator or scatter can hand out `index * 0.37` without knowing the clip
186
+ length — offsets wrap, so any number is legal.
187
+
188
+ For a second clip on the upper body only (attack while running), see
189
+ `animationUpper` / `animationUpperLoop` / `upperBodyRoot` in the node reference.
190
+
153
191
  ## How loading works
154
192
 
155
193
  `Renderer3D` owns an `AssetStore3D` (GLTFLoader + `@pixiv/three-vrm`'s VRMLoaderPlugin).
@@ -105,6 +105,19 @@ const { asset, props } = spriteFromLibraryMeta(animJson, {
105
105
  For 3D files, ALWAYS inspect before placing: `bunx incanto-model file.glb`
106
106
  (bounds, animations, rig — see incanto-3d-models).
107
107
 
108
+ ### The agent8 library, from inside the editor
109
+
110
+ `bunx incanto-editor --token <v8 access token>` puts a **📚** button on every
111
+ field that takes a resource. It browses the same catalog the agent8 workbench
112
+ does (2D sprites; 3D characters/monsters/objects/vehicles/weapons/polyhaven/
113
+ textures), previews a GLB with the engine and an image as an image, and on pick
114
+ writes the URL — or, for a `$assetKey` field, writes the scene `assets{}` entry
115
+ AND the ref together. See `incanto-editor` for the token and proxy details.
116
+
117
+ Everything the library hands you is an ordinary URL on the agent8 CDN, so a
118
+ scene built that way stays plain JSON you can also hand-write.
119
+
120
+
108
121
  ## 3. No art yet? Ship gameplay anyway
109
122
 
110
123
  - `ColorRect2D` — solid rectangles for paddles/walls/platforms/flashes.