incanto 0.52.0 → 0.54.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-check.mjs +56 -1
- package/dist/2d.d.ts +95 -4
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +111 -6
- package/dist/3d.js +10 -6
- package/dist/{behavior-uPEuZrUB.d.ts → behavior-CyQoSu4n.d.ts} +212 -10
- package/dist/{create-game-B_e9hJf7.js → create-game-BRt6XKmP.js} +31 -12
- package/dist/{create-game-CGnoypjL.js → create-game-Czzp6ZuE.js} +26 -14
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +1 -1
- package/dist/{duplicate-B-OtSRFL.js → duplicate-MNLMAcbz.js} +1 -1
- package/dist/editor.js +14 -0
- package/dist/{environment-presets-DSZwsKPs.js → environment-presets-BQ_QsIBY.js} +222 -11
- package/dist/{gameplay-B6jqvYeM.js → gameplay-CZ2yq37J.js} +32 -23
- package/dist/gameplay.d.ts +8 -3
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +178 -6
- package/dist/index.js +9 -9
- package/dist/{loader-B-Gft32x.js → loader-BTkHYrQn.js} +54 -18
- package/dist/{loader-B9iTqs27.d.ts → loader-DhI1jFW_.d.ts} +1 -1
- package/dist/{log-report-lxrQY9cH.js → log-report-CPFm4OXf.js} +0 -0
- package/dist/net.d.ts +2 -2
- package/dist/net.js +1 -1
- package/dist/{particle-sim-BzJ1yxoE.d.ts → particle-sim-C5OfBbmU.d.ts} +11 -0
- package/dist/{pathfinding-CAR9DjQQ.d.ts → pathfinding-CXGCpRQe.d.ts} +24 -1
- package/dist/{physics-2d-Dns-oZlE.js → physics-2d-CfWAggJ1.js} +2 -2
- package/dist/{physics-3d-BhI0ehpe.js → physics-3d-Bes-GIuR.js} +3 -3
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-Trx7WHnD.js → register-en63AEZO.js} +163 -4
- package/dist/{register-DVwlnZAZ.js → register-p48lHE2o.js} +433 -36
- package/dist/{replay-BU1CCM15.d.ts → replay-ePMz26jw.d.ts} +1 -1
- package/dist/{replay-BicPOMX0.js → replay-t1pP0gQg.js} +2 -2
- package/dist/{split-screen-paxkQs_q.d.ts → split-screen-BsdOHbzP.d.ts} +1 -1
- package/dist/{split-screen-CZ9ccBBQ.js → split-screen-CSb_uZ6W.js} +2 -2
- package/dist/{sprite-animation-D_p28jwU.js → sprite-animation-7qvUxF6Z.js} +19 -0
- package/dist/{src-5gbZO47I.js → src-vNPQeQ1W.js} +1 -1
- package/dist/{teardown-ks3d5W9n.js → teardown-C7uVSJvx.js} +30 -1
- package/dist/{test-DAojuFdb.js → test-Tnf1nQl3.js} +25 -13
- package/dist/test.d.ts +4 -4
- package/dist/test.js +2 -2
- package/dist/{touch-BoNg_MnF.js → touch-BnMyy9tr.js} +4 -4
- package/dist/vite.js +2 -2
- package/editor/assets/{agent8-CCvckvbw.js → agent8-BWaW-D85.js} +1 -1
- package/editor/assets/{debug-CLNCOnbc.js → debug-DLF8rUtr.js} +2 -2
- package/editor/assets/{index-Cb5Brupb.js → index-BYfiwbQx.js} +91 -91
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +221 -0
- package/skills/incanto-assets.md +45 -2
- package/skills/incanto-audio.md +97 -10
- package/skills/incanto-building-2d-games.md +55 -3
- package/skills/incanto-building-3d-games.md +3 -1
- package/skills/incanto-environment.md +3 -1
- package/skills/incanto-gameplay-behaviors.md +51 -4
- package/skills/incanto-hud.md +7 -0
- package/skills/incanto-node-reference.md +36 -0
- package/skills/incanto-performance.md +47 -0
- package/skills/incanto-physics-and-input.md +38 -0
- package/skills/incanto-verifying-your-game.md +50 -1
- package/skills/incanto-web-integration.md +1 -0
- package/templates-app/beacon-isle-3d/index.html +3 -3
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/index.html +3 -3
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/index.html +3 -3
- package/templates-app/village-quest-3d/package.json +1 -1
|
@@ -613,6 +613,7 @@ _No props (structural fields only)._
|
|
|
613
613
|
| `visible` | `true` | boolean |
|
|
614
614
|
| `preset` | `"custom"` | one of: `custom` `fire` `smoke` `sparks` `fireworks` `explosion` `flash` `lightning` `rain` `snow` `magic` |
|
|
615
615
|
| `emitting` | `true` | boolean |
|
|
616
|
+
| `worldSpace` | `false` | boolean |
|
|
616
617
|
| `rate` | `40` | number |
|
|
617
618
|
| `burst` | `0` | number |
|
|
618
619
|
| `lifetime` | `[0.6,1.2]` | array |
|
|
@@ -648,6 +649,7 @@ Signals: `finished`
|
|
|
648
649
|
| `snapToGround` | `null` | null |
|
|
649
650
|
| `preset` | `"custom"` | one of: `custom` `fire` `smoke` `sparks` `fireworks` `explosion` `flash` `lightning` `rain` `snow` `magic` |
|
|
650
651
|
| `emitting` | `true` | boolean |
|
|
652
|
+
| `worldSpace` | `false` | boolean |
|
|
651
653
|
| `rate` | `40` | number |
|
|
652
654
|
| `burst` | `0` | number |
|
|
653
655
|
| `lifetime` | `[0.6,1.2]` | array |
|
|
@@ -1039,6 +1041,20 @@ Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped`
|
|
|
1039
1041
|
|
|
1040
1042
|
Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `changed`
|
|
1041
1043
|
|
|
1044
|
+
## `UiMuteToggle` — `incanto`
|
|
1045
|
+
|
|
1046
|
+
| Prop | Default | Kind |
|
|
1047
|
+
|---|---|---|
|
|
1048
|
+
| `anchor` | `"topLeft"` | one of: `topLeft` `top` `topRight` `left` `center` `right` `bottomLeft` `bottom` `bottomRight` |
|
|
1049
|
+
| `visible` | `true` | boolean |
|
|
1050
|
+
| `focusable` | `true` | boolean |
|
|
1051
|
+
| `draggable` | `false` | boolean |
|
|
1052
|
+
| `dropTarget` | `false` | boolean |
|
|
1053
|
+
| `label` | `""` | string |
|
|
1054
|
+
| `value` | `false` | boolean |
|
|
1055
|
+
|
|
1056
|
+
Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `changed`
|
|
1057
|
+
|
|
1042
1058
|
## `UiPanel` — `incanto`
|
|
1043
1059
|
|
|
1044
1060
|
| Prop | Default | Kind |
|
|
@@ -1155,6 +1171,26 @@ Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped`
|
|
|
1155
1171
|
|
|
1156
1172
|
Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `changed`
|
|
1157
1173
|
|
|
1174
|
+
## `UiVolumeSlider` — `incanto`
|
|
1175
|
+
|
|
1176
|
+
| Prop | Default | Kind |
|
|
1177
|
+
|---|---|---|
|
|
1178
|
+
| `anchor` | `"topLeft"` | one of: `topLeft` `top` `topRight` `left` `center` `right` `bottomLeft` `bottom` `bottomRight` |
|
|
1179
|
+
| `visible` | `true` | boolean |
|
|
1180
|
+
| `focusable` | `true` | boolean |
|
|
1181
|
+
| `draggable` | `false` | boolean |
|
|
1182
|
+
| `dropTarget` | `false` | boolean |
|
|
1183
|
+
| `label` | `""` | string |
|
|
1184
|
+
| `value` | `0.5` | number |
|
|
1185
|
+
| `min` | `0` | number |
|
|
1186
|
+
| `max` | `1` | number |
|
|
1187
|
+
| `step` | `0.01` | number |
|
|
1188
|
+
| `width` | `180` | number |
|
|
1189
|
+
| `color` | `"#6ee7dc"` | string |
|
|
1190
|
+
| `bus` | `"master"` | one of: `master` `sfx` `music` |
|
|
1191
|
+
|
|
1192
|
+
Signals: `dragStarted` · `dragCancelled` · `droppedOn` · `dropped` · `changed`
|
|
1193
|
+
|
|
1158
1194
|
## `VoxelGrid3D` — `incanto/3d`
|
|
1159
1195
|
|
|
1160
1196
|
| Prop | Default | Kind |
|
|
@@ -13,6 +13,37 @@ You do not implement any of this. You decide whether to expose it.
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## First: where did the frame go?
|
|
17
|
+
|
|
18
|
+
A settings menu is what you offer a PLAYER. This is what you ask the game.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
game.stats();
|
|
22
|
+
// { fps: 31, frameMs: 32.1, nodes: 4210, triangles: 890_000, drawCalls: 340,
|
|
23
|
+
// phases: { fixedMs: 21.4, updateMs: 2.2, renderMs: 6.1, otherMs: 2.4 } }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`fps` and `frameMs` are a total, and a total has no next question. The four
|
|
27
|
+
slices say which part of the frame to look at:
|
|
28
|
+
|
|
29
|
+
| slice | what is in it | what to do about it |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `fixedMs` | physics and everything on `fixedUpdate` | fewer/simpler colliders, `fixedHz`, sleep distant bodies |
|
|
32
|
+
| `updateMs` | behaviors, node logic, tweens | the per-frame work your game does — profile it in your own code |
|
|
33
|
+
| `renderMs` | the renderer, reported by itself | quality tier, `renderScale`, draw calls, shadows |
|
|
34
|
+
| `otherMs` | the frame MINUS the three above | GC, browser layout, your own rAF work. **Allocations show up here.** |
|
|
35
|
+
|
|
36
|
+
`otherMs` is the one worth knowing about. A frame drop that everyone reads as a
|
|
37
|
+
GPU problem is often garbage collection, and it looks identical from the outside
|
|
38
|
+
— this engine lost a day to exactly that. A big `otherMs` with small everything
|
|
39
|
+
else means you are allocating per frame: replace arrays and objects created in
|
|
40
|
+
`update()` with reused ones.
|
|
41
|
+
|
|
42
|
+
Averages over the same rolling window as `frameMs`, so the numbers add up to it
|
|
43
|
+
and are comparable to each other. Headless (`step()`), `fps`/`frameMs` are 0 and
|
|
44
|
+
the slices are still real CPU times — a behavior that got slower shows up in a
|
|
45
|
+
test.
|
|
46
|
+
|
|
16
47
|
## The short version
|
|
17
48
|
|
|
18
49
|
```ts
|
|
@@ -32,6 +63,22 @@ Every one of those is written to the save store and comes back on the next launc
|
|
|
32
63
|
|
|
33
64
|
---
|
|
34
65
|
|
|
66
|
+
## Leaks: a level swap has to give the GPU back
|
|
67
|
+
|
|
68
|
+
`stats().geometries` and `stats().textures` are the leak witnesses — they count
|
|
69
|
+
what three is holding right now. Swap levels a few times and read them again:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const before = game.stats().geometries;
|
|
73
|
+
await loadLevel(2);
|
|
74
|
+
await loadLevel(1);
|
|
75
|
+
game.stats().geometries; // should be ~before, not 2x before
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Every node that builds geometry releases it when it is freed, and a scene swap
|
|
79
|
+
frees the old tree. If your own code holds a three object (a custom behavior
|
|
80
|
+
that built a mesh), dispose it in the behavior's `onExitTree`.
|
|
81
|
+
|
|
35
82
|
## The four levers, and what each actually costs
|
|
36
83
|
|
|
37
84
|
| setting | values | costs | changes live |
|
|
@@ -142,6 +142,44 @@ disables; they overlay `touchContainer` — default the canvas's parent, give it
|
|
|
142
142
|
`position: relative`). Manual boots call `attachTouchControls(engine, container,
|
|
143
143
|
{ force? })` from `incanto`. Wrong touch kinds (`"joystick"` on a button) fail at load.
|
|
144
144
|
|
|
145
|
+
**Three things a phone needs that a desktop never shows you**, two of which the
|
|
146
|
+
engine now does for you:
|
|
147
|
+
|
|
148
|
+
- **The canvas owns its touch gestures.** `createGame2D/3D` set
|
|
149
|
+
`touch-action: none` (plus no selection/callout) on the canvas, or the browser
|
|
150
|
+
keeps pan, pinch-zoom and pull-to-refresh over the play surface — a drag meant
|
|
151
|
+
for aim or a virtual stick SCROLLS THE PAGE instead. `pageGestures: true`
|
|
152
|
+
gives them back to the browser (a small canvas inside a scrolling article).
|
|
153
|
+
- **The controls clear the phone's furniture.** They sit
|
|
154
|
+
`calc(24px + env(safe-area-inset-*))` from the edges, off the iPhone home
|
|
155
|
+
indicator — whose band is also the OS's "leave the app" swipe.
|
|
156
|
+
- **Your PAGE has to opt in**, and this part is yours: `env()` is 0 unless the
|
|
157
|
+
document says so, and `100vh` is the toolbar-hidden height, so the bottom strip
|
|
158
|
+
(where the controls are) hides under the browser chrome.
|
|
159
|
+
|
|
160
|
+
```html
|
|
161
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
162
|
+
<style>
|
|
163
|
+
html, body { margin: 0; height: 100%; overflow: hidden; }
|
|
164
|
+
canvas { display: block; width: 100%; height: 100%; } /* NOT 100vw/100vh */
|
|
165
|
+
</style>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Every `incanto-new` template and every runnable example ships exactly this.
|
|
169
|
+
|
|
170
|
+
Two more phone facts worth knowing:
|
|
171
|
+
|
|
172
|
+
- **Audio unlocks on ANY first gesture** — the canvas, a key, or the on-screen
|
|
173
|
+
stick and buttons. (Those controls are siblings of the canvas, so their taps
|
|
174
|
+
never reached it; a game with on-screen controls used to stay silent until the
|
|
175
|
+
player happened to touch the play area.)
|
|
176
|
+
- **2D renders at `pixelRatio: 1` by default** (Phaser parity — the browser
|
|
177
|
+
upscales, edges read soft). Phones are 2–3× denser, so a text-heavy or
|
|
178
|
+
vector-heavy 2D game wants
|
|
179
|
+
`createGame2D({ pixelRatio: Math.min(devicePixelRatio, 2) })`, or
|
|
180
|
+
`"environment": { "rendering": { "pixelRatio": "device" } }` in the scene. 3D
|
|
181
|
+
already defaults to `min(dpr, 2)`; pixel art is usually happier left at 1.
|
|
182
|
+
|
|
145
183
|
Collision layers/masks are not in v0 — everything collides with everything; use groups +
|
|
146
184
|
`triggerEnter` filtering for game logic.
|
|
147
185
|
|
|
@@ -314,7 +314,9 @@ They now go to **`engine.log` as well as the console**, so:
|
|
|
314
314
|
- `runScript().logs` contains them, and `describe()` prints them under the
|
|
315
315
|
failures (`! warn: …`).
|
|
316
316
|
- The debug overlay's **Logs** panel shows them live.
|
|
317
|
-
- `game.assetErrors()` lists every asset that failed **with its reason and ref**
|
|
317
|
+
- `game.assetErrors()` lists every asset that failed **with its reason and ref** —
|
|
318
|
+
textures, models, AND sounds (an `AudioPlayer` whose `src` the browser cannot
|
|
319
|
+
play), in 2D and 3D alike
|
|
318
320
|
— the direct answer to "why is my model not there". The store itself is
|
|
319
321
|
reachable as `renderer.assets` in both dimensions.
|
|
320
322
|
|
|
@@ -661,6 +663,53 @@ never Math.random, dt/`engine.time` never Date.now. Gamepads replay through
|
|
|
661
663
|
the ACTIONS they were bound to, not raw pad state.
|
|
662
664
|
|
|
663
665
|
|
|
666
|
+
## Sound: did the right thing sound?
|
|
667
|
+
|
|
668
|
+
Audio makes no sound in the VM, but the calls still happen — so the wiring is
|
|
669
|
+
checkable where the game can be run a thousand times:
|
|
670
|
+
|
|
671
|
+
```ts
|
|
672
|
+
session.engine.audio.clearLog();
|
|
673
|
+
hitTheEnemy();
|
|
674
|
+
session.step(200);
|
|
675
|
+
expect(session.engine.audio.countOf('hit')).toBe(1);
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
`engine.audio.recent()` is the last 200 sounds with their node path, bus and
|
|
679
|
+
time; `countOf(name)` counts one. A clip whose FILE is broken shows up in
|
|
680
|
+
`assetErrors()` instead. Full guide: `incanto-audio.md`.
|
|
681
|
+
|
|
682
|
+
## Speed: which part of the frame is expensive?
|
|
683
|
+
|
|
684
|
+
```ts
|
|
685
|
+
game.stats().phases;
|
|
686
|
+
// { fixedMs: 21.4, updateMs: 2.2, renderMs: 6.1, otherMs: 2.4 }
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
Physics, behaviors, the renderer, and everything else. The last one is where GC
|
|
690
|
+
and per-frame allocation hide, and it is the slice a screenshot can never show.
|
|
691
|
+
See `incanto-performance.md`.
|
|
692
|
+
|
|
693
|
+
## Sight: did the right thing SHOW?
|
|
694
|
+
|
|
695
|
+
The same question as sound, about the other half of a game's feedback. Nothing
|
|
696
|
+
renders in the VM, so shake, flash and freeze frames are no-ops and a particle
|
|
697
|
+
system that never fired sits at exactly the same coordinates as one that fired a
|
|
698
|
+
hundred times — `framing()` reports WHERE an emitter is, never whether it went
|
|
699
|
+
off.
|
|
700
|
+
|
|
701
|
+
```ts
|
|
702
|
+
session.engine.effects.clearLog();
|
|
703
|
+
smashTheCrystal();
|
|
704
|
+
session.step(200);
|
|
705
|
+
expect(session.engine.effects.countOf('explosion')).toBe(1);
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
`engine.effects.recent()` is the last 200 effects — `kind` (`burst` · `emit` ·
|
|
709
|
+
`shake` · `flash` · `hitstop`), the preset or colour, the node path, and how big.
|
|
710
|
+
`countFrom(path)` asks about one emitter, which is usually the wiring question.
|
|
711
|
+
A particle node's `aliveCount` answers the other one: is it running right now.
|
|
712
|
+
|
|
664
713
|
## Multiplayer: do the players end up in the SAME world?
|
|
665
714
|
|
|
666
715
|
Every rung above asks about one player. A multiplayer game's first question is
|
|
@@ -59,6 +59,7 @@ Rule of thumb: score/menus/dialogs that look like WEB UI → DOM overlay;
|
|
|
59
59
|
anything the game itself must own (and runScript must verify) → UILayer.
|
|
60
60
|
|
|
61
61
|
Pin a DOM element to a world position: `game.renderer.screenFromWorld(wx, wy)`
|
|
62
|
+
(and `worldFromScreen(sx, sy)` the other way, for taps and clicks)
|
|
62
63
|
each frame (subscribe `engine.updated`), then `transform: translate(x, y)`.
|
|
63
64
|
|
|
64
65
|
Perf note: `useNodeProp` deep-compares per frame — subscribe to LEAF values
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><rect width=%22100%22 height=%22100%22 rx=%2220%22 fill=%22%232a4a6b%22/><text x=%2250%22 y=%2268%22 font-size=%2255%22 text-anchor=%22middle%22 font-family=%22Arial%22 font-weight=%22bold%22 fill=%22%23ffd166%22>bi</text></svg>" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
7
7
|
<title>Beacon Isle — Incanto 3D template</title>
|
|
8
8
|
<style>
|
|
9
9
|
html,
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
canvas {
|
|
19
19
|
display: block;
|
|
20
|
-
width:
|
|
21
|
-
height:
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
22
|
}
|
|
23
23
|
.loading-container {
|
|
24
24
|
position: fixed;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><rect width=%22100%22 height=%22100%22 rx=%2220%22 fill=%22%233b6ea5%22/><text x=%2250%22 y=%2268%22 font-size=%2255%22 text-anchor=%22middle%22 font-family=%22Arial%22 font-weight=%22bold%22 fill=%22%236ee7dc%22>vg</text></svg>" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
7
7
|
<title>Vanguard — Incanto TPS</title>
|
|
8
8
|
<style>
|
|
9
9
|
html,
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
canvas {
|
|
19
19
|
display: block;
|
|
20
|
-
width:
|
|
21
|
-
height:
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
22
|
}
|
|
23
23
|
.loading-container {
|
|
24
24
|
position: fixed;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><rect width=%22100%22 height=%22100%22 rx=%2220%22 fill=%22%234a6b3a%22/><text x=%2250%22 y=%2268%22 font-size=%2255%22 text-anchor=%22middle%22 font-family=%22Arial%22 font-weight=%22bold%22 fill=%22%23ffd166%22>eb</text></svg>" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
7
7
|
<title>Emberwood — Incanto quest</title>
|
|
8
8
|
<style>
|
|
9
9
|
html,
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
canvas {
|
|
19
19
|
display: block;
|
|
20
|
-
width:
|
|
21
|
-
height:
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
22
|
}
|
|
23
23
|
.loading-container {
|
|
24
24
|
position: fixed;
|