incanto 0.3.3 → 0.4.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/dist/3d.d.ts +68 -7
- package/dist/3d.js +4 -4
- package/dist/{create-game-DKdR28SI.js → create-game-DlTZUCSj.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{physics-3d-DXODbpY1.js → physics-3d-Yvle9Clx.js} +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-CljkZG08.js → register-9mSsSUS1.js} +119 -20
- package/dist/test.js +3 -3
- package/editor/assets/{agent8-CXBTOrkj.js → agent8-B-6dWaK9.js} +1 -1
- package/editor/assets/{index-DMghTAy4.js → index-BN0hHg3U.js} +36 -36
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +94 -0
- package/skills/incanto-building-3d-games.md +49 -1
- package/skills/incanto-environment.md +1 -1
- package/skills/incanto-node-reference.md +11 -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-BN0hHg3U.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/GameServer-C56iOUgF.js">
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-DF3tMeKJ.css">
|
|
11
11
|
</head>
|
package/package.json
CHANGED
|
@@ -187,6 +187,9 @@
|
|
|
187
187
|
{
|
|
188
188
|
"$ref": "#/$defs/AudioPlayer"
|
|
189
189
|
},
|
|
190
|
+
{
|
|
191
|
+
"$ref": "#/$defs/Billboard3D"
|
|
192
|
+
},
|
|
190
193
|
{
|
|
191
194
|
"$ref": "#/$defs/Camera2D"
|
|
192
195
|
},
|
|
@@ -918,6 +921,97 @@
|
|
|
918
921
|
},
|
|
919
922
|
"required": ["name", "type"]
|
|
920
923
|
},
|
|
924
|
+
"Billboard3D": {
|
|
925
|
+
"type": "object",
|
|
926
|
+
"properties": {
|
|
927
|
+
"name": {
|
|
928
|
+
"type": "string"
|
|
929
|
+
},
|
|
930
|
+
"uid": {
|
|
931
|
+
"type": "string"
|
|
932
|
+
},
|
|
933
|
+
"type": {
|
|
934
|
+
"const": "Billboard3D"
|
|
935
|
+
},
|
|
936
|
+
"groups": {
|
|
937
|
+
"type": "array",
|
|
938
|
+
"items": {
|
|
939
|
+
"type": "string"
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
"tags": {
|
|
943
|
+
"type": "object"
|
|
944
|
+
},
|
|
945
|
+
"props": {
|
|
946
|
+
"type": "object",
|
|
947
|
+
"properties": {
|
|
948
|
+
"position": {
|
|
949
|
+
"type": "array",
|
|
950
|
+
"items": {
|
|
951
|
+
"type": "number"
|
|
952
|
+
},
|
|
953
|
+
"minItems": 3,
|
|
954
|
+
"maxItems": 3,
|
|
955
|
+
"default": [0, 0, 0]
|
|
956
|
+
},
|
|
957
|
+
"rotation": {
|
|
958
|
+
"type": "array",
|
|
959
|
+
"items": {
|
|
960
|
+
"type": "number"
|
|
961
|
+
},
|
|
962
|
+
"minItems": 3,
|
|
963
|
+
"maxItems": 3,
|
|
964
|
+
"default": [0, 0, 0]
|
|
965
|
+
},
|
|
966
|
+
"scale": {
|
|
967
|
+
"type": "array",
|
|
968
|
+
"items": {
|
|
969
|
+
"type": "number"
|
|
970
|
+
},
|
|
971
|
+
"minItems": 3,
|
|
972
|
+
"maxItems": 3,
|
|
973
|
+
"default": [1, 1, 1]
|
|
974
|
+
},
|
|
975
|
+
"visible": {
|
|
976
|
+
"type": "boolean",
|
|
977
|
+
"default": true
|
|
978
|
+
},
|
|
979
|
+
"renderOrder": {
|
|
980
|
+
"type": "number",
|
|
981
|
+
"default": 0
|
|
982
|
+
},
|
|
983
|
+
"mode": {
|
|
984
|
+
"type": "string",
|
|
985
|
+
"enum": ["screen", "y", "none"],
|
|
986
|
+
"default": "screen"
|
|
987
|
+
}
|
|
988
|
+
},
|
|
989
|
+
"additionalProperties": false
|
|
990
|
+
},
|
|
991
|
+
"script": {
|
|
992
|
+
"type": "object",
|
|
993
|
+
"properties": {
|
|
994
|
+
"name": {
|
|
995
|
+
"type": "string"
|
|
996
|
+
},
|
|
997
|
+
"props": {
|
|
998
|
+
"type": "object"
|
|
999
|
+
}
|
|
1000
|
+
},
|
|
1001
|
+
"required": ["name"]
|
|
1002
|
+
},
|
|
1003
|
+
"network": {
|
|
1004
|
+
"type": "object"
|
|
1005
|
+
},
|
|
1006
|
+
"children": {
|
|
1007
|
+
"type": "array",
|
|
1008
|
+
"items": {
|
|
1009
|
+
"$ref": "#/$defs/node"
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
"required": ["name", "type"]
|
|
1014
|
+
},
|
|
921
1015
|
"Camera2D": {
|
|
922
1016
|
"type": "object",
|
|
923
1017
|
"properties": {
|
|
@@ -62,7 +62,7 @@ All 3D nodes extend `Node3D` and therefore have the transform props:
|
|
|
62
62
|
|---|---|---|
|
|
63
63
|
| `mesh` | `"box"` | `box \| sphere \| capsule \| plane \| cylinder \| gem` — `gem` is a faceted crystal (icosahedron); with `material.flatShading: true` + low `roughness` it reads as a sparkling jewel (spin/tumble it for the sparkle) |
|
|
64
64
|
| `size` | `[1,1,1]` | box: extents · sphere/gem: radius = x · capsule: radius = x, height = y · plane: x·z ground (laid FLAT on XZ) · cylinder: radius = x, height = y |
|
|
65
|
-
| `material` | `{}` | `{color: '#hex', metalness: 0..1, roughness: 0..1, opacity: 0..1, wireframe, flatShading, emissive, emissiveIntensity, map, normalMap, repeat: [u,v]}` — the object IS the material state: omitted keys reset to defaults (`#ffffff`, 0, 1, 1, false, false). `opacity < 1` turns on transparency; `flatShading` gives faceted per-face glints (the gem sparkle). `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). 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 |
|
|
65
|
+
| `material` | `{}` | `{color: '#hex', metalness: 0..1, roughness: 0..1, opacity: 0..1, 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). 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
66
|
| `castShadow` / `receiveShadow` | `false` | |
|
|
67
67
|
|
|
68
68
|
### `Sprite3D` / `AnimatedSprite3D` (2D billboard sprites — the 2.5D look)
|
|
@@ -108,6 +108,54 @@ Drop sprites onto terrain height like any 3D node (their feet are the anchor). I
|
|
|
108
108
|
a top-down / quarter view, `billboard: "y"` keeps characters readable from every
|
|
109
109
|
camera yaw.
|
|
110
110
|
|
|
111
|
+
### `Billboard3D` (a group that turns toward the camera — children follow)
|
|
112
|
+
A transform container that orients itself toward the camera every rendered frame;
|
|
113
|
+
**children inherit the rotation** through the scene graph. Hang world-space UI
|
|
114
|
+
under it — HP bars, pickup markers, floating icons — instead of hand-rolling
|
|
115
|
+
camera math in game code. (There is no 3D text node — a nameplate or damage
|
|
116
|
+
number is a canvas-rendered texture on a child `Sprite3D`.)
|
|
117
|
+
|
|
118
|
+
| Prop | Default | Notes |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| `mode` | `"screen"` | `"screen"` copies the camera's orientation wholesale (screen-aligned) — a child rectangle projects as an **upright rectangle anywhere on screen** · `"y"` stays vertical and only yaws toward the camera (standing content) · `"none"` billboarding off — the authored rotation applies again |
|
|
121
|
+
|
|
122
|
+
- Use `"screen"` for flat UI shapes (bars, plates). A yaw-only look-at — the
|
|
123
|
+
tempting hand-rolled version — visibly TILTS off-center shapes when the camera
|
|
124
|
+
pitches down; `"screen"` is immune. (This is deliberately not the same as
|
|
125
|
+
Sprite3D's `"full"`, which faces the camera POSITION and keeps that tilt.)
|
|
126
|
+
- Unlike `Sprite3D` (which spins only its own textured quad, never its children),
|
|
127
|
+
`Billboard3D` rotates the whole group. Parent one under a character and offset
|
|
128
|
+
it upward. The node's own `rotation` prop is overwritten while billboarding —
|
|
129
|
+
set `mode: "none"` to pose children with the editor gizmo, then switch back.
|
|
130
|
+
- A typo'd `mode` (e.g. Sprite3D's `"full"`) fails HARD at scene load — it never
|
|
131
|
+
silently falls back to another mode.
|
|
132
|
+
- It keeps orienting while `visible: false` on purpose: a hidden HP bar revealed
|
|
133
|
+
on first hit is already correctly oriented (no one-frame pop).
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{ "name": "Slime", "type": "AnimatedSprite3D",
|
|
137
|
+
"props": {
|
|
138
|
+
"position": [4, 0, -2], "size": [1.5, 1.5],
|
|
139
|
+
"sheet": "https://example.com/slime.png", "frameWidth": 32, "frameHeight": 32,
|
|
140
|
+
"animations": { "idle": { "frames": [0, 3], "fps": 4, "loop": true } },
|
|
141
|
+
"autoplay": "idle" },
|
|
142
|
+
"children": [
|
|
143
|
+
{ "name": "HpBar", "type": "Billboard3D", "props": { "position": [0, 1.8, 0] },
|
|
144
|
+
"children": [
|
|
145
|
+
{ "name": "Back", "type": "MeshInstance3D",
|
|
146
|
+
"props": { "mesh": "box", "size": [1, 0.12, 0.04],
|
|
147
|
+
"material": { "color": "#150a0a" } } },
|
|
148
|
+
{ "name": "Fill", "type": "MeshInstance3D",
|
|
149
|
+
"props": { "mesh": "box", "size": [1, 0.12, 0.06], "position": [0, 0, 0.01],
|
|
150
|
+
"material": { "color": "#ff3b3b", "emissive": "#ff3b3b" } } }
|
|
151
|
+
] }
|
|
152
|
+
] }
|
|
153
|
+
```
|
|
154
|
+
Drain the fill from game code with **`scale`, not `size`** — resizing a mesh
|
|
155
|
+
rebuilds its geometry every call, scaling is free: `Fill.scale = [frac, 1, 1]`
|
|
156
|
+
and `Fill.position = [-(1 - frac) / 2, 0, 0.01]`. The offsets stay local to the
|
|
157
|
+
(rotated) bar, so the drain always hugs the bar's left edge.
|
|
158
|
+
|
|
111
159
|
### `Camera3D`
|
|
112
160
|
`fov: 60`, `near: 0.1`, `far: 1000`, `current: false`.
|
|
113
161
|
Mark exactly ONE camera `current: true` (otherwise the first camera in tree order is used).
|
|
@@ -268,7 +268,7 @@ Four looks, picked by `style`:
|
|
|
268
268
|
|
|
269
269
|
| style | what renders | when to pick it |
|
|
270
270
|
|---|---|---|
|
|
271
|
-
| `"tufts"` | ONE InstancedMesh of 3-crossed-quad CARDS (12 verts each) carrying a baked multi-blade alpha-cutout texture, per-instance clump-coherent green tints + dry patches, ported sin·cos sway — kind `grass` only | the dense 잔디밭 — the ez-tree demo's actual grass mechanism; overlapping textured fans read as a full meadow at a fraction of the instances. Layer a sparse `mesh` field on top for close-up blade silhouettes |
|
|
271
|
+
| `"tufts"` | ONE InstancedMesh of 3-crossed-quad CARDS (12 verts each) carrying a baked multi-blade alpha-cutout texture, per-instance clump-coherent green tints + dry patches, ported sin·cos sway — kind `grass` only | the dense 잔디밭 — the ez-tree demo's actual grass mechanism; overlapping textured fans read as a full meadow at a fraction of the instances. Layer a sparse `mesh` field on top for close-up blade silhouettes. ⚠️ Cards are BIG (drawn ~1.5× `height`, wide) + opaque, so over a field of small gameplay sprites (mobs/items) they hide actors shorter than the grass — there, prefer `"blades"` (thin) or keep `height` low |
|
|
272
272
|
| `"mesh"` (default) | ONE InstancedMesh where EVERY instance is a real tapered blade (7-vertex strip) curved along a vertex-shader quadratic bezier — kind `grass` only | real per-blade geometry — best up close; reads thin at field scale |
|
|
273
273
|
| `"blades"` | TWO crossed quads per slot; the fragment shader draws 8 procedural SDF blades per quad — kind `grass` only; bends to the player (`interaction`) and receives scene `fog` (distant fields melt into the haze like the mesh grass / trees / water) | pinning the pre-mesh ported look |
|
|
274
274
|
| `"simple"` | legacy crossed quads, per-instance colorA→colorB lerp, whole-field shear sway | `flowers`/`reeds` (their only look); ultra-cheap set dressing |
|
|
@@ -109,6 +109,17 @@ Signals: `triggerEnter(other)` · `triggerExit(other)`
|
|
|
109
109
|
|
|
110
110
|
Signals: `finished`
|
|
111
111
|
|
|
112
|
+
## `Billboard3D` — `incanto/3d`
|
|
113
|
+
|
|
114
|
+
| Prop | Default | Kind |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `position` | `[0,0,0]` | array |
|
|
117
|
+
| `rotation` | `[0,0,0]` | array |
|
|
118
|
+
| `scale` | `[1,1,1]` | array |
|
|
119
|
+
| `visible` | `true` | boolean |
|
|
120
|
+
| `renderOrder` | `0` | number |
|
|
121
|
+
| `mode` | `"screen"` | one of: `screen` `y` `none` |
|
|
122
|
+
|
|
112
123
|
## `Camera2D` — `incanto/2d`
|
|
113
124
|
|
|
114
125
|
| Prop | Default | Kind |
|