melonjs 20.13.0 → 20.14.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 +65 -22
- package/build/application/application.d.ts +6 -1
- package/build/application/application.d.ts.map +1 -1
- package/build/application/header.d.ts.map +1 -1
- package/build/application/resize.d.ts.map +1 -1
- package/build/application/settings.d.ts +13 -8
- package/build/application/settings.d.ts.map +1 -1
- package/build/audio/audio.d.ts +8 -4
- package/build/audio/audio.d.ts.map +1 -1
- package/build/audio/backend/engine.d.ts.map +1 -1
- package/build/audio/playback.d.ts +2 -2
- package/build/audio/playback.d.ts.map +1 -1
- package/build/audio/spatial.d.ts.map +1 -1
- package/build/audio/types.d.ts +15 -5
- package/build/audio/types.d.ts.map +1 -1
- package/build/camera/camera2d.d.ts +10 -9
- package/build/camera/camera2d.d.ts.map +1 -1
- package/build/camera/camera3d.d.ts +11 -8
- package/build/camera/camera3d.d.ts.map +1 -1
- package/build/geometries/path2d.d.ts +9 -8
- package/build/geometries/path2d.d.ts.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +40993 -39738
- package/build/index.js.map +4 -4
- package/build/input/keyboard.d.ts.map +1 -1
- package/build/input/pointerevent.d.ts.map +1 -1
- package/build/level/gltf/GLTFModel.d.ts.map +1 -1
- package/build/level/level.d.ts.map +1 -1
- package/build/level/tiled/TMXLayer.d.ts +2 -1
- package/build/level/tiled/TMXLayer.d.ts.map +1 -1
- package/build/level/tiled/TMXTileMap.d.ts +13 -3
- package/build/level/tiled/TMXTileMap.d.ts.map +1 -1
- package/build/level/tiled/TMXTileset.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXHexagonalRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXIsometricRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXObliqueRenderer.d.ts +1 -0
- package/build/level/tiled/renderer/TMXObliqueRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXRenderer.d.ts +7 -3
- package/build/level/tiled/renderer/TMXRenderer.d.ts.map +1 -1
- package/build/lighting/light3d.d.ts +55 -0
- package/build/lighting/light3d.d.ts.map +1 -1
- package/build/loader/cache.d.ts +146 -97
- package/build/loader/cache.d.ts.map +1 -1
- package/build/loader/internal-types.d.ts +69 -0
- package/build/loader/internal-types.d.ts.map +1 -0
- package/build/loader/loader.d.ts +584 -292
- package/build/loader/loader.d.ts.map +1 -1
- package/build/loader/parsers/audio.d.ts.map +1 -1
- package/build/loader/parsers/fontface.d.ts.map +1 -1
- package/build/loader/parsers/gltf.d.ts.map +1 -1
- package/build/loader/parsers/tmx.d.ts.map +1 -1
- package/build/math/color.d.ts +4 -0
- package/build/math/color.d.ts.map +1 -1
- package/build/math/noise.d.ts +16 -16
- package/build/math/noise.d.ts.map +1 -1
- package/build/physics/adapter.d.ts +64 -25
- package/build/physics/adapter.d.ts.map +1 -1
- package/build/physics/builtin/body.d.ts +9 -0
- package/build/physics/builtin/body.d.ts.map +1 -1
- package/build/physics/builtin/builtin-adapter.d.ts +25 -3
- package/build/physics/builtin/builtin-adapter.d.ts.map +1 -1
- package/build/physics/builtin/detector.d.ts.map +1 -1
- package/build/physics/builtin/raycast.d.ts.map +1 -1
- package/build/physics/world.d.ts +40 -10
- package/build/physics/world.d.ts.map +1 -1
- package/build/renderable/container.d.ts +1 -0
- package/build/renderable/container.d.ts.map +1 -1
- package/build/renderable/draggable.d.ts.map +1 -1
- package/build/renderable/frameAnimation.d.ts.map +1 -1
- package/build/renderable/instanced_mesh.d.ts +16 -14
- package/build/renderable/instanced_mesh.d.ts.map +1 -1
- package/build/renderable/mesh.d.ts +62 -5
- package/build/renderable/mesh.d.ts.map +1 -1
- package/build/renderable/trigger.d.ts +5 -0
- package/build/renderable/trigger.d.ts.map +1 -1
- package/build/renderable/ui/uibaseelement.d.ts +8 -1
- package/build/renderable/ui/uibaseelement.d.ts.map +1 -1
- package/build/renderable/ui/uicheckbox.d.ts.map +1 -1
- package/build/system/device.d.ts +11 -2
- package/build/system/device.d.ts.map +1 -1
- package/build/system/fullscreen.d.ts.map +1 -1
- package/build/system/pool.d.ts.map +1 -1
- package/build/utils/utils.d.ts.map +1 -1
- package/build/video/canvas/canvas_renderer.d.ts +16 -14
- package/build/video/canvas/canvas_renderer.d.ts.map +1 -1
- package/build/video/canvas_factory.d.ts.map +1 -1
- package/build/video/effects/shadereffect.d.ts +127 -0
- package/build/video/effects/shadereffect.d.ts.map +1 -1
- package/build/video/renderer.d.ts.map +1 -1
- package/build/video/webgl/batchers/lit_mesh_batcher.d.ts.map +1 -1
- package/build/video/webgl/webgl_renderer.d.ts.map +1 -1
- package/build/video/webgpu/batchers/lit_mesh_batcher.d.ts.map +1 -1
- package/build/video/webgpu/webgpu_renderer.d.ts.map +1 -1
- package/package.json +1 -1
- package/skills/melonjs/SKILL.md +38 -14
- package/skills/melonjs-20-migration/SKILL.md +50 -7
- package/skills/melonjs-3d/SKILL.md +138 -38
- package/skills/melonjs-3d-assets/SKILL.md +83 -33
- package/skills/melonjs-audio/SKILL.md +55 -14
- package/skills/melonjs-camera-and-drawing/SKILL.md +95 -8
- package/skills/melonjs-deployment/SKILL.md +92 -14
- package/skills/melonjs-display-and-scaling/SKILL.md +8 -3
- package/skills/melonjs-effects-and-shaders/SKILL.md +46 -15
- package/skills/melonjs-events/SKILL.md +3 -2
- package/skills/melonjs-getting-started/SKILL.md +5 -0
- package/skills/melonjs-input/SKILL.md +65 -15
- package/skills/melonjs-lighting/SKILL.md +13 -1
- package/skills/melonjs-loading-assets/SKILL.md +202 -27
- package/skills/melonjs-math/SKILL.md +12 -5
- package/skills/melonjs-particles-and-trails/SKILL.md +7 -3
- package/skills/melonjs-performance/SKILL.md +18 -8
- package/skills/melonjs-physics/SKILL.md +385 -127
- package/skills/melonjs-plugins/SKILL.md +45 -11
- package/skills/melonjs-renderables/SKILL.md +24 -7
- package/skills/melonjs-renderer-backends/SKILL.md +38 -17
- package/skills/melonjs-scenes-and-state/SKILL.md +30 -5
- package/skills/melonjs-sprites-and-animation/SKILL.md +23 -2
- package/skills/melonjs-tilemaps/SKILL.md +56 -18
- package/skills/melonjs-ui-and-text/SKILL.md +120 -25
|
@@ -13,13 +13,13 @@ This trips people up, so it is worth stating first:
|
|
|
13
13
|
| | how it is installed |
|
|
14
14
|
|---|---|
|
|
15
15
|
| **Plugins** | `plugin.register(Class, name?, ...args)` — after `await app.init()` |
|
|
16
|
-
| **Physics adapters** | an `Application` **setting** (`physic:`), before `init()` |
|
|
16
|
+
| **Physics adapters** | an `Application` **setting** (`physic:`), before `init()`; or `app.world.setAdapter()` on a running game |
|
|
17
17
|
|
|
18
18
|
```js
|
|
19
19
|
// the adapter is chosen at construction …
|
|
20
20
|
const app = new Application(800, 600, {
|
|
21
21
|
parent: "screen",
|
|
22
|
-
physic:
|
|
22
|
+
physic: await PlanckAdapter.create({ gravity, pixelsPerMeter }),
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
// … the plugin is registered once the renderer exists
|
|
@@ -88,6 +88,19 @@ This is the answer for deforming animated characters — glTF import does not re
|
|
|
88
88
|
vertex skinning. It also registers a Tiled object class, so Spine skeletons can
|
|
89
89
|
be placed from a map.
|
|
90
90
|
|
|
91
|
+
```js
|
|
92
|
+
import Spine, { SpinePlugin } from "@melonjs/spine-plugin";
|
|
93
|
+
|
|
94
|
+
await app.init();
|
|
95
|
+
plugin.register(SpinePlugin); // after init: it reads app.renderer
|
|
96
|
+
// preloaded as { name: "hero.atlas", type: "spine", src: … }, and the same for
|
|
97
|
+
// "hero.json" (a binary "hero.skel" also goes in jsonFile)
|
|
98
|
+
const hero = new Spine(100, 100, { atlasFile: "hero.atlas", jsonFile: "hero.json" });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Register it after `await app.init()` like any plugin: its constructor builds
|
|
102
|
+
an asset manager on the application's renderer, which does not exist before.
|
|
103
|
+
|
|
91
104
|
### `@melonjs/gamepad-plugin`
|
|
92
105
|
|
|
93
106
|
On-screen touch controls, reported as a gamepad: one or two sticks and any
|
|
@@ -109,8 +122,10 @@ of a pad button. Since 1.1 a stick or button can be `floating` (shows up
|
|
|
109
122
|
under the finger anywhere in its `area`), `hideAtRest` (drawn only while
|
|
110
123
|
held), and a stick can take a `tap` (a quick touch that does not steer presses
|
|
111
124
|
a key or button) — so the mobile layouts, two thumbs or one thumb with
|
|
112
|
-
tap-to-act, need no code of their own.
|
|
113
|
-
the
|
|
125
|
+
tap-to-act, need no code of their own. Since 1.2 a `skin` texture atlas draws
|
|
126
|
+
them from the game's own art (`base` / `thumb` regions on a stick, `region` /
|
|
127
|
+
`pressed` on a button). It needs melonJS 20.12 or later. See `melonjs-input`
|
|
128
|
+
for the bindings, the layouts and the overlap rules.
|
|
114
129
|
|
|
115
130
|
### `@melonjs/capacitor-plugin`
|
|
116
131
|
|
|
@@ -126,16 +141,29 @@ It also exports helpers usable on their own: `onBackButton`, `bindStageBack`,
|
|
|
126
141
|
`lockOrientation` / `unlockOrientation`, `hideSplash`. See `melonjs-deployment`
|
|
127
142
|
for the wider packaging picture.
|
|
128
143
|
|
|
144
|
+
With `pauseOnBackground` (the default), going to the background pauses the
|
|
145
|
+
game and coming back resumes it, but only if this pause was the plugin's: a
|
|
146
|
+
game the player had paused stays paused.
|
|
147
|
+
|
|
129
148
|
## Physics adapters
|
|
130
149
|
|
|
131
|
-
`@melonjs/planck-adapter
|
|
132
|
-
wholesale. Pick **one** per
|
|
133
|
-
`
|
|
150
|
+
`@melonjs/planck-adapter`, `@melonjs/matter-adapter` and
|
|
151
|
+
`@melonjs/rapier2d-adapter` swap the physics world wholesale. Pick **one** per
|
|
152
|
+
game (`app.world.setAdapter()` can switch it later). The portable surface — the
|
|
153
|
+
declarative `renderable.bodyDef`, `world.adapter.queryAABB(rect)` and
|
|
134
154
|
`world.adapter.raycast(from, to)` — works unchanged across the built-in world and
|
|
135
|
-
|
|
155
|
+
every adapter, so entity code does not have to branch. The exceptions are in
|
|
156
|
+
`melonjs-physics`: `Box3d`, `Sphere` and `Point` shapes are built-in only,
|
|
157
|
+
`type: "kinematic"` is planck and Rapier only, and the adapters' `queryAABB`
|
|
158
|
+
returns only renderables with a body. The Rapier adapter's WebAssembly loads
|
|
159
|
+
asynchronously, so it must be made with `await RapierAdapter.create()`.
|
|
160
|
+
`create()` is on every adapter from planck-adapter 1.7, matter-adapter 1.6 and
|
|
161
|
+
rapier2d-adapter 1.0 (an older planck or matter adapter is made with
|
|
162
|
+
`new X(options)`), and `world.setAdapter()` from melonJS 20.14.
|
|
136
163
|
|
|
137
164
|
Two queries are **not** portable: `querySphere` and `raycast3d` exist only on the
|
|
138
|
-
built-in adapter (planck and
|
|
165
|
+
built-in adapter (planck, matter and Rapier are 2D-only and deliberately omit
|
|
166
|
+
them).
|
|
139
167
|
`queryAABB` is the one region query every adapter must implement, so call the
|
|
140
168
|
others optionally — `app.world.adapter.querySphere?.(center, radius)`. For rays
|
|
141
169
|
there is a real flag: `app.world.adapter.capabilities.raycasts3d`.
|
|
@@ -200,11 +228,17 @@ or a `Stage` is simpler and easier to test.
|
|
|
200
228
|
| `Plugin version mismatch` | the plugin's `version` floor is above the running engine |
|
|
201
229
|
| `Plugin should extend the BasePlugin Class !` | the class does not extend `plugin.BasePlugin` |
|
|
202
230
|
| the panel reports another app's numbers | `app` defaulted to `game`, the last Application to finish `init()` |
|
|
203
|
-
| `world.adapter.querySphere is not a function` | built-in adapter only; planck and
|
|
231
|
+
| `world.adapter.querySphere is not a function` | built-in adapter only; planck, matter and Rapier are 2D-only |
|
|
204
232
|
| a Tiled map opens in Tiled but not melonJS | compressed layers, tiled-inflate plugin missing |
|
|
205
233
|
| plugin setup never runs | setup written in an `init()` method — there is no such hook |
|
|
206
|
-
| `
|
|
234
|
+
| `Plugin should extend the BasePlugin Class !` when registering an adapter | adapters are a construction setting (or `world.setAdapter()`), not a plugin |
|
|
235
|
+
| `PlanckAdapter.create is not a function` (or `MatterAdapter`) | planck-adapter < 1.7 or matter-adapter < 1.6; upgrade, or `new PlanckAdapter(options)` |
|
|
236
|
+
| `TypeError` reading `renderer` from `plugin.register(SpinePlugin)` | registered before `await app.init()`; the plugin's `app` is still unset |
|
|
207
237
|
| physics behaves differently after adding an adapter | expected — units and force scales differ; re-tune |
|
|
238
|
+
| a Spine object slides off screen when the camera moves, or ignores its container (WebGL / WebGPU) | spine-plugin < 4.0.1 drew without the camera's and the parents' transform; upgrade |
|
|
239
|
+
| a Spine object ignores `setOpacity()` / `alpha` | spine-plugin < 4.0.1; upgrade |
|
|
240
|
+
| a game paused from its menu is running again after the app comes back from the background | capacitor-plugin < 1.0.1 resumed on every return; upgrade |
|
|
241
|
+
| touch controls miss the finger after browser zoom or a move to another screen | gamepad-plugin < 1.2.1; upgrade |
|
|
208
242
|
|
|
209
243
|
## Related skills
|
|
210
244
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-renderables
|
|
3
|
-
description: "Use this skill when subclassing Renderable, writing a custom draw() method, handling pointer or click events on game objects, applying post effects and shaders, or controlling draw order. Covers the pos-relative draw contract, isKinematic and input, addPostEffect vs the deprecated shader property,
|
|
3
|
+
description: "Use this skill when subclassing Renderable, writing a custom draw() method, handling pointer or click events on game objects, applying post effects and shaders, or controlling draw order. Covers the pos-relative draw contract, isKinematic and input, addPostEffect vs the deprecated shader property, z-ordering, y-sorting with sortOn and sortOffset, and Container clipping. Triggers on: Tween, easing, animate, Renderable, extends Renderable, draw(, update(, isKinematic, pointerEvent, registerPointerEvent, onClick, addPostEffect, removePostEffect, ShaderEffect, addChild, z-order, sortOn, sortOffset, autoSort, clipping, clipContains, anchorPoint, floating."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -67,14 +67,19 @@ class Button extends Renderable {
|
|
|
67
67
|
The symptom is a handler that is registered, looks correct, and never fires.
|
|
68
68
|
|
|
69
69
|
Note that `Container`, `Camera2d`, `Draggable`, `DropTarget` and the `UI*`
|
|
70
|
-
elements set it to `false` for you, and so does
|
|
71
|
-
|
|
72
|
-
`Renderable` or `Sprite` subclass with no body is the case that catches
|
|
70
|
+
elements set it to `false` for you, and so does giving a renderable a body (a
|
|
71
|
+
`bodyDef`, on every physics adapter, or a `Body`, which covers `Entity`) — so a
|
|
72
|
+
plain `Renderable` or `Sprite` subclass with no body is the case that catches
|
|
73
|
+
people. Before melonJS 20.14, matter-adapter 1.6.0 and planck-adapter 1.7.0,
|
|
74
|
+
only the built-in adapter cleared it: under matter or planck a renderable with
|
|
75
|
+
a `bodyDef` stayed kinematic and missed pointer events.
|
|
73
76
|
|
|
74
77
|
The same flag has a second, less obvious effect: it is also the opt-in to the
|
|
75
78
|
world **broadphase**. The quadtree/octree insert skips any child with
|
|
76
79
|
`isKinematic === true`, so body-less objects you want `adapter.queryAABB` /
|
|
77
|
-
`querySphere` to find need `isKinematic = false` as well.
|
|
80
|
+
`querySphere` to find need `isKinematic = false` as well. That is the built-in
|
|
81
|
+
adapter; the `queryAABB` of matter, planck and Rapier only ever returns
|
|
82
|
+
renderables with a body. The builtin
|
|
78
83
|
narrowphase likewise requires *both* partners to be non-kinematic.
|
|
79
84
|
|
|
80
85
|
## 3. Draw order: only `addChild(child, z)` works
|
|
@@ -297,6 +302,13 @@ With the flag, the bounds are the children's union, re-measured on every update
|
|
|
297
302
|
and, while the group is off screen, before each culling check — so a squad that
|
|
298
303
|
walks back into view is drawn again.
|
|
299
304
|
|
|
305
|
+
`clipping = true` crops a container's children to its own rectangle, so it
|
|
306
|
+
needs a size of its own: a sizeless container (the default) does not clip.
|
|
307
|
+
Since 20.13 the pointer skips a child where it is cropped away, and
|
|
308
|
+
`container.clipContains(x, y)` tells whether the container shows its children
|
|
309
|
+
at a point (screen pixels for a floating one). A clip inside a clip shows
|
|
310
|
+
their overlap, on WebGL and WebGPU as on Canvas.
|
|
311
|
+
|
|
300
312
|
## Anchors and floating
|
|
301
313
|
|
|
302
314
|
- `anchorPoint` defaults to centre `(0.5, 0.5)` on `Renderable`, but `Container`
|
|
@@ -312,8 +324,10 @@ walks back into view is drawn again.
|
|
|
312
324
|
## Prefer built-in features
|
|
313
325
|
|
|
314
326
|
Before writing custom `draw()` code, check whether the engine already covers it —
|
|
315
|
-
`ParticleEmitter`, `Tween`, the `ShaderEffect` presets,
|
|
316
|
-
`UITextButton`,
|
|
327
|
+
`ParticleEmitter`, `Tween`, the `ShaderEffect` presets, the UI widgets
|
|
328
|
+
(`UITextButton`, `UISpriteElement`, `UIPanel`, `ProgressBar` and the rest, see
|
|
329
|
+
`melonjs-ui-and-text`), `NineSliceSprite`, the physics adapters, and the Tiled
|
|
330
|
+
tilemap renderers all exist.
|
|
317
331
|
Hand-rolled equivalents miss the batching and the multi-backend support.
|
|
318
332
|
|
|
319
333
|
## Symptom → cause
|
|
@@ -334,9 +348,12 @@ Hand-rolled equivalents miss the batching and the multi-backend support.
|
|
|
334
348
|
| `z` unexpectedly 0 after a `set` | `Vector3d.set(x, y)` defaults `z` to 0 — `setPosition(x, y)` on the renderable leaves the depth alone |
|
|
335
349
|
| a settings option appears to do nothing | the class never reads that key — see section 7 |
|
|
336
350
|
| `isRenderable = false` did not hide the object | it gates bounds, not drawing — use `alpha` |
|
|
351
|
+
| `clipping = true` crops nothing | the container has no size of its own: give it one (`resize`) |
|
|
352
|
+
| a child cropped out of a clipping container still takes clicks where it is hidden | before 20.13 the pointer ignored the clip: update |
|
|
337
353
|
|
|
338
354
|
## Related skills
|
|
339
355
|
- `melonjs-scenes-and-state` — `Tween` and the easing families, for animating any of this
|
|
340
356
|
|
|
341
357
|
- `melonjs-getting-started` — Application lifecycle and the scene graph
|
|
358
|
+
- `melonjs-ui-and-text` — the UI widgets, before drawing your own
|
|
342
359
|
- `melonjs-20-migration` — the deprecated `shader` property and other pre-20 APIs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-renderer-backends
|
|
3
|
-
description: "Use this skill when a feature works on one machine and not another, when choosing or pinning a renderer, or when writing code that must degrade gracefully. Covers the WebGPU to WebGL 2 to Canvas fallback ladder, what each backend supports, capability detection, and the subsystems that warn once and then stop working on Canvas. Triggers on: video.AUTO, video.WEBGPU, video.WEBGL, video.CANVAS, renderer.type, supportsDepthBuffer, shaderLanguage, WebGPU, WebGL2, Canvas fallback, context loss, backend, works on my machine."
|
|
3
|
+
description: "Use this skill when a feature works on one machine and not another, when choosing or pinning a renderer, or when writing code that must degrade gracefully. Covers the WebGPU to WebGL 2 to Canvas fallback ladder, what each backend supports, capability detection, and the subsystems that warn once and then stop working on Canvas. Triggers on: video.AUTO, video.WEBGPU, video.WEBGL, video.CANVAS, renderer.type, supportsDepthBuffer, shaderLanguage, WebGPU, WebGL2, Canvas fallback, failIfMajorPerformanceCaveat, software rendering, supportsHDR, hdrOutput, context loss, backend, works on my machine."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -27,6 +27,14 @@ await app.init();
|
|
|
27
27
|
WebGL 1 is not in the ladder — 20.x made WebGL 2 the baseline, and pinning
|
|
28
28
|
`video.WEBGL` **rejects** on a WebGL-1-only device rather than degrading.
|
|
29
29
|
|
|
30
|
+
A GPU backend also counts as unavailable when the browser says it would run
|
|
31
|
+
far slower than native — a software rasterizer, a blocklisted driver. The
|
|
32
|
+
`failIfMajorPerformanceCaveat` setting (`true` by default, stricter than
|
|
33
|
+
WebGL's own default) refuses such a context, and the WebGPU backend refuses a
|
|
34
|
+
software adapter the same way: under `AUTO` that machine gets Canvas, and
|
|
35
|
+
under `video.WEBGL` or `video.WEBGPU` `init()` rejects. Set it `false` to accept
|
|
36
|
+
the slow context instead.
|
|
37
|
+
|
|
30
38
|
## What each backend can do
|
|
31
39
|
|
|
32
40
|
| | WebGPU | WebGL 2 | Canvas |
|
|
@@ -36,21 +44,28 @@ WebGL 1 is not in the ladder — 20.x made WebGL 2 the baseline, and pinning
|
|
|
36
44
|
| shaders and post effects | ✅ | ✅ | ❌ inert |
|
|
37
45
|
| `Light2d` glow (`drawLight`) | ✅ | ✅ | ✅ cached radial gradient |
|
|
38
46
|
| normal-map lighting, `Light3d` | ✅ | ✅ | ❌ inert |
|
|
39
|
-
| `Mesh` | ✅ | ✅ | ⚠️ CPU-projected, no depth |
|
|
47
|
+
| `Mesh` | ✅ | ✅ | ⚠️ last resort: 2D camera only, CPU-projected, no depth, unlit |
|
|
40
48
|
| `Camera3d` / depth sorting | ✅ | ✅ | ❌ warns, renders wrong |
|
|
41
49
|
| `InstancedMesh` | ✅ | ✅ | ⚠️ one draw per instance |
|
|
42
50
|
| GPU tilemap rendering | ✅ | ✅ | ❌ per-tile CPU path |
|
|
51
|
+
| `textureFilter: "linear-mipmap"` | ✅ | ✅ | ❌ no mipmaps |
|
|
52
|
+
| `hdr` (headroom through the effects) | ✅ | ✅ where the driver can render and filter half-float | ❌ |
|
|
53
|
+
| `hdrOutput` (HDR presentation) | ✅ with `hdr` on too | ❌ | ❌ |
|
|
54
|
+
| `state.transition("pixelate", …)` | ✅ | ✅ | ❌ the state changes with no transition |
|
|
43
55
|
|
|
44
|
-
The Canvas column is the problem: none of it throws.
|
|
45
|
-
once and then
|
|
56
|
+
The Canvas column is the problem: none of it throws. Most of those paths warn
|
|
57
|
+
once and then degrade or no-op, and some (the pixelate transition, `hdr`) do
|
|
58
|
+
so without a word, so the game runs and looks wrong.
|
|
46
59
|
|
|
47
60
|
Two Canvas rows are worth spelling out because they are easy to misread as total
|
|
48
61
|
failures. A `Light2d` still *draws* — the Canvas renderer bakes a radial gradient
|
|
49
62
|
per light and blits it — what disappears is per-pixel normal-map shading, which
|
|
50
63
|
warns once (`Light2d normal-map lighting requires a GPU backend`), the first time
|
|
51
|
-
a normal-mapped sprite is drawn under a light, and then renders sprites unlit. A `Mesh` still draws too,
|
|
52
|
-
algorithm and no depth buffer, so convex models look
|
|
53
|
-
self-overlapping
|
|
64
|
+
a normal-mapped sprite is drawn under a light, and then renders sprites unlit. A `Mesh` under a 2D camera still draws too, textured,
|
|
65
|
+
sorted with the painter's algorithm and no depth buffer, so convex models look
|
|
66
|
+
right and a self-overlapping one can draw a few triangles over the wrong ones.
|
|
67
|
+
That is a last resort so a game does not go blank, not a target: anything
|
|
68
|
+
built on meshes should run on WebGPU or WebGL 2. `"none"` (replace, alpha included) is the one blend
|
|
54
69
|
mode Canvas cannot express — there is no `globalCompositeOperation` that
|
|
55
70
|
disables blending for the drawn area alone — so it resolves to `"normal"`, and
|
|
56
71
|
`setBlendMode` reports that back.
|
|
@@ -65,7 +80,9 @@ app.renderer.shaderLanguage; // "wgsl" | "glsl" | null (null on Canvas)
|
|
|
65
80
|
|
|
66
81
|
There are narrower flags for the features that do not travel together:
|
|
67
82
|
`supportsShaderTileLayers`, `supportsRetainedMesh` and `supportsInstancing` —
|
|
68
|
-
all three `false` on Canvas, `true` on both GPU backends.
|
|
83
|
+
all three `false` on Canvas, `true` on both GPU backends. `supportsHDR` and
|
|
84
|
+
`supportsHDROutput` say what the `hdr` and `hdrOutput` settings were actually
|
|
85
|
+
granted, which can be less than was asked. Prefer a capability
|
|
69
86
|
flag over comparing `type` against a string; `type` is an identity check for
|
|
70
87
|
code coupled to one backend's machinery.
|
|
71
88
|
|
|
@@ -160,12 +177,12 @@ or the `#webgpu` / `#webgl` URI fragment on a page you can open by hand.
|
|
|
160
177
|
`OperationError: GPUQueue.copyExternalImageToTexture: External image state is
|
|
161
178
|
not valid`, from a stack that reads `drawImage → addQuad → getBinding`. WebGL
|
|
162
179
|
uploads the same source without complaint, so this is a class of bug that
|
|
163
|
-
cannot appear on one backend and stops the game dead on the other.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
- **3D meshes do not honour blend modes.** The mesh pass draws
|
|
168
|
-
depth/blend state; asking for one of
|
|
180
|
+
cannot appear on one backend and stops the game dead on the other. Check
|
|
181
|
+
any canvas the game sizes from content that can be empty before it is drawn.
|
|
182
|
+
An empty or whitespace-only `Text` did this before 20.11; its glyph canvas
|
|
183
|
+
is now at least 32 pixels on each side.
|
|
184
|
+
- **3D meshes do not honour the advanced blend modes.** The mesh pass draws
|
|
185
|
+
with its own depth/blend state; asking for one of them logs a one-time
|
|
169
186
|
`blend mode "…" is not supported for 3D meshes` warning and falls back to
|
|
170
187
|
`"normal"`. Gradient fills warn the same way.
|
|
171
188
|
|
|
@@ -175,14 +192,18 @@ or the `#webgpu` / `#webgl` URI fragment on a page you can open by hand.
|
|
|
175
192
|
|---|---|
|
|
176
193
|
| effect works locally, not for a user | they got the Canvas fallback, or a different GPU backend |
|
|
177
194
|
| sprites render unlit though lights are in the scene | Canvas fallback — normal-map shading is inert (check the console) |
|
|
178
|
-
| mesh
|
|
195
|
+
| a few triangles of a mesh drawn over the wrong ones where it overlaps itself | Canvas fallback — painter's algorithm sorts whole triangles, no depth buffer |
|
|
196
|
+
| a mesh shows its far side through its near one (looks inside-out) on Canvas | melonJS older than 20.14 sorted the triangles the wrong way round; update |
|
|
197
|
+
| a textured OBJ + MTL mesh is a white silhouette on Canvas | melonJS older than 20.14 filled it with its material color and skipped the texture; update |
|
|
198
|
+
| a low-poly (palette) model looks faceted on Canvas, with faint lines between its triangles | melonJS older than 20.14 filled each of its triangles with one texel; update |
|
|
179
199
|
| shader works on one machine only | GLSL-only effect, AUTO chose WebGPU elsewhere — the console carries the warning |
|
|
180
200
|
| a solid box where an effect should be | carrier drawn while the effect is disabled |
|
|
181
201
|
| nothing renders at all on an old device | `video.WEBGL` pinned; WebGL 1 is rejected, not downgraded |
|
|
182
202
|
| ported shader is vertically flipped | the WebGL capture is bottom-up, the WebGPU one top-down |
|
|
183
|
-
| `copyExternalImageToTexture: External image state is not valid` | a zero-sized upload source
|
|
203
|
+
| `copyExternalImageToTexture: External image state is not valid` | a zero-sized upload source: a canvas sized from empty content (an empty `Text` before 20.11) |
|
|
204
|
+
| Canvas renderer on a machine that has a GPU, or `init()` rejects with a pinned backend | software rendering or a blocklisted driver, refused by `failIfMajorPerformanceCaveat` (default `true`) |
|
|
184
205
|
| runs on the test machine, dies on the dev's | the harness fell back to WebGL and never exercised WebGPU |
|
|
185
|
-
|
|
|
206
|
+
| an advanced blend mode is ignored on a mesh | meshes fall back to `"normal"` for those; look for the one-time warning |
|
|
186
207
|
| textures vanish after the tab is restored | cached raw GPU handles not re-acquired on context restore |
|
|
187
208
|
|
|
188
209
|
## Related skills
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-scenes-and-state
|
|
3
|
-
description: "Use this skill for scene structure and game flow in melonJS — Stage subclasses, the state manager, transitions, pausing, the update loop, timers and tweens. Covers onResetEvent versus the constructor, state.set before state.change, the camera and world lifecycle per stage, and pause-aware timing. Triggers on: Tween, easing, fade out, flash, pulse, punch, shrink back, ease in, ease out, animate over time, countdown in update, Stage, state, state.set, state.change, state.transition, state.pause, onResetEvent, onDestroyEvent, onActivateEvent, onDeactivateEvent, PLAY, MENU, LOADING, update loop, GAME_UPDATE, timer, setTimeout, setInterval, Tween, freeze."
|
|
3
|
+
description: "Use this skill for scene structure and game flow in melonJS — Stage subclasses, the state manager, transitions, pausing, the update loop, timers and tweens. Covers onResetEvent versus the constructor, state.set before state.change, the camera and world lifecycle per stage, and pause-aware timing. Triggers on: Tween, easing, fade out, flash, pulse, punch, shrink back, ease in, ease out, animate over time, countdown in update, Stage, state, state.set, state.change, state.transition, pixelate, Trigger transition, retry level, state.pause, pauseOnBlur, resumeOnFocus, onResetEvent, onDestroyEvent, onActivateEvent, onDeactivateEvent, PLAY, MENU, LOADING, update loop, GAME_UPDATE, timer, setTimeout, setInterval, Tween, freeze."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -59,6 +59,12 @@ was drawn unstepped, and showed the stage's background alone for one frame.
|
|
|
59
59
|
registered for that id, and `state.set` throws if the second argument is not a
|
|
60
60
|
`Stage` instance. Neither fails quietly.
|
|
61
61
|
|
|
62
|
+
A change to the state that is **already current does nothing**: no
|
|
63
|
+
`onDestroyEvent`, no `onResetEvent`, no transition. So a "Retry" that calls
|
|
64
|
+
`state.change(state.PLAY)` from `PLAY` is ignored; reload the level with
|
|
65
|
+
`level.reload()`, or go through another state (a game-over or `READY` screen)
|
|
66
|
+
and back.
|
|
67
|
+
|
|
62
68
|
### Always leave `state.LOADING` — it is transitional
|
|
63
69
|
|
|
64
70
|
`loader.preload()` switches to `state.LOADING` for you. Building your scene in
|
|
@@ -122,11 +128,21 @@ app.freeze(150); // brief hit-stop (proxies state.fr
|
|
|
122
128
|
```
|
|
123
129
|
|
|
124
130
|
`state.transition` is global and sticky — once set it applies to every later
|
|
125
|
-
`state.change
|
|
131
|
+
`state.change`; `state.setTransition(id, false)` turns it off for changes
|
|
132
|
+
into one state.
|
|
133
|
+
The duration (1000 ms when omitted) is for each half: the cover, then the
|
|
134
|
+
reveal. The `"mask"` form needs its fourth argument, an `Ellipse` or
|
|
126
135
|
`Polygon`; without one it warns and falls back to a direct switch. The
|
|
127
136
|
`"pixelate"` form breaks the screen up into blocks (its fourth argument is the
|
|
128
|
-
largest block size in pixels). It is a shader effect,
|
|
129
|
-
nothing and the state changes directly.
|
|
137
|
+
largest block size in pixels; the color is not used). It is a shader effect,
|
|
138
|
+
so on Canvas it does nothing and the state changes directly.
|
|
139
|
+
|
|
140
|
+
A level change is not a state change, so `state.transition` does not apply to
|
|
141
|
+
it. A Tiled `Trigger` that loads a level has a transition of its own, set on
|
|
142
|
+
the trigger: `color` and `duration` (both needed, or the level loads with
|
|
143
|
+
none), and `transition: "fade"` (the default) or `"mask"` with a `shape`.
|
|
144
|
+
A `"mask"` without a `shape` fades instead, without a warning, and there is
|
|
145
|
+
no pixelate form for a trigger. See `melonjs-tilemaps`.
|
|
130
146
|
|
|
131
147
|
A `state.change` asked for while a transition covers the screen (Enter pressed
|
|
132
148
|
twice on a menu, a timer firing as the player skips a screen) needs no guard:
|
|
@@ -149,7 +165,8 @@ this.isDirty = state.current().update(this.updateDelta) || this.isDirty;
|
|
|
149
165
|
Only `Container.update` consults the pause — it reads `state.isPaused()` and
|
|
150
166
|
skips every child that is not `updateWhenPaused`. So world children (entities,
|
|
151
167
|
sprites, meshes, a `GLTFModel`) freeze correctly, while **the Stage subclass's
|
|
152
|
-
own `update()` keeps running**.
|
|
168
|
+
own `update()` keeps running**. (`event.GAME_UPDATE` is not emitted while
|
|
169
|
+
paused, so logic hung off it stops with the world.)
|
|
153
170
|
|
|
154
171
|
A game that keeps its logic in entities pauses for free and never notices. A
|
|
155
172
|
game that drives the simulation from `Stage.update` — common for an endless
|
|
@@ -175,6 +192,10 @@ Worth knowing that `pauseOnBlur` (default `true`) fires on a window `blur`, not
|
|
|
175
192
|
only on tab `visibilitychange` — so clicking another window on the same screen
|
|
176
193
|
triggers it while the canvas is still fully visible.
|
|
177
194
|
|
|
195
|
+
On focus, `resumeOnFocus` (default `true`) undoes only the pause the
|
|
196
|
+
application applied on blur. A game the player paused from its own menu stays
|
|
197
|
+
paused when the window comes back; before 20.9 it resumed underneath the menu.
|
|
198
|
+
|
|
178
199
|
## The update loop
|
|
179
200
|
|
|
180
201
|
There is no loop you own. Logic goes in `update(dt)` overrides, or a
|
|
@@ -422,6 +443,10 @@ both.
|
|
|
422
443
|
|---|---|
|
|
423
444
|
| scene builds once and is empty on return | content created in the constructor, not `onResetEvent` |
|
|
424
445
|
| `Undefined Stage for state 'N'` thrown | no `state.set` for that id first |
|
|
446
|
+
| `state.change(state.PLAY)` from `PLAY` does nothing ("Retry" ignored) | a change to the current state is a no-op — `level.reload()`, or go through another state |
|
|
447
|
+
| a level change through a `Trigger` ignores `state.transition` | it is a level change, not a state change: set `color` and `duration` (and `transition`) on the trigger |
|
|
448
|
+
| the game stops updating for good after one very slow update, such as a heavy first frame (20.12 and 20.13) | the loop could no longer save enough time to step the world: update |
|
|
449
|
+
| the game resumes under its own pause menu after a tab switch (before 20.9) | focus undid any pause; update, only the blur pause is undone |
|
|
425
450
|
| a tween finishes in 1000 ms whatever you pass | `to()` takes `{ duration }`, not a number |
|
|
426
451
|
| object frozen while its state updates | `update()` not returning `true` |
|
|
427
452
|
| timers fire behind a pause menu | `window.setTimeout` instead of `timer.setTimeout` |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-sprites-and-animation
|
|
3
|
-
description: "Use this skill when creating sprites, playing frame animations, or working with texture atlases in melonJS. Covers Sprite construction from a plain image or an atlas region, addAnimation/setCurrentAnimation, TextureAtlas for TexturePacker/ShoeBox/Aseprite, tint, flip, NineSliceSprite, and the centred anchorPoint that offsets naive positioning. Triggers on: Sprite, new Sprite, addAnimation, setCurrentAnimation, isCurrentAnimation, TextureAtlas, TexturePacker, Aseprite, ShoeBox, atlas, region, framewidth, frameheight, tint, flipX, flipY, NineSliceSprite, animation."
|
|
3
|
+
description: "Use this skill when creating sprites, playing frame animations, or working with texture atlases in melonJS. Covers Sprite construction from a plain image or an atlas region, addAnimation/setCurrentAnimation, TextureAtlas for TexturePacker/ShoeBox/Aseprite, tint, flip, NineSliceSprite, and the centred anchorPoint that offsets naive positioning. Triggers on: Sprite, new Sprite, addAnimation, setCurrentAnimation, isCurrentAnimation, onFrame, onComplete, play, pause, flicker, anchorPoint, TextureAtlas, TexturePacker, Aseprite, ShoeBox, atlas, region, framewidth, frameheight, tint, flipX, flipY, NineSliceSprite, animation."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -20,6 +20,10 @@ sprites appear to disagree.
|
|
|
20
20
|
const s = new Sprite(100, 100, { image: "hero", framewidth: 64, frameheight: 64 });
|
|
21
21
|
|
|
22
22
|
s.anchorPoint.set(0, 0); // switch to top-left positioning if you prefer
|
|
23
|
+
|
|
24
|
+
// or in the settings, by name: "top-left", "bottom" (the feet), "center",
|
|
25
|
+
// "top", "left", "right", "top-right", "bottom-left", "bottom-right"
|
|
26
|
+
const hero = new Sprite(x, y, { image: "hero", anchorPoint: "bottom" });
|
|
23
27
|
```
|
|
24
28
|
|
|
25
29
|
Writing top-left maths against the centred default puts everything half a sprite
|
|
@@ -93,7 +97,16 @@ attack) needs no manual switch back to idle.
|
|
|
93
97
|
|
|
94
98
|
Re-selecting the animation that is already current is a no-op, so calling
|
|
95
99
|
`setCurrentAnimation` every frame is safe, and an `isCurrentAnimation` check
|
|
96
|
-
around it adds nothing.
|
|
100
|
+
around it adds nothing. **The exception is a `loop: false` run that has
|
|
101
|
+
finished**: selecting it again plays it again, so a death animation selected
|
|
102
|
+
every frame replays forever. Select a play-once animation once, on the event
|
|
103
|
+
that starts it. An unknown id throws.
|
|
104
|
+
|
|
105
|
+
A frame can carry its own delay, as `{ name, delay }` in place of the index or
|
|
106
|
+
region name: `[{ name: 3, delay: 200 }, { name: 4, delay: 100 }]`.
|
|
107
|
+
`play(name?, options?)` switches and plays (or, with no name, resumes),
|
|
108
|
+
`pause()` freezes the current frame and `stop()` rewinds to the first one and
|
|
109
|
+
pauses there; the options are `setCurrentAnimation`'s.
|
|
97
110
|
|
|
98
111
|
`addAnimation` with an id that exists replaces it, which is how you change an
|
|
99
112
|
animation's frames or speed (`hero.addAnimation("walk", [4, 5, 6, 7], 200)`).
|
|
@@ -104,6 +117,11 @@ finished `loop: false` run holds the new last frame, and the
|
|
|
104
117
|
a shorter strip threw on the next `update()` and a longer one never reached its
|
|
105
118
|
new frames.
|
|
106
119
|
|
|
120
|
+
Since 20.14, a frame the texture does not have (a number past the end of the
|
|
121
|
+
sheet, a name the atlas does not hold) is left out with a console warning
|
|
122
|
+
naming it, and the frames that exist play in order; `addAnimation` returns how
|
|
123
|
+
many it kept.
|
|
124
|
+
|
|
107
125
|
## Texture atlases
|
|
108
126
|
|
|
109
127
|
`TextureAtlas` reads several formats:
|
|
@@ -197,7 +215,10 @@ Two consequences:
|
|
|
197
215
|
| event handlers leak on pooled objects | bound in the constructor rather than `onActivateEvent` |
|
|
198
216
|
| animation never advances | no `framewidth`/`frameheight`, so the sheet is one frame |
|
|
199
217
|
| animation plays once and stops | `{ loop: false }`, or a callback that returned `false` |
|
|
218
|
+
| a `loop: false` animation keeps replaying | `setCurrentAnimation` called every frame: a finished play-once run is restarted by selecting it again |
|
|
219
|
+
| a one-frame animation never calls its `onComplete` | a looping one-frame animation does not step; play it with `loop: false` or `next` (before 20.12 even those never completed) |
|
|
200
220
|
| `TypeError` reading `offset` or `delay` in `update()` after re-adding the playing animation with fewer frames, or a longer one never shows its new frames | before 20.13 redefining the current animation kept its old frame count; upgrade |
|
|
221
|
+
| a console warning `addAnimation("walk"): no frame … in the texture`, or a `TypeError` reading `delay` / `offset` on an older melonJS | a frame number or name the sheet or atlas does not have (often an atlas re-exported without one of its images): fix the frame list or re-export the atlas |
|
|
201
222
|
| `this` is wrong in a `flicker(ms, callback)` callback, or a flicker started from it stops at once | before 20.13.0 the callback ran on an internal object and the flicker was stopped after it; now `this` is the sprite and the flicker has already ended |
|
|
202
223
|
| sprites do not batch | more distinct textures in one frame than `renderer.maxTextures` |
|
|
203
224
|
| nine-slice corners stretch anyway | `insetX`/`insetY` instead of `insetx`/`insety` |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-tilemaps
|
|
3
|
-
description: "Use this skill for Tiled maps in melonJS — loading TMX/TSX levels, spawning entities from Tiled objects, collision shapes authored in Tiled, isometric and hexagonal maps, and image layers, and reading or changing tiles while the game runs. Covers registerTiledObjectClass and the pool.register name contract, camera bounds, compressed maps needing the inflate plugin, and the level director API. Triggers on: Tiled, TMX, TSX, tilemap, setTile, clearTile, cellAt, getTile, getTileById, change a tile, tile at run time, game board, grid, tile properties at run time, getTileProperties, map properties, level.load, level.load async, await level.load, tileset, ImageLayer, isometric, hexagonal, staggered, pool.register, Collectable, Trigger, object layer, collision layer, parallax, registerTiledObjectClass, registerTiledObjectFactory, TMXObject, resolvedProperty, resolvedProperties, tile class, tile properties."
|
|
3
|
+
description: "Use this skill for Tiled maps in melonJS — loading TMX/TSX levels, spawning entities from Tiled objects, collision shapes authored in Tiled, isometric and hexagonal maps, and image layers, and reading or changing tiles while the game runs. Covers registerTiledObjectClass and the pool.register name contract, camera bounds, compressed maps needing the inflate plugin, and the level director API. Triggers on: Tiled, TMX, TSX, tilemap, setTile, clearTile, cellAt, getTile, getTileById, change a tile, tile at run time, game board, grid, tile properties at run time, getTileProperties, map properties, level.load, level.load async, await level.load, tileset, ImageLayer, infinite map, render order, isometric, hexagonal, staggered, pool.register, Collectable, Trigger, object layer, collision layer, parallax, registerTiledObjectClass, registerTiledObjectFactory, TMXObject, resolvedProperty, resolvedProperties, tile class, tile properties."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -25,12 +25,29 @@ await loader.preload([
|
|
|
25
25
|
level.load("map1"); // inside Stage.onResetEvent
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
**A game with several maps** keeps each map and the images only it uses in a
|
|
29
|
+
bundle of its own, beside a `common` bundle for the shared tileset, the player
|
|
30
|
+
and the sounds: an assets file exporting `{ common: [...], map1: [...],
|
|
31
|
+
map2: [...] }`, passed as it is to `loader.preload()`. A map's level can then be
|
|
32
|
+
freed with `loader.unload("map1")` and brought back with
|
|
33
|
+
`await loader.load("map1")` before `level.load("map1")`; a game whose maps
|
|
34
|
+
loop back simply keeps them all loaded. `melonjs-loading-assets` covers the
|
|
35
|
+
pattern.
|
|
36
|
+
|
|
28
37
|
Asset `type` is `"tmx"` for maps and `"tsx"` for external tilesets — those are
|
|
29
38
|
the only two type strings the loader knows. The *file* can be `.tmx` / `.tsx`
|
|
30
39
|
(XML), or `.json` / `.tmj` / `.tsj`; the parser branches on the extension, not
|
|
31
40
|
on the type. `type: "tmj"` throws `unknown or invalid resource type`. You can
|
|
32
|
-
also skip `src` and pass the map inline via `data
|
|
33
|
-
|
|
41
|
+
also skip `src` and pass the map inline via `data`, as the parsed JSON object
|
|
42
|
+
of a Tiled JSON export (not an XML string).
|
|
43
|
+
|
|
44
|
+
**Prefer JSON where there is no DOM.** XML maps and tilesets are parsed with
|
|
45
|
+
`DOMParser`, which only a page has: in a Web Worker, or on nx.js (the Nintendo
|
|
46
|
+
Switch, see `melonjs-deployment`), loading one fails, and `preload()` rejects
|
|
47
|
+
with "Failed loading resource …" whose `cause` (also logged to the console)
|
|
48
|
+
says to export it as JSON. Export the map *and* its external tilesets as
|
|
49
|
+
`.tmj` / `.tsj` (Tiled: File → Export As, or set JSON as the format in the
|
|
50
|
+
project).
|
|
34
51
|
|
|
35
52
|
`level.load(levelId, options)` accepts `container` (default `game.world`),
|
|
36
53
|
`onLoaded` (default `game.onLevelLoaded`), `flatten` (default `game.mergeGroup`),
|
|
@@ -178,9 +195,12 @@ authored against melonJS 1.x finds them where it looks.
|
|
|
178
195
|
|
|
179
196
|
A `Trigger` forwards a **fixed list** of settings to `level.load()` — not
|
|
180
197
|
whatever you pass it. Today that list is `container`, `onLoaded`, `flatten`,
|
|
181
|
-
`setViewportBounds` and the glTF scene options
|
|
182
|
-
`lights`, `lightIntensityScale
|
|
183
|
-
|
|
198
|
+
`setViewportBounds` and the glTF scene options: `scale`, `rightHanded`,
|
|
199
|
+
`lights`, `lightIntensityScale` and the ground-shadow settings
|
|
200
|
+
(`castGroundShadow`, `shadowGroundY`, `shadowOpacity`, `shadowScale`,
|
|
201
|
+
`shadowOffset`, `shadowStretch`, `shadowDirectionX`, `shadowDirectionZ`,
|
|
202
|
+
`shadowGroundNormal`, `shadowLight`, but not `shadowGroundPoint`); see the 3D
|
|
203
|
+
assets skill. Anything outside it is **dropped silently**, which is worth
|
|
184
204
|
knowing before you spend an afternoon on a map that loads at the wrong scale.
|
|
185
205
|
`async` is excluded deliberately — the trigger needs to sequence its own
|
|
186
206
|
transition.
|
|
@@ -224,8 +244,17 @@ for (let row = board.starty; row < board.starty + board.rows; row++) {
|
|
|
224
244
|
}
|
|
225
245
|
```
|
|
226
246
|
|
|
227
|
-
|
|
228
|
-
|
|
247
|
+
An infinite map's `getBounds()` (and its `width` / `height`) covers the map's
|
|
248
|
+
own area and every painted tile, so a level loaded with `setViewportBounds`
|
|
249
|
+
lets the camera reach tiles painted past the map's size, or left of or above
|
|
250
|
+
its origin. Empty chunk cells past the last painted tile are not part of it.
|
|
251
|
+
Size a camera you set up yourself from `map.getBounds()`, not from
|
|
252
|
+
`cols * tilewidth`, which is only right for an orthogonal map:
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
const b = level.getCurrentLevel().getBounds();
|
|
256
|
+
app.viewport.setBounds(b.left, b.top, b.width, b.height);
|
|
257
|
+
```
|
|
229
258
|
|
|
230
259
|
A `Tile` carries `tileId` (its **global** id), `col`, `row` and `tileset`.
|
|
231
260
|
Tiled shows a tile's *local* id in the tileset editor; the global id is
|
|
@@ -271,7 +300,8 @@ their own (`title`, not `name`).
|
|
|
271
300
|
|
|
272
301
|
## Camera bounds
|
|
273
302
|
|
|
274
|
-
`level.load` sets the viewport bounds to the map
|
|
303
|
+
`level.load` sets the viewport bounds to the map's bounds for you (on an
|
|
304
|
+
infinite map, every painted tile, see above) —
|
|
275
305
|
`setViewportBounds` defaults to `true`, and the handler is re-run on every
|
|
276
306
|
viewport resize, centring the map when it is smaller than the screen. You only
|
|
277
307
|
need to do it by hand when you opted out:
|
|
@@ -415,14 +445,17 @@ per-pixel GID lookup in the fragment shader. It requires **all** of:
|
|
|
415
445
|
- `world.gpuTilemap !== false` (the `gpuTilemap` application setting, default `true`),
|
|
416
446
|
- an **orthogonal** layer — isometric, hexagonal, staggered and oblique always
|
|
417
447
|
fall back,
|
|
418
|
-
- no collection-of-image tileset, no non-zero `tileoffset`,
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
448
|
+
- no collection-of-image tileset, no non-zero `tileoffset`, a tileset tile
|
|
449
|
+
render size of "Tile size" (`tilerendersize`, not "Map grid size"), and tile
|
|
450
|
+
overflow within the shader's 4-cell limit.
|
|
451
|
+
|
|
452
|
+
When a layer falls back, melonJS `console.warn`s the reason each time the
|
|
453
|
+
layer is activated (once per page when the renderer has no GPU tile support at
|
|
454
|
+
all), and says nothing while `gpuTilemap` is off. Practical consequences:
|
|
455
|
+
fewer tilesets means fewer batches (one packed tileset beats several small
|
|
456
|
+
ones), and on the Canvas fallback large maps get noticeably slower.
|
|
457
|
+
`world.preRender` (or a `preRender` layer property in Tiled) opts an unanimated
|
|
458
|
+
layer into the offscreen-bake path instead.
|
|
426
459
|
|
|
427
460
|
## Symptom → cause
|
|
428
461
|
|
|
@@ -432,7 +465,7 @@ unanimated layer into the offscreen-bake path instead.
|
|
|
432
465
|
| a Tiled object becomes a plain shape with no behaviour | its class/name does not match any registered factory, or it was registered after `level.load` |
|
|
433
466
|
| the world is still empty right after `level.load` | the load is deferred to a timer while the loop runs — `await level.load(id, { async: true })`, or use `onLoaded` / `LEVEL_LOADED` |
|
|
434
467
|
| `await level.load(id)` returned `true` rather than a promise | without `async: true` the call returns a boolean; `await true` resolves immediately and the load has not run yet — pass the flag when you mean to await |
|
|
435
|
-
| `level <id> not found` | the map was never preloaded, or the asset `name` differs from the id passed to `load` |
|
|
468
|
+
| `level <id> not found` | the map was never preloaded, its bundle was unloaded (`await loader.load(bundleId)` first), or the asset `name` differs from the id passed to `load` |
|
|
436
469
|
| `unknown or invalid resource type` | asset `type` set to `"tmj"` / `"tsj"` — use `"tmx"` / `"tsx"` with the `.tmj` / `.tsj` file |
|
|
437
470
|
| camera will not scroll | `setViewportBounds: false`, or the map was added with `addTo()` (which defaults to `false`) |
|
|
438
471
|
| `No inflate function set` at parse time | compressed layers without the tiled-inflate plugin |
|
|
@@ -446,12 +479,17 @@ unanimated layer into the offscreen-bake path instead.
|
|
|
446
479
|
| a property set on a tile is undefined on the object's `settings` | tile properties are not copied onto `settings`: read them with `settings.resolvedProperty(name)` |
|
|
447
480
|
| objects placed from a classed tile come out as plain sprites | that class is not registered, it is a built-in name, or the object has a class or a registered name of its own, which come first |
|
|
448
481
|
| missing tiles or a blank map | the `.tsx` or tileset image was not preloaded |
|
|
482
|
+
| a map fails to load with "needs DOMParser" (in the error's `cause` and the console) | an XML `.tmx` / `.tsx` loaded where there is no DOM (a Web Worker, nx.js): export the map and its tilesets as JSON (`.tmj` / `.tsj`) |
|
|
449
483
|
| a tileset's transparent color (`trans`) still shows (the console says it could not be applied) | the image cannot be read back: an image the game made itself from another origin without CORS (an `<img>` with no `crossOrigin`) cannot be, so load it with the loader, which only loads an image another origin's server allows (its CORS headers) and can always read back; and a compressed texture cannot be changed: export it with an alpha channel instead |
|
|
450
484
|
| `cellAt` returns `null` (or the wrong tile) for a cell that has one | it was given pixels: `cellAt`, `setTile` and `clearTile` take column and row, only `getTile` takes world pixels |
|
|
451
485
|
| `setTile` shows the wrong tile, or nothing | it was given Tiled's local tile id: use the global id, `tileset.firstgid + localId`, through `getTileById` |
|
|
486
|
+
| on an infinite map the camera stops before tiles painted past the map's size, or left of or above its origin | an older melonJS held the camera to the map's width and height from (0, 0): update; and size a camera you set up yourself from `map.getBounds()` |
|
|
487
|
+
| oversized tiles overlap in the wrong order (the map's render order is not "right-down") | an older melonJS drew every layer "right-down", whatever the map's render order: update |
|
|
452
488
|
| an infinite map's tiles sit in the wrong row, or tiles left of or above the origin are missing | an older melonJS ignored where the chunks start (update); and loop from `layer.startx` / `layer.starty`, not 0, to reach cells left of or above the origin |
|
|
453
489
|
| on a staggered map `getTile` gives the neighbouring tile on every row but the first, or a scrolled view draws nothing | a bug of older melonJS versions in the staggered renderer: update |
|
|
454
490
|
| on an isometric (or staggered, oblique) map `getTile` gives an edge tile for a point just outside the tiles | an older melonJS rounded a fractional column or row toward 0: update |
|
|
491
|
+
| on an isometric map a tile drawn by hand with the map renderer's `drawTile()` lands away from where the layer draws that cell | before 20.14 the isometric `drawTile()` placed it differently from the layer: update |
|
|
492
|
+
| a repeating `ImageLayer` slides off the screen once the camera has moved far enough | before 20.12 its repeat did not follow the camera: update |
|
|
455
493
|
| tiles of a second tileset draw from the first tileset's image (that first one has a `margin`) | an older melonJS let its gid range overrun into the next tileset (update): the range is the `tilecount` Tiled saved |
|
|
456
494
|
| a layer is `undefined` from `getChildByName(name)[0]` | the level is not in the world yet (await `level.load(id, { async: true })`), or the name differs from the layer's in Tiled |
|
|
457
495
|
| a tile set or cleared at run time does not block or free movement | tile layers carry no collision: collision comes from collision object layers |
|