incanto 0.36.2 → 0.38.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/bin/incanto-feel.mjs +93 -0
- package/bin/incanto-playtest.mjs +136 -0
- package/dist/2d.d.ts +2 -2
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +44 -3
- package/dist/3d.js +4 -32
- package/dist/{behavior-DibCwrW7.d.ts → behavior-B5PJ32eu.d.ts} +106 -6
- package/dist/{create-game-D-StzrTj.js → create-game-C4t5gVhc.js} +5 -5
- package/dist/{create-game-DnOu2aAa.js → create-game-DqY2lMBc.js} +783 -689
- package/dist/debug.d.ts +1 -1
- package/dist/{duplicate-Cvb1BSca.js → duplicate-CRtihGmC.js} +1 -1
- package/dist/editor.js +21 -0
- package/dist/{environment-presets-fK9oyrrR.js → environment-presets-C0Axpfth.js} +75 -9
- package/dist/{gameplay-C0MUMSak.js → gameplay-CsJDUQh_.js} +2 -2
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +4 -50
- package/dist/index.js +5 -75
- package/dist/{loader-BqigZsfF.d.ts → loader-DSMdqasu.d.ts} +1 -1
- package/dist/{loader-DAZlgqeC.js → loader-r49nDwB4.js} +15 -0
- package/dist/net.d.ts +1 -1
- package/dist/net.js +3 -3
- package/dist/{pathfinding-B3QtdhvZ.d.ts → pathfinding-BrE5Uec6.d.ts} +1 -1
- package/dist/{physics-2d-B4v39hca.js → physics-2d-OfKb0PV_.js} +2 -2
- package/dist/{physics-3d-DFJVMYET.js → physics-3d-C8242nXr.js} +3 -3
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-DXwtIVqP.js → register-B4RRQbyq.js} +2 -2
- package/dist/{register-BHAwM4bK.js → register-Dm7nrd-E.js} +322 -34
- package/dist/{register-COGCNitr.js → register-DnB9m7Rl.js} +2 -2
- package/dist/replay-Cxrr-ApH.d.ts +86 -0
- package/dist/{audit-D7F3n3Nt.js → replay-DH0UOuZ-.js} +72 -2
- package/dist/test-CoDPWx-m.js +1404 -0
- package/dist/test.d.ts +113 -4
- package/dist/test.js +3 -3
- package/dist/vite.js +1 -1
- package/editor/assets/{agent8-DMvBVhe4.js → agent8-BtIlblPm.js} +1 -1
- package/editor/assets/{debug-BgAq55oC.js → debug-BvTmHXI7.js} +1 -1
- package/editor/assets/{index-Cy_QsKbh.js → index-BS01WWgM.js} +91 -91
- package/editor/index.html +1 -1
- package/package.json +3 -1
- package/schemas/scene.schema.json +318 -0
- package/skills/incanto-3d-character.md +35 -0
- package/skills/incanto-building-2d-games.md +6 -0
- package/skills/incanto-building-3d-games.md +6 -0
- package/skills/incanto-game-feel.md +76 -0
- package/skills/incanto-node-reference.md +51 -0
- package/skills/incanto-performance.md +212 -0
- package/skills/incanto-playtesting.md +143 -0
- package/skills/incanto-verifying-your-game.md +41 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/village.scene.json +3 -1
- package/templates-app/village-quest-3d/verify.ts +55 -5
- package/dist/errors-DGRtWlSx.d.ts +0 -39
- package/dist/test-X_xE6Yay.js +0 -642
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-BS01WWgM.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/GameServer-C56iOUgF.js">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "incanto",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "Vibe-coding-first web game engine SDK — JSON-driven scenes on three.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"game-engine",
|
|
@@ -95,6 +95,8 @@
|
|
|
95
95
|
"incanto-assets": "bin/incanto-assets.mjs",
|
|
96
96
|
"incanto-env": "bin/incanto-env.mjs",
|
|
97
97
|
"incanto-play": "bin/incanto-play.mjs",
|
|
98
|
+
"incanto-playtest": "bin/incanto-playtest.mjs",
|
|
99
|
+
"incanto-feel": "bin/incanto-feel.mjs",
|
|
98
100
|
"incanto-new": "bin/incanto-new.mjs"
|
|
99
101
|
}
|
|
100
102
|
}
|
|
@@ -343,6 +343,9 @@
|
|
|
343
343
|
{
|
|
344
344
|
"$ref": "#/$defs/UiDialogue"
|
|
345
345
|
},
|
|
346
|
+
{
|
|
347
|
+
"$ref": "#/$defs/UiFrameCapSelect"
|
|
348
|
+
},
|
|
346
349
|
{
|
|
347
350
|
"$ref": "#/$defs/UiImage"
|
|
348
351
|
},
|
|
@@ -352,6 +355,12 @@
|
|
|
352
355
|
{
|
|
353
356
|
"$ref": "#/$defs/UiPanel"
|
|
354
357
|
},
|
|
358
|
+
{
|
|
359
|
+
"$ref": "#/$defs/UiQualitySelect"
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
"$ref": "#/$defs/UiRenderScaleSelect"
|
|
363
|
+
},
|
|
355
364
|
{
|
|
356
365
|
"$ref": "#/$defs/UiSelect"
|
|
357
366
|
},
|
|
@@ -2054,6 +2063,30 @@
|
|
|
2054
2063
|
"type": "number",
|
|
2055
2064
|
"default": 1.2
|
|
2056
2065
|
},
|
|
2066
|
+
"coyoteSeconds": {
|
|
2067
|
+
"type": "number",
|
|
2068
|
+
"default": 0
|
|
2069
|
+
},
|
|
2070
|
+
"jumpBufferSeconds": {
|
|
2071
|
+
"type": "number",
|
|
2072
|
+
"default": 0
|
|
2073
|
+
},
|
|
2074
|
+
"jumpCutMultiplier": {
|
|
2075
|
+
"type": "number",
|
|
2076
|
+
"default": 1
|
|
2077
|
+
},
|
|
2078
|
+
"maxJumps": {
|
|
2079
|
+
"type": "number",
|
|
2080
|
+
"default": 1
|
|
2081
|
+
},
|
|
2082
|
+
"airControl": {
|
|
2083
|
+
"type": "number",
|
|
2084
|
+
"default": 0.2
|
|
2085
|
+
},
|
|
2086
|
+
"fallGravity": {
|
|
2087
|
+
"type": "number",
|
|
2088
|
+
"default": 2.5
|
|
2089
|
+
},
|
|
2057
2090
|
"floatHeight": {
|
|
2058
2091
|
"type": "number",
|
|
2059
2092
|
"default": 0.01
|
|
@@ -6536,6 +6569,101 @@
|
|
|
6536
6569
|
},
|
|
6537
6570
|
"required": ["name", "type"]
|
|
6538
6571
|
},
|
|
6572
|
+
"UiFrameCapSelect": {
|
|
6573
|
+
"type": "object",
|
|
6574
|
+
"x-signals": ["dragStarted", "dragCancelled", "droppedOn", "dropped", "changed"],
|
|
6575
|
+
"properties": {
|
|
6576
|
+
"name": {
|
|
6577
|
+
"type": "string"
|
|
6578
|
+
},
|
|
6579
|
+
"uid": {
|
|
6580
|
+
"type": "string"
|
|
6581
|
+
},
|
|
6582
|
+
"type": {
|
|
6583
|
+
"const": "UiFrameCapSelect"
|
|
6584
|
+
},
|
|
6585
|
+
"groups": {
|
|
6586
|
+
"type": "array",
|
|
6587
|
+
"items": {
|
|
6588
|
+
"type": "string"
|
|
6589
|
+
}
|
|
6590
|
+
},
|
|
6591
|
+
"tags": {
|
|
6592
|
+
"type": "object"
|
|
6593
|
+
},
|
|
6594
|
+
"props": {
|
|
6595
|
+
"type": "object",
|
|
6596
|
+
"properties": {
|
|
6597
|
+
"anchor": {
|
|
6598
|
+
"type": "string",
|
|
6599
|
+
"enum": [
|
|
6600
|
+
"topLeft",
|
|
6601
|
+
"top",
|
|
6602
|
+
"topRight",
|
|
6603
|
+
"left",
|
|
6604
|
+
"center",
|
|
6605
|
+
"right",
|
|
6606
|
+
"bottomLeft",
|
|
6607
|
+
"bottom",
|
|
6608
|
+
"bottomRight"
|
|
6609
|
+
],
|
|
6610
|
+
"default": "topLeft"
|
|
6611
|
+
},
|
|
6612
|
+
"visible": {
|
|
6613
|
+
"type": "boolean",
|
|
6614
|
+
"default": true
|
|
6615
|
+
},
|
|
6616
|
+
"focusable": {
|
|
6617
|
+
"type": "boolean",
|
|
6618
|
+
"default": true
|
|
6619
|
+
},
|
|
6620
|
+
"draggable": {
|
|
6621
|
+
"type": "boolean",
|
|
6622
|
+
"default": false
|
|
6623
|
+
},
|
|
6624
|
+
"dropTarget": {
|
|
6625
|
+
"type": "boolean",
|
|
6626
|
+
"default": false
|
|
6627
|
+
},
|
|
6628
|
+
"label": {
|
|
6629
|
+
"type": "string",
|
|
6630
|
+
"default": "Frame rate"
|
|
6631
|
+
},
|
|
6632
|
+
"options": {
|
|
6633
|
+
"type": "string",
|
|
6634
|
+
"default": "30,60,120,0"
|
|
6635
|
+
},
|
|
6636
|
+
"value": {
|
|
6637
|
+
"type": "string",
|
|
6638
|
+
"default": ""
|
|
6639
|
+
}
|
|
6640
|
+
},
|
|
6641
|
+
"additionalProperties": false
|
|
6642
|
+
},
|
|
6643
|
+
"script": {
|
|
6644
|
+
"type": "object",
|
|
6645
|
+
"properties": {
|
|
6646
|
+
"name": {
|
|
6647
|
+
"type": "string"
|
|
6648
|
+
},
|
|
6649
|
+
"props": {
|
|
6650
|
+
"type": "object"
|
|
6651
|
+
}
|
|
6652
|
+
},
|
|
6653
|
+
"required": ["name"]
|
|
6654
|
+
},
|
|
6655
|
+
"network": {
|
|
6656
|
+
"type": "object"
|
|
6657
|
+
},
|
|
6658
|
+
"children": {
|
|
6659
|
+
"type": "array",
|
|
6660
|
+
"items": {
|
|
6661
|
+
"$ref": "#/$defs/node"
|
|
6662
|
+
}
|
|
6663
|
+
}
|
|
6664
|
+
},
|
|
6665
|
+
"required": ["name", "type"]
|
|
6666
|
+
},
|
|
6539
6667
|
"UiImage": {
|
|
6540
6668
|
"type": "object",
|
|
6541
6669
|
"x-signals": ["dragStarted", "dragCancelled", "droppedOn", "dropped"],
|
|
@@ -6859,6 +6987,196 @@
|
|
|
6859
6987
|
},
|
|
6860
6988
|
"required": ["name", "type"]
|
|
6861
6989
|
},
|
|
6990
|
+
"UiQualitySelect": {
|
|
6991
|
+
"type": "object",
|
|
6992
|
+
"x-signals": ["dragStarted", "dragCancelled", "droppedOn", "dropped", "changed"],
|
|
6993
|
+
"properties": {
|
|
6994
|
+
"name": {
|
|
6995
|
+
"type": "string"
|
|
6996
|
+
},
|
|
6997
|
+
"uid": {
|
|
6998
|
+
"type": "string"
|
|
6999
|
+
},
|
|
7000
|
+
"type": {
|
|
7001
|
+
"const": "UiQualitySelect"
|
|
7002
|
+
},
|
|
7003
|
+
"groups": {
|
|
7004
|
+
"type": "array",
|
|
7005
|
+
"items": {
|
|
7006
|
+
"type": "string"
|
|
7007
|
+
}
|
|
7008
|
+
},
|
|
7009
|
+
"tags": {
|
|
7010
|
+
"type": "object"
|
|
7011
|
+
},
|
|
7012
|
+
"props": {
|
|
7013
|
+
"type": "object",
|
|
7014
|
+
"properties": {
|
|
7015
|
+
"anchor": {
|
|
7016
|
+
"type": "string",
|
|
7017
|
+
"enum": [
|
|
7018
|
+
"topLeft",
|
|
7019
|
+
"top",
|
|
7020
|
+
"topRight",
|
|
7021
|
+
"left",
|
|
7022
|
+
"center",
|
|
7023
|
+
"right",
|
|
7024
|
+
"bottomLeft",
|
|
7025
|
+
"bottom",
|
|
7026
|
+
"bottomRight"
|
|
7027
|
+
],
|
|
7028
|
+
"default": "topLeft"
|
|
7029
|
+
},
|
|
7030
|
+
"visible": {
|
|
7031
|
+
"type": "boolean",
|
|
7032
|
+
"default": true
|
|
7033
|
+
},
|
|
7034
|
+
"focusable": {
|
|
7035
|
+
"type": "boolean",
|
|
7036
|
+
"default": true
|
|
7037
|
+
},
|
|
7038
|
+
"draggable": {
|
|
7039
|
+
"type": "boolean",
|
|
7040
|
+
"default": false
|
|
7041
|
+
},
|
|
7042
|
+
"dropTarget": {
|
|
7043
|
+
"type": "boolean",
|
|
7044
|
+
"default": false
|
|
7045
|
+
},
|
|
7046
|
+
"label": {
|
|
7047
|
+
"type": "string",
|
|
7048
|
+
"default": "Quality"
|
|
7049
|
+
},
|
|
7050
|
+
"options": {
|
|
7051
|
+
"type": "string",
|
|
7052
|
+
"default": "low,medium,high"
|
|
7053
|
+
},
|
|
7054
|
+
"value": {
|
|
7055
|
+
"type": "string",
|
|
7056
|
+
"default": ""
|
|
7057
|
+
}
|
|
7058
|
+
},
|
|
7059
|
+
"additionalProperties": false
|
|
7060
|
+
},
|
|
7061
|
+
"script": {
|
|
7062
|
+
"type": "object",
|
|
7063
|
+
"properties": {
|
|
7064
|
+
"name": {
|
|
7065
|
+
"type": "string"
|
|
7066
|
+
},
|
|
7067
|
+
"props": {
|
|
7068
|
+
"type": "object"
|
|
7069
|
+
}
|
|
7070
|
+
},
|
|
7071
|
+
"required": ["name"]
|
|
7072
|
+
},
|
|
7073
|
+
"network": {
|
|
7074
|
+
"type": "object"
|
|
7075
|
+
},
|
|
7076
|
+
"children": {
|
|
7077
|
+
"type": "array",
|
|
7078
|
+
"items": {
|
|
7079
|
+
"$ref": "#/$defs/node"
|
|
7080
|
+
}
|
|
7081
|
+
}
|
|
7082
|
+
},
|
|
7083
|
+
"required": ["name", "type"]
|
|
7084
|
+
},
|
|
7085
|
+
"UiRenderScaleSelect": {
|
|
7086
|
+
"type": "object",
|
|
7087
|
+
"x-signals": ["dragStarted", "dragCancelled", "droppedOn", "dropped", "changed"],
|
|
7088
|
+
"properties": {
|
|
7089
|
+
"name": {
|
|
7090
|
+
"type": "string"
|
|
7091
|
+
},
|
|
7092
|
+
"uid": {
|
|
7093
|
+
"type": "string"
|
|
7094
|
+
},
|
|
7095
|
+
"type": {
|
|
7096
|
+
"const": "UiRenderScaleSelect"
|
|
7097
|
+
},
|
|
7098
|
+
"groups": {
|
|
7099
|
+
"type": "array",
|
|
7100
|
+
"items": {
|
|
7101
|
+
"type": "string"
|
|
7102
|
+
}
|
|
7103
|
+
},
|
|
7104
|
+
"tags": {
|
|
7105
|
+
"type": "object"
|
|
7106
|
+
},
|
|
7107
|
+
"props": {
|
|
7108
|
+
"type": "object",
|
|
7109
|
+
"properties": {
|
|
7110
|
+
"anchor": {
|
|
7111
|
+
"type": "string",
|
|
7112
|
+
"enum": [
|
|
7113
|
+
"topLeft",
|
|
7114
|
+
"top",
|
|
7115
|
+
"topRight",
|
|
7116
|
+
"left",
|
|
7117
|
+
"center",
|
|
7118
|
+
"right",
|
|
7119
|
+
"bottomLeft",
|
|
7120
|
+
"bottom",
|
|
7121
|
+
"bottomRight"
|
|
7122
|
+
],
|
|
7123
|
+
"default": "topLeft"
|
|
7124
|
+
},
|
|
7125
|
+
"visible": {
|
|
7126
|
+
"type": "boolean",
|
|
7127
|
+
"default": true
|
|
7128
|
+
},
|
|
7129
|
+
"focusable": {
|
|
7130
|
+
"type": "boolean",
|
|
7131
|
+
"default": true
|
|
7132
|
+
},
|
|
7133
|
+
"draggable": {
|
|
7134
|
+
"type": "boolean",
|
|
7135
|
+
"default": false
|
|
7136
|
+
},
|
|
7137
|
+
"dropTarget": {
|
|
7138
|
+
"type": "boolean",
|
|
7139
|
+
"default": false
|
|
7140
|
+
},
|
|
7141
|
+
"label": {
|
|
7142
|
+
"type": "string",
|
|
7143
|
+
"default": "Resolution"
|
|
7144
|
+
},
|
|
7145
|
+
"options": {
|
|
7146
|
+
"type": "string",
|
|
7147
|
+
"default": "0.5,0.75,1"
|
|
7148
|
+
},
|
|
7149
|
+
"value": {
|
|
7150
|
+
"type": "string",
|
|
7151
|
+
"default": ""
|
|
7152
|
+
}
|
|
7153
|
+
},
|
|
7154
|
+
"additionalProperties": false
|
|
7155
|
+
},
|
|
7156
|
+
"script": {
|
|
7157
|
+
"type": "object",
|
|
7158
|
+
"properties": {
|
|
7159
|
+
"name": {
|
|
7160
|
+
"type": "string"
|
|
7161
|
+
},
|
|
7162
|
+
"props": {
|
|
7163
|
+
"type": "object"
|
|
7164
|
+
}
|
|
7165
|
+
},
|
|
7166
|
+
"required": ["name"]
|
|
7167
|
+
},
|
|
7168
|
+
"network": {
|
|
7169
|
+
"type": "object"
|
|
7170
|
+
},
|
|
7171
|
+
"children": {
|
|
7172
|
+
"type": "array",
|
|
7173
|
+
"items": {
|
|
7174
|
+
"$ref": "#/$defs/node"
|
|
7175
|
+
}
|
|
7176
|
+
}
|
|
7177
|
+
},
|
|
7178
|
+
"required": ["name", "type"]
|
|
7179
|
+
},
|
|
6862
7180
|
"UiSelect": {
|
|
6863
7181
|
"type": "object",
|
|
6864
7182
|
"x-signals": ["dragStarted", "dragCancelled", "droppedOn", "dropped", "changed"],
|
|
@@ -39,6 +39,41 @@ capsule BOTTOM `floatHeight` above the ground (5-ray probe). Author the Skin
|
|
|
39
39
|
y-offset as `-(halfHeight + radius + floatHeight)` so the feet sit at the
|
|
40
40
|
capsule bottom (the example templates do this).
|
|
41
41
|
|
|
42
|
+
## Game feel — the props a jump needs to stop feeling broken
|
|
43
|
+
|
|
44
|
+
A jump that only fires while the ground ray says so feels BROKEN, and players do
|
|
45
|
+
not report it as "the coyote time is missing" — they report the game as
|
|
46
|
+
unresponsive. `CharacterController2D` got these in 0.33.0; the 3D controller
|
|
47
|
+
did not until now.
|
|
48
|
+
|
|
49
|
+
| prop | default | what it buys |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `coyoteSeconds` | `0` | jump this long AFTER walking off a ledge. **The single biggest one** — try `0.12`. |
|
|
52
|
+
| `jumpBufferSeconds` | `0` | press jump this long BEFORE landing and still get it. Try `0.15`. |
|
|
53
|
+
| `jumpCutMultiplier` | `1` | release early and the rise is cut to this fraction. `0.45` = variable-height jump. |
|
|
54
|
+
| `maxJumps` | `1` | `2` = double jump. Extra jumps work in mid-air. |
|
|
55
|
+
| `airControl` | `0.2` | how much ground control you keep airborne (0 = committed, 1 = full). |
|
|
56
|
+
| `fallGravity` | `2.5` | gravity multiplier while falling. Higher = snappier arc. |
|
|
57
|
+
|
|
58
|
+
Every one defaults to OFF (`0` / `1`) or to the previous hard-coded constant, so
|
|
59
|
+
a scene that asks for nothing behaves exactly as before.
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{ "name": "Ctl", "type": "CharacterController3D",
|
|
63
|
+
"props": { "coyoteSeconds": 0.12, "jumpBufferSeconds": 0.15,
|
|
64
|
+
"jumpCutMultiplier": 0.45, "maxJumps": 2 } }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Jump reads the button EDGE.** Holding the jump key no longer re-jumps every
|
|
68
|
+
frame — that auto-hop was never intended and it made coyote time incoherent (a
|
|
69
|
+
held button would re-fire through the whole window).
|
|
70
|
+
|
|
71
|
+
**Two ground senses, deliberately.** `grounded` stays generous (slack for the
|
|
72
|
+
hover spring, so slopes and bumps do not flicker the animation state). The feel
|
|
73
|
+
props and the airborne animation use a stricter test: close to the support AND
|
|
74
|
+
not moving upward. Without the velocity term the check is one frame stale, which
|
|
75
|
+
at launch speed is ~6 cm — enough to still read as standing on the floor.
|
|
76
|
+
|
|
42
77
|
## Views
|
|
43
78
|
|
|
44
79
|
| view | what it does | key props |
|
|
@@ -329,3 +329,9 @@ Wall moves need to know there IS a wall: `CharacterBody2D` now answers
|
|
|
329
329
|
`isOnFloor()`. Before, the floor was the only surface a character could report,
|
|
330
330
|
which is why a wall jump was not awkward to write but impossible.
|
|
331
331
|
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
**And then play it.** `bunx incanto-playtest src/game.scene.json` runs seeded
|
|
335
|
+
headless bot sessions and reports what could not be reached, which wires never
|
|
336
|
+
fired, and whether anything in the level can hurt the player. See
|
|
337
|
+
`incanto-playtesting.md`.
|
|
@@ -578,3 +578,9 @@ behind it), what lights the scene, and which colliders intersect. Then read
|
|
|
578
578
|
`stats().errors` (something threw and got skipped) and `assetErrors()` (a model
|
|
579
579
|
404'd). All four are silent failures otherwise — the screen just looks wrong,
|
|
580
580
|
or empty, and nothing throws.
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
**And then play it.** `bunx incanto-playtest src/game.scene.json` runs seeded
|
|
584
|
+
headless bot sessions and reports what could not be reached, which wires never
|
|
585
|
+
fired, and whether anything in the level can hurt the player. See
|
|
586
|
+
`incanto-playtesting.md`.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: incanto-game-feel
|
|
3
|
+
description: Measure what your controls actually feel like — bunx incanto-feel reports top speed, ramp and stop times, turnaround, jump apex and airtime, and the coyote/jump-buffer windows MEASURED by probing rather than read from the schema. Use when tuning a character, or when a game "feels off" and you cannot see why.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Game feel, as numbers
|
|
7
|
+
|
|
8
|
+
`capture()` and `framing()` are snapshots. Feel lives on the **time axis**.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
bunx incanto-feel src/game.scene.json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
feel of /Game/Player
|
|
16
|
+
|
|
17
|
+
top speed 5.62 u/s
|
|
18
|
+
to full speed 383 ms
|
|
19
|
+
to a stop 133 ms
|
|
20
|
+
turnaround 367 ms
|
|
21
|
+
input → motion 1 frame(s)
|
|
22
|
+
|
|
23
|
+
jump apex 0.262 u
|
|
24
|
+
time to apex 133 ms
|
|
25
|
+
airtime 267 ms
|
|
26
|
+
coyote window 100 ms
|
|
27
|
+
jump buffer 150 ms
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Why this exists
|
|
31
|
+
|
|
32
|
+
**`jumpVelocity: 4` is not a height.** It is an impulse, and what it produces
|
|
33
|
+
depends on gravity, mass, drag and `fallGravity`. The apex in world units and the
|
|
34
|
+
hang time in milliseconds are what you are actually choosing.
|
|
35
|
+
|
|
36
|
+
**The windows are MEASURED, not read.** `coyoteMs` comes from probing — lift the
|
|
37
|
+
character clear of its support, wait, press, see whether it launched, binary
|
|
38
|
+
search the boundary. A `coyoteSeconds: 0.12` that the controller never consults
|
|
39
|
+
reports **0 ms**. That was a real bug in this engine's own 3D controller until
|
|
40
|
+
0.37, and the JSON looked correct the entire time.
|
|
41
|
+
|
|
42
|
+
## Reading it
|
|
43
|
+
|
|
44
|
+
| line | what it tells you |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `to full speed` | acceleration. Under ~100 ms is twitchy-responsive; over ~500 ms is weighty. |
|
|
47
|
+
| `to a stop` | how much you slide. A platformer that overshoots ledges has a long one. |
|
|
48
|
+
| `turnaround` | the cost of changing your mind. The number players feel most and name least. |
|
|
49
|
+
| `input → motion` | 1 frame is immediate. More than 2 and something is buffering. |
|
|
50
|
+
| `jump apex` / `airtime` | compare against the gaps and platform heights in your level. |
|
|
51
|
+
| `coyote window` | **0 reads to players as "the controls are broken"**, not as a missing feature. |
|
|
52
|
+
| `jump buffer` | 0 throws away a press made a few frames before landing. |
|
|
53
|
+
|
|
54
|
+
## It reports, it does not grade
|
|
55
|
+
|
|
56
|
+
There are no PASS/FAIL bands here on purpose. A twitch platformer and a survival
|
|
57
|
+
horror want opposite numbers, and a table of thresholds invented by an engine
|
|
58
|
+
would be confidently wrong for most games. Two exceptions are called out because
|
|
59
|
+
they are almost never intentional: a coyote window of 0 and a jump buffer of 0.
|
|
60
|
+
|
|
61
|
+
## Tuning loop
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
bunx incanto-feel src/game.scene.json # measure
|
|
65
|
+
# edit coyoteSeconds / jumpBufferSeconds / jumpVelocity / fallGravity
|
|
66
|
+
bunx incanto-feel src/game.scene.json # measure again
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The props live on `CharacterController2D` / `CharacterController3D` — see
|
|
70
|
+
`incanto-3d-character.md`.
|
|
71
|
+
|
|
72
|
+
## Pairs with the playtester
|
|
73
|
+
|
|
74
|
+
`incanto-feel` answers *how does it move*. `incanto-playtest` answers *can it be
|
|
75
|
+
finished*. A level whose jump apex is 0.26 u and whose gaps are 2 u apart will
|
|
76
|
+
show up in the second as `never reached`.
|
|
@@ -277,6 +277,12 @@ Signals: `movementStateChanged(state)`
|
|
|
277
277
|
| `sprintMultiplier` | `2` | number |
|
|
278
278
|
| `jumpVelocity` | `4` | number |
|
|
279
279
|
| `sprintJumpMultiplier` | `1.2` | number |
|
|
280
|
+
| `coyoteSeconds` | `0` | number |
|
|
281
|
+
| `jumpBufferSeconds` | `0` | number |
|
|
282
|
+
| `jumpCutMultiplier` | `1` | number |
|
|
283
|
+
| `maxJumps` | `1` | number |
|
|
284
|
+
| `airControl` | `0.2` | number |
|
|
285
|
+
| `fallGravity` | `2.5` | number |
|
|
280
286
|
| `floatHeight` | `0.01` | number |
|
|
281
287
|
| `mouseLook` | `true` | boolean |
|
|
282
288
|
| `zoomMin` | `0` | number |
|
|
@@ -984,6 +990,21 @@ Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `presse
|
|
|
984
990
|
|
|
985
991
|
Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `lineShown` · `choiceMade` · `dialogueFinished`
|
|
986
992
|
|
|
993
|
+
## `UiFrameCapSelect` — `incanto`
|
|
994
|
+
|
|
995
|
+
| Prop | Default | Kind |
|
|
996
|
+
|---|---|---|
|
|
997
|
+
| `anchor` | `"topLeft"` | one of: `topLeft` `top` `topRight` `left` `center` `right` `bottomLeft` `bottom` `bottomRight` |
|
|
998
|
+
| `visible` | `true` | boolean |
|
|
999
|
+
| `focusable` | `true` | boolean |
|
|
1000
|
+
| `draggable` | `false` | boolean |
|
|
1001
|
+
| `dropTarget` | `false` | boolean |
|
|
1002
|
+
| `label` | `"Frame rate"` | string |
|
|
1003
|
+
| `options` | `"30,60,120,0"` | string |
|
|
1004
|
+
| `value` | `""` | string |
|
|
1005
|
+
|
|
1006
|
+
Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `changed`
|
|
1007
|
+
|
|
987
1008
|
## `UiImage` — `incanto`
|
|
988
1009
|
|
|
989
1010
|
| Prop | Default | Kind |
|
|
@@ -1038,6 +1059,36 @@ Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `change
|
|
|
1038
1059
|
|
|
1039
1060
|
Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped`
|
|
1040
1061
|
|
|
1062
|
+
## `UiQualitySelect` — `incanto`
|
|
1063
|
+
|
|
1064
|
+
| Prop | Default | Kind |
|
|
1065
|
+
|---|---|---|
|
|
1066
|
+
| `anchor` | `"topLeft"` | one of: `topLeft` `top` `topRight` `left` `center` `right` `bottomLeft` `bottom` `bottomRight` |
|
|
1067
|
+
| `visible` | `true` | boolean |
|
|
1068
|
+
| `focusable` | `true` | boolean |
|
|
1069
|
+
| `draggable` | `false` | boolean |
|
|
1070
|
+
| `dropTarget` | `false` | boolean |
|
|
1071
|
+
| `label` | `"Quality"` | string |
|
|
1072
|
+
| `options` | `"low,medium,high"` | string |
|
|
1073
|
+
| `value` | `""` | string |
|
|
1074
|
+
|
|
1075
|
+
Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `changed`
|
|
1076
|
+
|
|
1077
|
+
## `UiRenderScaleSelect` — `incanto`
|
|
1078
|
+
|
|
1079
|
+
| Prop | Default | Kind |
|
|
1080
|
+
|---|---|---|
|
|
1081
|
+
| `anchor` | `"topLeft"` | one of: `topLeft` `top` `topRight` `left` `center` `right` `bottomLeft` `bottom` `bottomRight` |
|
|
1082
|
+
| `visible` | `true` | boolean |
|
|
1083
|
+
| `focusable` | `true` | boolean |
|
|
1084
|
+
| `draggable` | `false` | boolean |
|
|
1085
|
+
| `dropTarget` | `false` | boolean |
|
|
1086
|
+
| `label` | `"Resolution"` | string |
|
|
1087
|
+
| `options` | `"0.5,0.75,1"` | string |
|
|
1088
|
+
| `value` | `""` | string |
|
|
1089
|
+
|
|
1090
|
+
Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `changed`
|
|
1091
|
+
|
|
1041
1092
|
## `UiSelect` — `incanto`
|
|
1042
1093
|
|
|
1043
1094
|
| Prop | Default | Kind |
|