incanto 0.11.0 → 0.13.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 +22 -0
- package/bin/incanto-new.mjs +87 -0
- package/dist/2d.d.ts +2 -2
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +246 -71
- package/dist/3d.js +143 -4
- package/dist/{behavior-Bod1AyJS.d.ts → behavior-CPibUfnH.d.ts} +4 -2
- package/dist/{create-game-CdWhP6ve.js → create-game-Ct86zczq.js} +5 -5
- package/dist/{create-game-DZ_4Nfq8.js → create-game-J6g4QInv.js} +75 -24
- package/dist/debug.d.ts +1 -1
- package/dist/{duplicate-BEvGBtb_.js → duplicate-BINwP0WG.js} +1 -1
- package/dist/{gameplay-Dn3yidJw.js → gameplay-sSqrUcnz.js} +75 -3
- package/dist/gameplay.d.ts +37 -2
- package/dist/gameplay.js +2 -2
- package/dist/index.d.ts +3 -30
- package/dist/index.js +4 -4
- package/dist/{loader-B4OEXDZ8.js → loader-BZqOKfI2.js} +3 -1
- package/dist/{loader-BcUDfNHn.d.ts → loader-DILt9PGC.d.ts} +1 -1
- package/dist/net.d.ts +1 -1
- package/dist/net.js +3 -3
- package/dist/{audio-player-Dyjg5k92.d.ts → pathfinding-RWYkNKx9.d.ts} +29 -2
- package/dist/{physics-2d-DweTURFt.js → physics-2d-BiIdl51r.js} +8 -4
- package/dist/{physics-3d-Br97Ans2.js → physics-3d-DhaAaKJo.js} +8 -4
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-Cwc-yCYx.js → register-BFFE1Mh1.js} +2 -2
- package/dist/{register-ysLR3WQS.js → register-BFw4c83z.js} +508 -123
- package/dist/{register-BD6zgrv_.js → register-Djwckzgx.js} +2 -2
- package/dist/{register-DJZXxwAn.js → register-nObreUQR.js} +1 -1
- package/dist/test.d.ts +2 -2
- package/dist/test.js +10 -10
- package/editor/assets/{agent8-BxNvAcLA.js → agent8-BF8Jfbkl.js} +1 -1
- package/editor/assets/{index-DMCQXowF.js → index-DSq0gdhE.js} +53 -41
- package/editor/index.html +1 -1
- package/package.json +4 -2
- package/schemas/scene.schema.json +263 -0
- package/skills/incanto-3d-character.md +37 -1
- package/skills/incanto-building-3d-games.md +83 -0
- package/skills/incanto-editor.md +3 -3
- package/skills/incanto-gameplay-behaviors.md +23 -1
- package/skills/incanto-node-reference.md +51 -0
- package/templates-app/beacon-isle-3d/PROJECT/Context.md +44 -0
- package/templates-app/beacon-isle-3d/PROJECT/Requirements.md +16 -0
- package/templates-app/beacon-isle-3d/PROJECT/Status.md +38 -0
- package/templates-app/beacon-isle-3d/PROJECT/Structure.md +24 -0
- package/templates-app/beacon-isle-3d/docs/project-3d-rules.md +38 -0
- package/templates-app/beacon-isle-3d/generate-world.ts +814 -0
- package/templates-app/beacon-isle-3d/index.html +68 -0
- package/templates-app/beacon-isle-3d/package.json +26 -0
- package/templates-app/beacon-isle-3d/src/behaviors.ts +570 -0
- package/templates-app/beacon-isle-3d/src/game.scene.json +2992 -0
- package/templates-app/beacon-isle-3d/src/main.ts +33 -0
- package/templates-app/beacon-isle-3d/tsconfig.json +13 -0
- package/templates-app/beacon-isle-3d/verify.ts +285 -0
- package/templates-app/beacon-isle-3d/vite.config.ts +5 -0
- package/templates-app/tps-3d/PROJECT/Context.md +49 -0
- package/templates-app/tps-3d/PROJECT/Requirements.md +38 -0
- package/templates-app/tps-3d/PROJECT/Status.md +41 -0
- package/templates-app/tps-3d/PROJECT/Structure.md +44 -0
- package/templates-app/tps-3d/docs/project-3d-rules.md +38 -0
- package/templates-app/tps-3d/index.html +244 -0
- package/templates-app/tps-3d/package.json +25 -0
- package/templates-app/tps-3d/src/behaviors.ts +405 -0
- package/templates-app/tps-3d/src/game.scene.json +677 -0
- package/templates-app/tps-3d/src/main.ts +64 -0
- package/templates-app/tps-3d/tsconfig.json +13 -0
- package/templates-app/tps-3d/verify.ts +167 -0
- package/templates-app/tps-3d/vite.config.ts +5 -0
- package/templates-app/village-quest-3d/PROJECT/Context.md +39 -0
- package/templates-app/village-quest-3d/PROJECT/Requirements.md +36 -0
- package/templates-app/village-quest-3d/PROJECT/Status.md +26 -0
- package/templates-app/village-quest-3d/PROJECT/Structure.md +30 -0
- package/templates-app/village-quest-3d/docs/project-3d-rules.md +38 -0
- package/templates-app/village-quest-3d/index.html +68 -0
- package/templates-app/village-quest-3d/package.json +25 -0
- package/templates-app/village-quest-3d/src/behaviors.ts +529 -0
- package/templates-app/village-quest-3d/src/grove.scene.json +826 -0
- package/templates-app/village-quest-3d/src/main.ts +35 -0
- package/templates-app/village-quest-3d/src/quest.ts +18 -0
- package/templates-app/village-quest-3d/src/village.scene.json +680 -0
- package/templates-app/village-quest-3d/tsconfig.json +13 -0
- package/templates-app/village-quest-3d/verify.ts +254 -0
- package/templates-app/village-quest-3d/vite.config.ts +5 -0
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-
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-DSq0gdhE.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/GameServer-C56iOUgF.js">
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-D8QvwvOm.css">
|
|
11
11
|
</head>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "incanto",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Vibe-coding-first web game engine SDK — JSON-driven scenes on three.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"game-engine",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"skills",
|
|
28
28
|
"bin",
|
|
29
29
|
"editor",
|
|
30
|
+
"templates-app",
|
|
30
31
|
"assets",
|
|
31
32
|
"LICENSE",
|
|
32
33
|
"THIRD-PARTY-NOTICES.md"
|
|
@@ -92,6 +93,7 @@
|
|
|
92
93
|
"incanto-check": "bin/incanto-check.mjs",
|
|
93
94
|
"incanto-assets": "bin/incanto-assets.mjs",
|
|
94
95
|
"incanto-env": "bin/incanto-env.mjs",
|
|
95
|
-
"incanto-play": "bin/incanto-play.mjs"
|
|
96
|
+
"incanto-play": "bin/incanto-play.mjs",
|
|
97
|
+
"incanto-new": "bin/incanto-new.mjs"
|
|
96
98
|
}
|
|
97
99
|
}
|
|
@@ -193,6 +193,9 @@
|
|
|
193
193
|
{
|
|
194
194
|
"$ref": "#/$defs/BoneAttachment3D"
|
|
195
195
|
},
|
|
196
|
+
{
|
|
197
|
+
"$ref": "#/$defs/BoneLookAt3D"
|
|
198
|
+
},
|
|
196
199
|
{
|
|
197
200
|
"$ref": "#/$defs/Camera2D"
|
|
198
201
|
},
|
|
@@ -226,6 +229,9 @@
|
|
|
226
229
|
{
|
|
227
230
|
"$ref": "#/$defs/HudLayer"
|
|
228
231
|
},
|
|
232
|
+
{
|
|
233
|
+
"$ref": "#/$defs/InstancedMesh3D"
|
|
234
|
+
},
|
|
229
235
|
{
|
|
230
236
|
"$ref": "#/$defs/Joint2D"
|
|
231
237
|
},
|
|
@@ -1196,6 +1202,126 @@
|
|
|
1196
1202
|
},
|
|
1197
1203
|
"required": ["name", "type"]
|
|
1198
1204
|
},
|
|
1205
|
+
"BoneLookAt3D": {
|
|
1206
|
+
"type": "object",
|
|
1207
|
+
"properties": {
|
|
1208
|
+
"name": {
|
|
1209
|
+
"type": "string"
|
|
1210
|
+
},
|
|
1211
|
+
"uid": {
|
|
1212
|
+
"type": "string"
|
|
1213
|
+
},
|
|
1214
|
+
"type": {
|
|
1215
|
+
"const": "BoneLookAt3D"
|
|
1216
|
+
},
|
|
1217
|
+
"groups": {
|
|
1218
|
+
"type": "array",
|
|
1219
|
+
"items": {
|
|
1220
|
+
"type": "string"
|
|
1221
|
+
}
|
|
1222
|
+
},
|
|
1223
|
+
"tags": {
|
|
1224
|
+
"type": "object"
|
|
1225
|
+
},
|
|
1226
|
+
"props": {
|
|
1227
|
+
"type": "object",
|
|
1228
|
+
"properties": {
|
|
1229
|
+
"position": {
|
|
1230
|
+
"type": "array",
|
|
1231
|
+
"items": {
|
|
1232
|
+
"type": "number"
|
|
1233
|
+
},
|
|
1234
|
+
"minItems": 3,
|
|
1235
|
+
"maxItems": 3,
|
|
1236
|
+
"default": [0, 0, 0]
|
|
1237
|
+
},
|
|
1238
|
+
"rotation": {
|
|
1239
|
+
"type": "array",
|
|
1240
|
+
"items": {
|
|
1241
|
+
"type": "number"
|
|
1242
|
+
},
|
|
1243
|
+
"minItems": 3,
|
|
1244
|
+
"maxItems": 3,
|
|
1245
|
+
"default": [0, 0, 0]
|
|
1246
|
+
},
|
|
1247
|
+
"static": {
|
|
1248
|
+
"type": "boolean",
|
|
1249
|
+
"default": false
|
|
1250
|
+
},
|
|
1251
|
+
"scale": {
|
|
1252
|
+
"type": "array",
|
|
1253
|
+
"items": {
|
|
1254
|
+
"type": "number"
|
|
1255
|
+
},
|
|
1256
|
+
"minItems": 3,
|
|
1257
|
+
"maxItems": 3,
|
|
1258
|
+
"default": [1, 1, 1]
|
|
1259
|
+
},
|
|
1260
|
+
"visible": {
|
|
1261
|
+
"type": "boolean",
|
|
1262
|
+
"default": true
|
|
1263
|
+
},
|
|
1264
|
+
"renderOrder": {
|
|
1265
|
+
"type": "number",
|
|
1266
|
+
"default": 0
|
|
1267
|
+
},
|
|
1268
|
+
"orderGroup": {
|
|
1269
|
+
"type": "string",
|
|
1270
|
+
"enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
|
|
1271
|
+
"default": "default"
|
|
1272
|
+
},
|
|
1273
|
+
"target": {
|
|
1274
|
+
"type": "string",
|
|
1275
|
+
"default": ""
|
|
1276
|
+
},
|
|
1277
|
+
"bone": {
|
|
1278
|
+
"type": "string",
|
|
1279
|
+
"default": "Head"
|
|
1280
|
+
},
|
|
1281
|
+
"lookAt": {
|
|
1282
|
+
"type": "string",
|
|
1283
|
+
"default": ""
|
|
1284
|
+
},
|
|
1285
|
+
"maxAngleDeg": {
|
|
1286
|
+
"type": "number",
|
|
1287
|
+
"default": 75
|
|
1288
|
+
},
|
|
1289
|
+
"weight": {
|
|
1290
|
+
"type": "number",
|
|
1291
|
+
"default": 0.85
|
|
1292
|
+
},
|
|
1293
|
+
"forwardAxis": {
|
|
1294
|
+
"type": "string",
|
|
1295
|
+
"enum": ["+x", "-x", "+y", "-y", "+z", "-z"],
|
|
1296
|
+
"default": "+z"
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
"additionalProperties": false
|
|
1300
|
+
},
|
|
1301
|
+
"script": {
|
|
1302
|
+
"type": "object",
|
|
1303
|
+
"properties": {
|
|
1304
|
+
"name": {
|
|
1305
|
+
"type": "string"
|
|
1306
|
+
},
|
|
1307
|
+
"props": {
|
|
1308
|
+
"type": "object"
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
"required": ["name"]
|
|
1312
|
+
},
|
|
1313
|
+
"network": {
|
|
1314
|
+
"type": "object"
|
|
1315
|
+
},
|
|
1316
|
+
"children": {
|
|
1317
|
+
"type": "array",
|
|
1318
|
+
"items": {
|
|
1319
|
+
"$ref": "#/$defs/node"
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
"required": ["name", "type"]
|
|
1324
|
+
},
|
|
1199
1325
|
"Camera2D": {
|
|
1200
1326
|
"type": "object",
|
|
1201
1327
|
"properties": {
|
|
@@ -2526,6 +2652,131 @@
|
|
|
2526
2652
|
},
|
|
2527
2653
|
"required": ["name", "type"]
|
|
2528
2654
|
},
|
|
2655
|
+
"InstancedMesh3D": {
|
|
2656
|
+
"type": "object",
|
|
2657
|
+
"properties": {
|
|
2658
|
+
"name": {
|
|
2659
|
+
"type": "string"
|
|
2660
|
+
},
|
|
2661
|
+
"uid": {
|
|
2662
|
+
"type": "string"
|
|
2663
|
+
},
|
|
2664
|
+
"type": {
|
|
2665
|
+
"const": "InstancedMesh3D"
|
|
2666
|
+
},
|
|
2667
|
+
"groups": {
|
|
2668
|
+
"type": "array",
|
|
2669
|
+
"items": {
|
|
2670
|
+
"type": "string"
|
|
2671
|
+
}
|
|
2672
|
+
},
|
|
2673
|
+
"tags": {
|
|
2674
|
+
"type": "object"
|
|
2675
|
+
},
|
|
2676
|
+
"props": {
|
|
2677
|
+
"type": "object",
|
|
2678
|
+
"properties": {
|
|
2679
|
+
"position": {
|
|
2680
|
+
"type": "array",
|
|
2681
|
+
"items": {
|
|
2682
|
+
"type": "number"
|
|
2683
|
+
},
|
|
2684
|
+
"minItems": 3,
|
|
2685
|
+
"maxItems": 3,
|
|
2686
|
+
"default": [0, 0, 0]
|
|
2687
|
+
},
|
|
2688
|
+
"rotation": {
|
|
2689
|
+
"type": "array",
|
|
2690
|
+
"items": {
|
|
2691
|
+
"type": "number"
|
|
2692
|
+
},
|
|
2693
|
+
"minItems": 3,
|
|
2694
|
+
"maxItems": 3,
|
|
2695
|
+
"default": [0, 0, 0]
|
|
2696
|
+
},
|
|
2697
|
+
"static": {
|
|
2698
|
+
"type": "boolean",
|
|
2699
|
+
"default": false
|
|
2700
|
+
},
|
|
2701
|
+
"scale": {
|
|
2702
|
+
"type": "array",
|
|
2703
|
+
"items": {
|
|
2704
|
+
"type": "number"
|
|
2705
|
+
},
|
|
2706
|
+
"minItems": 3,
|
|
2707
|
+
"maxItems": 3,
|
|
2708
|
+
"default": [1, 1, 1]
|
|
2709
|
+
},
|
|
2710
|
+
"visible": {
|
|
2711
|
+
"type": "boolean",
|
|
2712
|
+
"default": true
|
|
2713
|
+
},
|
|
2714
|
+
"renderOrder": {
|
|
2715
|
+
"type": "number",
|
|
2716
|
+
"default": 0
|
|
2717
|
+
},
|
|
2718
|
+
"orderGroup": {
|
|
2719
|
+
"type": "string",
|
|
2720
|
+
"enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
|
|
2721
|
+
"default": "default"
|
|
2722
|
+
},
|
|
2723
|
+
"mesh": {
|
|
2724
|
+
"type": "string",
|
|
2725
|
+
"enum": ["box", "sphere", "capsule", "plane", "cylinder", "gem"],
|
|
2726
|
+
"default": "box"
|
|
2727
|
+
},
|
|
2728
|
+
"size": {
|
|
2729
|
+
"type": "array",
|
|
2730
|
+
"items": {
|
|
2731
|
+
"type": "number"
|
|
2732
|
+
},
|
|
2733
|
+
"minItems": 3,
|
|
2734
|
+
"maxItems": 3,
|
|
2735
|
+
"default": [1, 1, 1]
|
|
2736
|
+
},
|
|
2737
|
+
"material": {
|
|
2738
|
+
"type": "object",
|
|
2739
|
+
"default": {}
|
|
2740
|
+
},
|
|
2741
|
+
"transforms": {
|
|
2742
|
+
"type": "array",
|
|
2743
|
+
"default": []
|
|
2744
|
+
},
|
|
2745
|
+
"castShadow": {
|
|
2746
|
+
"type": "boolean",
|
|
2747
|
+
"default": false
|
|
2748
|
+
},
|
|
2749
|
+
"receiveShadow": {
|
|
2750
|
+
"type": "boolean",
|
|
2751
|
+
"default": false
|
|
2752
|
+
}
|
|
2753
|
+
},
|
|
2754
|
+
"additionalProperties": false
|
|
2755
|
+
},
|
|
2756
|
+
"script": {
|
|
2757
|
+
"type": "object",
|
|
2758
|
+
"properties": {
|
|
2759
|
+
"name": {
|
|
2760
|
+
"type": "string"
|
|
2761
|
+
},
|
|
2762
|
+
"props": {
|
|
2763
|
+
"type": "object"
|
|
2764
|
+
}
|
|
2765
|
+
},
|
|
2766
|
+
"required": ["name"]
|
|
2767
|
+
},
|
|
2768
|
+
"network": {
|
|
2769
|
+
"type": "object"
|
|
2770
|
+
},
|
|
2771
|
+
"children": {
|
|
2772
|
+
"type": "array",
|
|
2773
|
+
"items": {
|
|
2774
|
+
"$ref": "#/$defs/node"
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
},
|
|
2778
|
+
"required": ["name", "type"]
|
|
2779
|
+
},
|
|
2529
2780
|
"Joint2D": {
|
|
2530
2781
|
"type": "object",
|
|
2531
2782
|
"properties": {
|
|
@@ -3228,6 +3479,18 @@
|
|
|
3228
3479
|
"type": "boolean",
|
|
3229
3480
|
"default": true
|
|
3230
3481
|
},
|
|
3482
|
+
"animationUpper": {
|
|
3483
|
+
"type": "string",
|
|
3484
|
+
"default": ""
|
|
3485
|
+
},
|
|
3486
|
+
"animationUpperLoop": {
|
|
3487
|
+
"type": "boolean",
|
|
3488
|
+
"default": false
|
|
3489
|
+
},
|
|
3490
|
+
"upperBodyRoot": {
|
|
3491
|
+
"type": "string",
|
|
3492
|
+
"default": "Spine"
|
|
3493
|
+
},
|
|
3231
3494
|
"receiveShadow": {
|
|
3232
3495
|
"type": "boolean",
|
|
3233
3496
|
"default": false
|
|
@@ -102,6 +102,25 @@ shambling clip + glowing eyes for an enemy that reads as a different creature.
|
|
|
102
102
|
"animation": "$walk", "castShadow": true } }
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
+
## Two-layer animation: attack WHILE running (`animationUpper`)
|
|
106
|
+
|
|
107
|
+
A single `animation` clip owns the whole body — setting an attack clip freezes
|
|
108
|
+
the legs. The UPPER LAYER fixes that:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
skin.animationUpper = '$anims/attack'; // spine-up plays the attack…
|
|
112
|
+
// …while `animation` (idle/run via the controller) keeps driving the legs.
|
|
113
|
+
// One-shots AUTO-CLEAR when the clip ends (and emit animationFinished) —
|
|
114
|
+
// set it per attack and forget it.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- `upperBodyRoot` (default `'Spine'`, Mixamo spellings matched) picks the bone
|
|
118
|
+
subtree the layer owns; the base clip is automatically masked to the rest,
|
|
119
|
+
so there's no half-blended overlap and no stride reset (time stays aligned).
|
|
120
|
+
- `animationUpperLoop: true` for sustained upper loops (carry, aim, wave).
|
|
121
|
+
- Works with the controller's `animations` map untouched — locomotion logic
|
|
122
|
+
never learns the attack exists.
|
|
123
|
+
|
|
105
124
|
## Animations without code
|
|
106
125
|
|
|
107
126
|
Map movement states straight in JSON — no behavior needed:
|
|
@@ -146,7 +165,24 @@ A sword in the hand, a hat on the head, sparks on a wingtip — parent them to a
|
|
|
146
165
|
- Purely VISUAL: it follows the rendered skeleton, so headless the node stays
|
|
147
166
|
at its prop transform — keep hit checks range-based from the body, never
|
|
148
167
|
bone-based.
|
|
149
|
-
- `model.boneNames()` lists every bone at runtime when you need to hunt one
|
|
168
|
+
- `model.boneNames()` lists every bone at runtime when you need to hunt one
|
|
169
|
+
(the editor's inspector offers the same list as a dropdown on the `bone` prop).
|
|
170
|
+
|
|
171
|
+
## Heads that WATCH things (`BoneLookAt3D`)
|
|
172
|
+
|
|
173
|
+
NPCs feel alive when they track you. One node, zero code:
|
|
174
|
+
|
|
175
|
+
```jsonc
|
|
176
|
+
{ "name": "HeadTrack", "type": "BoneLookAt3D",
|
|
177
|
+
"props": { "target": "../Body", "bone": "Head", "lookAt": "%Player" } }
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
- Runs on top of the playing animation each frame; blends in/out smoothly and
|
|
181
|
+
DISENGAGES when the target leaves the `maxAngleDeg` comfort cone (75° —
|
|
182
|
+
no owl necks).
|
|
183
|
+
- `weight` (0.85) is how far the head commits; `forwardAxis` defaults to the
|
|
184
|
+
mixamo head convention (+z).
|
|
185
|
+
- Also good for chests (`bone: "Spine2"`, small weight) and turrets.
|
|
150
186
|
|
|
151
187
|
## Facing a direction in 3D — the +Z-FORWARD rule (read before turning ANY skin)
|
|
152
188
|
|
|
@@ -17,6 +17,19 @@ GLB/glTF/VRM files load via `ModelInstance3D` — see `incanto-3d-models.md` for
|
|
|
17
17
|
asset declaration, `targetHeight` sizing, and the `bunx incanto-model` inspector that
|
|
18
18
|
reports a file's real bounding box and animation names.
|
|
19
19
|
|
|
20
|
+
## Fastest start: scaffold a whole game
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bunx incanto-new my-game # Beacon Isle — 3D flagship template
|
|
24
|
+
bunx incanto-new my-game --template tps-3d # third-person shooter starter
|
|
25
|
+
bunx incanto-new --list
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Templates are COMPLETE games (world generation, quest, enemies, verify
|
|
29
|
+
harness) meant to be reshaped — regenerate the Beacon Isle world with
|
|
30
|
+
`bun run world`, keep the game logic. Prefer starting from one over wiring
|
|
31
|
+
from scratch.
|
|
32
|
+
|
|
20
33
|
## Boot sequence (the only TypeScript you need to start)
|
|
21
34
|
|
|
22
35
|
```ts
|
|
@@ -65,6 +78,28 @@ All 3D nodes extend `Node3D` and therefore have the transform props:
|
|
|
65
78
|
| `material` | `{}` | `{color: '#hex', metalness: 0..1, roughness: 0..1, opacity: 0..1, clearcoat: 0..1, clearcoatRoughness: 0..1, envMapIntensity: >=0, wireframe, flatShading, depthTest, depthWrite, emissive, emissiveIntensity, map, normalMap, repeat: [u,v]}` — the object IS the material state: omitted keys reset to defaults (`#ffffff`, 0, 1, 1, false, false, depthTest/Write true). `opacity < 1` turns on transparency; `flatShading` gives faceted per-face glints (the gem sparkle). `depthTest: false` (+ a high `renderOrder`) makes a flat `plane`/`cylinder` an ALWAYS-ON-TOP ground decal — AoE telegraphs, selection/spawn rings — that never z-fights with or is hidden by bumpy terrain (`depthWrite: false` keeps it from occluding later effects). `map`/`normalMap` are texture URLs (lazy-loaded, headless-safe; map samples sRGB, normalMap linear), tiled `repeat` times across each face — box/plane UVs span 0..1 per face, so for worldspace density use `repeat: [length/tile, height/tile]` (e.g. a 6×2.5 m brick wall at one tile per 2 m → `[3, 1.25]`). `color` tints the map (near-white keeps the texture's own color). CAR PAINT: `clearcoat: 1` adds a glossy lacquer layer with its own reflection over a metallic base (`clearcoatRoughness` ~0.05-0.1 = mirror lacquer) — THE premium vehicle-paint look; `envMapIntensity` scales how strongly the surface mirrors the scene's sky/HDRI (glass windows want ~2, matte surfaces <1; needs a `sky`/`hdri` environment to reflect — at night with no sky there is nothing to mirror). NB: under ACES a very bright `emissiveIntensity` blows out to white — keep it modest (~1–2) + a saturated color for a vivid GLOWING look. Unknown keys, malformed `repeat`, or `repeat` without a map hard-fail at load |
|
|
66
79
|
| `castShadow` / `receiveShadow` | `false` | |
|
|
67
80
|
|
|
81
|
+
### `InstancedMesh3D` (hundreds of props, one draw call)
|
|
82
|
+
|
|
83
|
+
Scattered rocks, fence posts, crates, gravestones — identical meshes should
|
|
84
|
+
never be one node each. One instanced node draws them all at once:
|
|
85
|
+
|
|
86
|
+
```jsonc
|
|
87
|
+
{ "name": "Rocks", "type": "InstancedMesh3D",
|
|
88
|
+
"props": { "mesh": "gem", "size": [0.7, 0.5, 0.7],
|
|
89
|
+
"material": { "color": "#8a8f98", "roughness": 0.95, "flatShading": true },
|
|
90
|
+
"transforms": [
|
|
91
|
+
[12.2, 0.1, -8.4, 40, 1.3],
|
|
92
|
+
[-6.0, 0.2, 14.1, 210, 0.8],
|
|
93
|
+
[3.5, 0.0, 22.7, 118, 1.0]
|
|
94
|
+
] } }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- Each row: `[x, y, z, yawDeg?, scale?]` — dense enough to GENERATE hundreds
|
|
98
|
+
(pair with `terrain.heightAt` for y).
|
|
99
|
+
- Same `mesh`/`size`/`material` surface as MeshInstance3D. Replace the whole
|
|
100
|
+
`transforms` array to update. No per-instance colliders — add a few
|
|
101
|
+
StaticBody3D boxes only where gameplay needs blocking.
|
|
102
|
+
|
|
68
103
|
### `LoftMesh3D` (smooth swept hulls — vehicle bodies, canopies)
|
|
69
104
|
|
|
70
105
|
A SMOOTH curved surface swept through cross-sections — what box/capsule assemblies can
|
|
@@ -283,6 +318,23 @@ demo into a commercial-looking outdoor scene:
|
|
|
283
318
|
dreamier), `strength` how strongly the blurred glow is added back (default
|
|
284
319
|
0.85; ~1.2 = neon night). One extra half-res blur pass per frame — cheap.
|
|
285
320
|
Caustics/clouds composites take precedence (pipelines don't stack yet).
|
|
321
|
+
- `post`: the color-grade tail — `{ "vignette"?, "saturation"?, "contrast"? }`.
|
|
322
|
+
`vignette` 0..1 darkens the corners (0.3 = cinematic, 0.6 = heavy mood);
|
|
323
|
+
`saturation` 0..4 (0 = grayscale, 1 = neutral, 1.2 = vivid); `contrast`
|
|
324
|
+
0.2..3 around mid-gray (1 = neutral). Applied after tone mapping on the same
|
|
325
|
+
composite pass as bloom — declaring `post` WITHOUT `bloom` costs just one
|
|
326
|
+
offscreen pass. The fastest way to give a scene a LOOK: dusk = vignette 0.35
|
|
327
|
+
+ saturation 0.85, candy = saturation 1.25 + contrast 1.08. Caustics/clouds
|
|
328
|
+
composites take precedence (pipelines don't stack yet).
|
|
329
|
+
- **Runtime changes**: the renderer re-reads `environment` every frame — call
|
|
330
|
+
`setEnvironment3D(engine, { sky: { elevationDeg: 4 }, exposure: 0.8 })`
|
|
331
|
+
(from `incanto/3d`) to change the look LIVE. One-level deep merge, `null`
|
|
332
|
+
deletes a key, and a bad patch throws in YOUR call stack, never mid-render.
|
|
333
|
+
For a full day/night loop, attach the built-in `DayNight` behavior instead:
|
|
334
|
+
`{ "script": { "name": "DayNight", "props": { "daySeconds": 240 } } }` —
|
|
335
|
+
sun arc + ambient/exposure ramps + a `dayPhaseChanged(dawn|day|dusk|night)`
|
|
336
|
+
signal to hook spawners/music to. Its `hour` field is writable (jump to a
|
|
337
|
+
time, or `paused: true` and animate it yourself).
|
|
286
338
|
- `shadows`: `true` | `false` | `{ "mapSize": 1024|2048, "radius": n }`.
|
|
287
339
|
Truthy makes the main DirectionalLight cast (PCF soft, terrain + trees +
|
|
288
340
|
meshes cast/receive, mesh grass RECEIVES only). One static ortho box (±75 m
|
|
@@ -388,6 +440,37 @@ from a `seed`, with `heightAt(x, z)` for spawning/placement and a
|
|
|
388
440
|
`StaticBody3D`). Full themes table, custom layer format and the water/beach
|
|
389
441
|
pairing live in the **incanto-environment** skill.
|
|
390
442
|
|
|
443
|
+
### Navigation on terrain (`buildTerrainNav` + findPath)
|
|
444
|
+
|
|
445
|
+
Enemies that chase across an open world need paths that respect the surface —
|
|
446
|
+
`buildTerrainNav` samples the heightfield into a walkable grid for the core
|
|
447
|
+
A* (`findPath`), and lifts waypoints back ONTO the surface:
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
import { buildTerrainNav } from 'incanto/3d';
|
|
451
|
+
import { findPath } from 'incanto';
|
|
452
|
+
|
|
453
|
+
const nav = buildTerrainNav(terrain, {
|
|
454
|
+
cellSize: 2, // meters per cell (character-scale corridors)
|
|
455
|
+
maxSlopeDeg: 40, // steeper cells are unwalkable
|
|
456
|
+
minHeight: 0.5, // below sea level = unwalkable (pair with your Water3D y)
|
|
457
|
+
});
|
|
458
|
+
const cells = findPath(nav.grid, nav.toCell(ex, ez), nav.toCell(px, pz));
|
|
459
|
+
if (cells) follow.setPath(cells.map(([cx, cy]) => nav.toWorld(cx, cy)));
|
|
460
|
+
// nav.toWorld's y IS the terrain height — PathFollow rides the surface.
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
- Build it ONCE per scene (onReady) — sampling is O(cells); re-pathing with
|
|
464
|
+
`findPath` per second is cheap (binary-heap A*).
|
|
465
|
+
- Trees/rocks/buildings aren't in the heightfield: stamp them out with
|
|
466
|
+
`nav.setSolid(...nav.toCell(x, z))` after building.
|
|
467
|
+
- Walkability is judged inside each cell (corner samples), so a flat trail
|
|
468
|
+
beside a cliff stays walkable — only genuinely steep cells go solid.
|
|
469
|
+
- **See what the enemies see**: `root.addChild(createNavDebugNode(nav))`
|
|
470
|
+
paints every walkable cell as a translucent quad on the surface (one
|
|
471
|
+
instanced draw call) — add while tuning `cellSize`/`maxSlopeDeg`, drop when
|
|
472
|
+
the routes look right.
|
|
473
|
+
|
|
391
474
|
## Performance levers for big scenes
|
|
392
475
|
|
|
393
476
|
- **`static: true`** (any Node3D): the renderer syncs that subtree ONCE and
|
package/skills/incanto-editor.md
CHANGED
|
@@ -119,9 +119,9 @@ active brush. The palette chips offer every char already used in `cells`, the
|
|
|
119
119
|
single char (digits map straight to atlas tile indices, other chars need a
|
|
120
120
|
`legend` entry). The grid grows right/down automatically when you paint past
|
|
121
121
|
the edge — to grow left/up, move the node instead (cell (0,0) hangs on the
|
|
122
|
-
node origin). Merged solid colliders re-derive live as you paint.
|
|
123
|
-
|
|
124
|
-
click-select back.
|
|
122
|
+
node origin). Merged solid colliders re-derive live as you paint. A whole
|
|
123
|
+
drag stroke is ONE undo step; toggle paint off (or select another node) to
|
|
124
|
+
get normal click-select back.
|
|
125
125
|
|
|
126
126
|
## Generate environments
|
|
127
127
|
|
|
@@ -749,7 +749,29 @@ Multi-scene games: `flow.goToScene(nextSceneJson, { fadeSeconds: 0.4 })`
|
|
|
749
749
|
fades to black, swaps, fades back (headless = instant). Title → level →
|
|
750
750
|
next level is three JSON files and this one call.
|
|
751
751
|
|
|
752
|
-
##
|
|
752
|
+
## DayNight (live 24-hour cycle)
|
|
753
|
+
|
|
754
|
+
Drives the scene `environment` (needs an atmosphere sky): sun elevation/
|
|
755
|
+
azimuth arc, ambient + exposure ramps with a moonlight floor.
|
|
756
|
+
|
|
757
|
+
```jsonc
|
|
758
|
+
{ "script": { "name": "DayNight", "props": { "daySeconds": 240, "startHour": 9 } } }
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
| Prop | Default | Notes |
|
|
762
|
+
|---|---|---|
|
|
763
|
+
| `daySeconds` | 240 | real seconds per full day |
|
|
764
|
+
| `startHour` | 10 | 0-24 clock at scene start |
|
|
765
|
+
| `maxSunElevationDeg` | 55 | noon height |
|
|
766
|
+
| `nightDarkness` | 0.75 | 0 = night looks like day, 1 = black |
|
|
767
|
+
| `paused` | false | freeze the clock; write `hour` yourself |
|
|
768
|
+
|
|
769
|
+
Signals: `dayPhaseChanged('dawn'|'day'|'dusk'|'night')` on transitions — hook
|
|
770
|
+
spawners, music, or monster aggression to it. The `hour` field (0-24) is
|
|
771
|
+
writable: jump to a time of day in one assignment. For one-off look changes
|
|
772
|
+
use `setEnvironment3D(engine, patch)` from `incanto/3d` directly.
|
|
773
|
+
|
|
774
|
+
## Boot loading overlay## Boot loading overlay
|
|
753
775
|
|
|
754
776
|
```ts
|
|
755
777
|
import { preloadSceneAssets } from 'incanto';
|
|
@@ -144,6 +144,24 @@ Signals: `finished`
|
|
|
144
144
|
| `target` | `""` | string |
|
|
145
145
|
| `bone` | `""` | string |
|
|
146
146
|
|
|
147
|
+
## `BoneLookAt3D` — `incanto/3d`
|
|
148
|
+
|
|
149
|
+
| Prop | Default | Kind |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `position` | `[0,0,0]` | array |
|
|
152
|
+
| `rotation` | `[0,0,0]` | array |
|
|
153
|
+
| `static` | `false` | boolean |
|
|
154
|
+
| `scale` | `[1,1,1]` | array |
|
|
155
|
+
| `visible` | `true` | boolean |
|
|
156
|
+
| `renderOrder` | `0` | number |
|
|
157
|
+
| `orderGroup` | `"default"` | one of: `background` `terrain` `default` `characters` `effects` `overlay` |
|
|
158
|
+
| `target` | `""` | string |
|
|
159
|
+
| `bone` | `"Head"` | string |
|
|
160
|
+
| `lookAt` | `""` | string |
|
|
161
|
+
| `maxAngleDeg` | `75` | number |
|
|
162
|
+
| `weight` | `0.85` | number |
|
|
163
|
+
| `forwardAxis` | `"+z"` | one of: `+x` `-x` `+y` `-y` `+z` `-z` |
|
|
164
|
+
|
|
147
165
|
## `Camera2D` — `incanto/2d`
|
|
148
166
|
|
|
149
167
|
| Prop | Default | Kind |
|
|
@@ -354,6 +372,24 @@ Signals: `movementStateChanged(state)`
|
|
|
354
372
|
| `zIndex` | `100` | number |
|
|
355
373
|
| `visible` | `true` | boolean |
|
|
356
374
|
|
|
375
|
+
## `InstancedMesh3D` — `incanto/3d`
|
|
376
|
+
|
|
377
|
+
| Prop | Default | Kind |
|
|
378
|
+
|---|---|---|
|
|
379
|
+
| `position` | `[0,0,0]` | array |
|
|
380
|
+
| `rotation` | `[0,0,0]` | array |
|
|
381
|
+
| `static` | `false` | boolean |
|
|
382
|
+
| `scale` | `[1,1,1]` | array |
|
|
383
|
+
| `visible` | `true` | boolean |
|
|
384
|
+
| `renderOrder` | `0` | number |
|
|
385
|
+
| `orderGroup` | `"default"` | one of: `background` `terrain` `default` `characters` `effects` `overlay` |
|
|
386
|
+
| `mesh` | `"box"` | one of: `box` `sphere` `capsule` `plane` `cylinder` `gem` |
|
|
387
|
+
| `size` | `[1,1,1]` | array |
|
|
388
|
+
| `material` | `{}` | object |
|
|
389
|
+
| `transforms` | `[]` | array |
|
|
390
|
+
| `castShadow` | `false` | boolean |
|
|
391
|
+
| `receiveShadow` | `false` | boolean |
|
|
392
|
+
|
|
357
393
|
## `Joint2D` — `incanto/2d`
|
|
358
394
|
|
|
359
395
|
| Prop | Default | Kind |
|
|
@@ -460,6 +496,9 @@ Signals: `movementStateChanged(state)`
|
|
|
460
496
|
| `targetHeight` | `0` | number |
|
|
461
497
|
| `castShadow` | `false` | boolean |
|
|
462
498
|
| `animationLoop` | `true` | boolean |
|
|
499
|
+
| `animationUpper` | `""` | string |
|
|
500
|
+
| `animationUpperLoop` | `false` | boolean |
|
|
501
|
+
| `upperBodyRoot` | `"Spine"` | string |
|
|
463
502
|
| `receiveShadow` | `false` | boolean |
|
|
464
503
|
| `tint` | `""` | string |
|
|
465
504
|
| `metalness` | `-1` | number |
|
|
@@ -961,6 +1000,18 @@ Signals: `totalChanged(total)`
|
|
|
961
1000
|
|
|
962
1001
|
Signals: `dealtDamage(amount, target)`
|
|
963
1002
|
|
|
1003
|
+
### `DayNight`
|
|
1004
|
+
|
|
1005
|
+
| Prop | Default | Kind |
|
|
1006
|
+
|---|---|---|
|
|
1007
|
+
| `daySeconds` | `240` | number |
|
|
1008
|
+
| `startHour` | `10` | number |
|
|
1009
|
+
| `maxSunElevationDeg` | `55` | number |
|
|
1010
|
+
| `nightDarkness` | `0.75` | number |
|
|
1011
|
+
| `paused` | `false` | boolean |
|
|
1012
|
+
|
|
1013
|
+
Signals: `dayPhaseChanged`
|
|
1014
|
+
|
|
964
1015
|
### `FollowCamera`
|
|
965
1016
|
|
|
966
1017
|
| Prop | Default | Kind |
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Beacon Isle — Context
|
|
2
|
+
|
|
3
|
+
**The incanto 3D template.** A complete island action-adventure vertical
|
|
4
|
+
slice designed to be COPIED as the starting point for 3D vibe-coding: a
|
|
5
|
+
generated world, a real quest loop, enemies with terrain-aware AI, readable
|
|
6
|
+
combat, win AND lose states, mobile controls, and a headless verify harness —
|
|
7
|
+
all in ~2 files of game code.
|
|
8
|
+
|
|
9
|
+
## What it plays like
|
|
10
|
+
|
|
11
|
+
You wash up on a golden-hour island. The lighthouse keeper (walk up, **E**)
|
|
12
|
+
asks you to relight three ancient wards. Shades — floating wraiths with
|
|
13
|
+
burning eyes — patrol each ward and HUNT you across the island surface when
|
|
14
|
+
you come close. Cut them down (**click/F**, a real melee swing with the sword
|
|
15
|
+
riding your hand bone), stand at the dark crystal and press **E**: it blazes
|
|
16
|
+
back to life. Three lit wards → report back → fireworks over the lighthouse.
|
|
17
|
+
The shades bite; at 0 HP the isle takes you and you respawn — lit wards stay lit.
|
|
18
|
+
|
|
19
|
+
## The template story (why it's built this way)
|
|
20
|
+
|
|
21
|
+
1. **The world is GENERATED, the game is AUTHORED.** `bun run world` re-emits
|
|
22
|
+
`src/game.scene.json` from `generate-world.ts` (deterministic seeds):
|
|
23
|
+
island terrain, sea, tree groves, grass, flowers, and QUEST SITES picked on
|
|
24
|
+
provably walkable ground (the same `buildTerrainNav` grid the enemies use).
|
|
25
|
+
Change the seed → new island, game logic untouched.
|
|
26
|
+
2. **Game code is two behaviors** (`IsleDirector`, `SwordStrike`); everything
|
|
27
|
+
else — movement/camera/animations, NPC proximity, dialogue, HUD, touch
|
|
28
|
+
controls, contact damage, physics — is engine surface declared in JSON.
|
|
29
|
+
3. **Verified headlessly**: `bun run verify` plays the whole quest, proves the
|
|
30
|
+
shades hunt, proves death keeps progress, and replays recorded input
|
|
31
|
+
bit-identically.
|
|
32
|
+
|
|
33
|
+
## Feature map (engine 0.12)
|
|
34
|
+
|
|
35
|
+
| Feature | Where |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `generateTerrain` island + Water3D sea | generate-world.ts |
|
|
38
|
+
| `buildTerrainNav` + `findPath` | ward-site picking AND shade patrol/hunt AI |
|
|
39
|
+
| `BoneAttachment3D` | sword in the right hand |
|
|
40
|
+
| `environment.post` + bloom | golden-hour grade, glowing ward crystals |
|
|
41
|
+
| input-map `"touch"` keys | joystick + buttons on phones, zero code |
|
|
42
|
+
| `UiDialogue`/`UiBar`/`UiBanner` | keeper quest, HP, win/lose |
|
|
43
|
+
| `startVoice('wind')` | speed-reactive ambience |
|
|
44
|
+
| `startRecording`/`replay` | determinism regression in verify.ts |
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Beacon Isle — Requirements
|
|
2
|
+
|
|
3
|
+
1. Quest arc: keeper dialogue (choice) → 3 wards guarded by shades → clear +
|
|
4
|
+
E to relight each → report back → win banner + fireworks.
|
|
5
|
+
2. Shades: patrol rings AROUND their ward on the island surface; hunt the
|
|
6
|
+
player within 9 m (re-path through terrain, never through sea/cliffs);
|
|
7
|
+
bites drain 20 HP per 1.2 s of contact.
|
|
8
|
+
3. Losing: 0 HP → banner → fresh respawn; lit wards persist.
|
|
9
|
+
4. A dark ward with living guards refuses to light (hint line).
|
|
10
|
+
5. One strike fells a shade (dissolve poof); the sword rides the hand bone
|
|
11
|
+
and plays the melee clip.
|
|
12
|
+
6. Works on phones: joystick + jump/interact/attack touch buttons from the
|
|
13
|
+
input map alone.
|
|
14
|
+
7. `bun run verify` green: audit 0 warnings, quest E2E, hunt proof,
|
|
15
|
+
death/respawn proof, bit-identical replay.
|
|
16
|
+
8. `bun run world` regenerates the island deterministically.
|