incanto 0.12.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 +194 -71
- package/dist/3d.js +74 -4
- package/dist/{behavior-Bod1AyJS.d.ts → behavior-CPibUfnH.d.ts} +4 -2
- package/dist/{create-game-C_xf4gm9.js → create-game-Ct86zczq.js} +5 -5
- package/dist/{create-game-B2fewJUy.js → create-game-J6g4QInv.js} +5 -5
- 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 -3
- 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/{pathfinding-BSiXjd6W.d.ts → pathfinding-RWYkNKx9.d.ts} +1 -1
- package/dist/{physics-2d-B6D864oZ.js → physics-2d-BiIdl51r.js} +2 -2
- package/dist/{physics-3d-RLNaxRyw.js → physics-3d-DhaAaKJo.js} +2 -2
- 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-D_4QY8Aq.js → agent8-BF8Jfbkl.js} +1 -1
- package/editor/assets/{index-s0DZuroe.js → index-DSq0gdhE.js} +74 -74
- package/editor/index.html +1 -1
- package/package.json +4 -2
- package/schemas/scene.schema.json +263 -0
- package/skills/incanto-3d-character.md +35 -0
- package/skills/incanto-building-3d-games.md +48 -0
- 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:
|
|
@@ -149,6 +168,22 @@ A sword in the hand, a hat on the head, sparks on a wingtip — parent them to a
|
|
|
149
168
|
- `model.boneNames()` lists every bone at runtime when you need to hunt one
|
|
150
169
|
(the editor's inspector offers the same list as a dropdown on the `bone` prop).
|
|
151
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.
|
|
186
|
+
|
|
152
187
|
## Facing a direction in 3D — the +Z-FORWARD rule (read before turning ANY skin)
|
|
153
188
|
|
|
154
189
|
This trips people up REPEATEDLY, so here is the one rule. agent8's `base-model` (and
|
|
@@ -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
|
|
@@ -291,6 +326,15 @@ demo into a commercial-looking outdoor scene:
|
|
|
291
326
|
offscreen pass. The fastest way to give a scene a LOOK: dusk = vignette 0.35
|
|
292
327
|
+ saturation 0.85, candy = saturation 1.25 + contrast 1.08. Caustics/clouds
|
|
293
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).
|
|
294
338
|
- `shadows`: `true` | `false` | `{ "mapSize": 1024|2048, "radius": n }`.
|
|
295
339
|
Truthy makes the main DirectionalLight cast (PCF soft, terrain + trees +
|
|
296
340
|
meshes cast/receive, mesh grass RECEIVES only). One static ortho box (±75 m
|
|
@@ -422,6 +466,10 @@ if (cells) follow.setPath(cells.map(([cx, cy]) => nav.toWorld(cx, cy)));
|
|
|
422
466
|
`nav.setSolid(...nav.toCell(x, z))` after building.
|
|
423
467
|
- Walkability is judged inside each cell (corner samples), so a flat trail
|
|
424
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.
|
|
425
473
|
|
|
426
474
|
## Performance levers for big scenes
|
|
427
475
|
|
|
@@ -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.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Beacon Isle — Status
|
|
2
|
+
|
|
3
|
+
## Round 2 (2026-07-12)
|
|
4
|
+
|
|
5
|
+
- **The Shadeheart**: lighting the third ward summons a glowing violet
|
|
6
|
+
colossus (primitive-built — a keeper.glb attempt rendered at zero scale,
|
|
7
|
+
see Learnings below) that hunts the player across the island via the
|
|
8
|
+
terrain nav; five strikes (recoil + eye-flare per hit) fell it in a triple
|
|
9
|
+
burst; only then does the keeper's report end the quest.
|
|
10
|
+
- **The clock IS the quest** (paused DayNight, director-lerped `hour`):
|
|
11
|
+
dusk start → each lit ward buys back light → the boss drags the isle to a
|
|
12
|
+
deep-red horizon (17.8 — sun kept ABOVE the horizon; below it the
|
|
13
|
+
atmosphere sky and its IBL go black) → victory brings morning.
|
|
14
|
+
- **Gliding**: hold JUMP while falling — capped descent + dash trail.
|
|
15
|
+
- Keeper NPC tracks the player with `BoneLookAt3D`; shoreline rocks are one
|
|
16
|
+
`InstancedMesh3D` draw call.
|
|
17
|
+
|
|
18
|
+
## Round 1 (2026-07-11)
|
|
19
|
+
|
|
20
|
+
- Full quest loop, terrain-nav shade AI, bone-mounted sword, golden-hour
|
|
21
|
+
grade, headless verify + replay determinism, 4-iteration visual pass.
|
|
22
|
+
|
|
23
|
+
## Learnings
|
|
24
|
+
|
|
25
|
+
- `PathFollow` writes WORLD coordinates into `node.position` — such nodes
|
|
26
|
+
must be ROOT children. Parenting one under a positioned wrapper doubles
|
|
27
|
+
the offset ON SCREEN while all position-based gameplay math stays
|
|
28
|
+
self-consistent: invisible-boss bugs that verify green.
|
|
29
|
+
- keeper.glb mounts at a degenerate scale through ModelInstance3D's
|
|
30
|
+
targetHeight fit (the known "explicit scale" trap) — primitives with
|
|
31
|
+
emissive materials are the reliable monster path.
|
|
32
|
+
- Atmosphere rule: keep `elevationDeg` (or DayNight `hour`) sun-above-horizon
|
|
33
|
+
in playable scenes; the sky + IBL die within ~1° below.
|
|
34
|
+
|
|
35
|
+
## Ideas next
|
|
36
|
+
|
|
37
|
+
- Boss attack telegraphs (charge-up glow, ground slam particles).
|
|
38
|
+
- A hurt/attack clip for shades once one lands in the catalog.
|