melonjs 20.11.0 → 20.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/build/application/application.d.ts.map +1 -1
- package/build/application/settings.d.ts +6 -2
- package/build/application/settings.d.ts.map +1 -1
- package/build/audio/backend/sound.d.ts.map +1 -1
- package/build/audio/playback.d.ts.map +1 -1
- package/build/audio/spatial.d.ts +13 -2
- package/build/audio/spatial.d.ts.map +1 -1
- package/build/audio/types.d.ts +25 -3
- package/build/audio/types.d.ts.map +1 -1
- package/build/camera/camera2d.d.ts +2 -0
- package/build/camera/camera2d.d.ts.map +1 -1
- package/build/camera/camera3d.d.ts.map +1 -1
- package/build/camera/effects/fade_effect.d.ts.map +1 -1
- package/build/camera/effects/pixelate_transition_effect.d.ts +67 -0
- package/build/camera/effects/pixelate_transition_effect.d.ts.map +1 -0
- package/build/geometries/ellipse.d.ts +11 -4
- package/build/geometries/ellipse.d.ts.map +1 -1
- package/build/geometries/line.d.ts +2 -2
- package/build/geometries/line.d.ts.map +1 -1
- package/build/geometries/polygon.d.ts +5 -4
- package/build/geometries/polygon.d.ts.map +1 -1
- package/build/geometries/rectangle.d.ts +2 -2
- package/build/geometries/rectangle.d.ts.map +1 -1
- package/build/geometries/roundrect.d.ts +2 -2
- package/build/geometries/roundrect.d.ts.map +1 -1
- package/build/index.d.ts +14 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +12716 -7233
- package/build/index.js.map +4 -4
- package/build/input/gamepad.d.ts.map +1 -1
- package/build/input/pointerevent.d.ts.map +1 -1
- package/build/lang/console.d.ts +16 -1
- package/build/lang/console.d.ts.map +1 -1
- package/build/level/tiled/TMXLayer.d.ts +48 -14
- package/build/level/tiled/TMXLayer.d.ts.map +1 -1
- package/build/level/tiled/TMXTileMap.d.ts +1 -1
- package/build/level/tiled/TMXTileMap.d.ts.map +1 -1
- package/build/level/tiled/TMXTileset.d.ts.map +1 -1
- package/build/level/tiled/TMXUtils.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.map +1 -1
- package/build/level/tiled/renderer/TMXOrthogonalRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXRenderer.d.ts +1 -1
- package/build/level/tiled/renderer/TMXRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXStaggeredRenderer.d.ts.map +1 -1
- package/build/loader/loader.d.ts +55 -11
- package/build/loader/loader.d.ts.map +1 -1
- package/build/loader/loadingscreen.d.ts.map +1 -1
- package/build/math/color.d.ts.map +1 -1
- package/build/math/math.d.ts +6 -1
- package/build/math/math.d.ts.map +1 -1
- package/build/math/matrix2d.d.ts +3 -2
- package/build/math/matrix2d.d.ts.map +1 -1
- package/build/math/matrix3d.d.ts +4 -1
- package/build/math/matrix3d.d.ts.map +1 -1
- package/build/math/observableVector2d.d.ts +6 -6
- package/build/math/observableVector2d.d.ts.map +1 -1
- package/build/math/observableVector3d.d.ts +8 -8
- package/build/math/observableVector3d.d.ts.map +1 -1
- package/build/particles/emitter.d.ts +15 -3
- package/build/particles/emitter.d.ts.map +1 -1
- package/build/particles/particle.d.ts +6 -0
- package/build/particles/particle.d.ts.map +1 -1
- package/build/particles/settings.d.ts +22 -5
- package/build/particles/settings.d.ts.map +1 -1
- package/build/physics/bounds.d.ts +5 -5
- package/build/physics/bounds.d.ts.map +1 -1
- package/build/physics/builtin/body.d.ts +15 -4
- package/build/physics/builtin/body.d.ts.map +1 -1
- 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.map +1 -1
- package/build/renderable/animation.d.ts +6 -1
- package/build/renderable/animation.d.ts.map +1 -1
- package/build/renderable/container.d.ts +40 -6
- package/build/renderable/container.d.ts.map +1 -1
- package/build/renderable/entity/entity.d.ts.map +1 -1
- package/build/renderable/frameAnimation.d.ts.map +1 -1
- package/build/renderable/imagelayer.d.ts.map +1 -1
- package/build/renderable/instanced_mesh.d.ts.map +1 -1
- package/build/renderable/mesh.d.ts.map +1 -1
- package/build/renderable/renderable.d.ts.map +1 -1
- package/build/renderable/sprite.d.ts +8 -2
- package/build/renderable/sprite.d.ts.map +1 -1
- package/build/renderable/text/bitmaptext.d.ts +12 -3
- package/build/renderable/text/bitmaptext.d.ts.map +1 -1
- package/build/renderable/text/text.d.ts +147 -4
- package/build/renderable/text/text.d.ts.map +1 -1
- package/build/renderable/text/textmetrics.d.ts +10 -2
- package/build/renderable/text/textmetrics.d.ts.map +1 -1
- package/build/renderable/text/textstyle.d.ts.map +1 -1
- package/build/renderable/trail3d.d.ts.map +1 -1
- package/build/renderable/ui/panelpaint.d.ts +5 -1
- package/build/renderable/ui/panelpaint.d.ts.map +1 -1
- package/build/renderable/ui/progressbar.d.ts +267 -95
- package/build/renderable/ui/progressbar.d.ts.map +1 -1
- package/build/renderable/ui/uibaseelement.d.ts +436 -50
- package/build/renderable/ui/uibaseelement.d.ts.map +1 -1
- package/build/renderable/ui/uicheckbox.d.ts +383 -0
- package/build/renderable/ui/uicheckbox.d.ts.map +1 -0
- package/build/renderable/ui/uifocus.d.ts +6 -0
- package/build/renderable/ui/uifocus.d.ts.map +1 -0
- package/build/renderable/ui/uilabel.d.ts +31 -0
- package/build/renderable/ui/uilabel.d.ts.map +1 -0
- package/build/renderable/ui/uipanel.d.ts +589 -116
- package/build/renderable/ui/uipanel.d.ts.map +1 -1
- package/build/renderable/ui/uiskin.d.ts +25 -0
- package/build/renderable/ui/uiskin.d.ts.map +1 -0
- package/build/renderable/ui/uislider.d.ts +518 -0
- package/build/renderable/ui/uislider.d.ts.map +1 -0
- package/build/renderable/ui/uispriteelement.d.ts +161 -85
- package/build/renderable/ui/uispriteelement.d.ts.map +1 -1
- package/build/renderable/ui/uitextbutton.d.ts +369 -63
- package/build/renderable/ui/uitextbutton.d.ts.map +1 -1
- package/build/state/stage.d.ts +3 -2
- package/build/state/stage.d.ts.map +1 -1
- package/build/state/state.d.ts +31 -6
- package/build/state/state.d.ts.map +1 -1
- package/build/system/legacy_pool.d.ts.map +1 -1
- package/build/system/pool.d.ts.map +1 -1
- package/build/system/save.d.ts +2 -1
- package/build/system/save.d.ts.map +1 -1
- package/build/tweens/tween.d.ts +5 -2
- package/build/tweens/tween.d.ts.map +1 -1
- package/build/utils/fetchdata.d.ts.map +1 -1
- package/build/video/canvas/canvas_renderer.d.ts +1 -5
- package/build/video/canvas/canvas_renderer.d.ts.map +1 -1
- package/build/video/effects/bloom.d.ts +3 -2
- package/build/video/effects/bloom.d.ts.map +1 -1
- package/build/video/effects/blur.d.ts +1 -1
- package/build/video/effects/chromaticAberration.d.ts +1 -1
- package/build/video/effects/dissolve.d.ts.map +1 -1
- package/build/video/effects/dropShadow.d.ts +1 -1
- package/build/video/effects/glow.d.ts +1 -1
- package/build/video/effects/glsl_realization.d.ts.map +1 -1
- package/build/video/effects/hologram.d.ts.map +1 -1
- package/build/video/effects/local_space.d.ts +19 -0
- package/build/video/effects/local_space.d.ts.map +1 -0
- package/build/video/effects/mesh_splice.d.ts.map +1 -1
- package/build/video/effects/outline.d.ts +1 -1
- package/build/video/effects/pixelate.d.ts +1 -1
- package/build/video/effects/radialGradient.d.ts +4 -12
- package/build/video/effects/radialGradient.d.ts.map +1 -1
- package/build/video/effects/scanline.d.ts.map +1 -1
- package/build/video/effects/shadereffect.d.ts.map +1 -1
- package/build/video/effects/shine.d.ts +5 -0
- package/build/video/effects/shine.d.ts.map +1 -1
- package/build/video/effects/sway.d.ts +9 -4
- package/build/video/effects/sway.d.ts.map +1 -1
- package/build/video/effects/vignette.d.ts.map +1 -1
- package/build/video/effects/wave.d.ts +9 -7
- package/build/video/effects/wave.d.ts.map +1 -1
- package/build/video/effects/wgsl/parse.d.ts.map +1 -1
- package/build/video/effects/wgsl/scaffold.d.ts.map +1 -1
- package/build/video/effects/wgsl_realization.d.ts.map +1 -1
- package/build/video/renderer.d.ts +22 -5
- package/build/video/renderer.d.ts.map +1 -1
- package/build/video/renderstate.d.ts +26 -3
- package/build/video/renderstate.d.ts.map +1 -1
- package/build/video/rendertarget/webgpurendertarget.d.ts +1 -0
- package/build/video/rendertarget/webgpurendertarget.d.ts.map +1 -1
- package/build/video/texture/atlas.d.ts +31 -31
- package/build/video/texture/atlas.d.ts.map +1 -1
- package/build/video/utils/pixel_snap.d.ts +44 -0
- package/build/video/utils/pixel_snap.d.ts.map +1 -0
- package/build/video/utils/tessellation.d.ts +11 -0
- package/build/video/utils/tessellation.d.ts.map +1 -1
- package/build/video/webgl/batchers/lit_quad_batcher.d.ts.map +1 -1
- package/build/video/webgl/batchers/mesh_batcher.d.ts.map +1 -1
- package/build/video/webgl/batchers/quad_batcher.d.ts +2 -1
- package/build/video/webgl/batchers/quad_batcher.d.ts.map +1 -1
- package/build/video/webgl/renderers/tmxlayer/orthogonal.d.ts.map +1 -1
- package/build/video/webgl/webgl_renderer.d.ts +3 -6
- package/build/video/webgl/webgl_renderer.d.ts.map +1 -1
- package/build/video/webgpu/batchers/mesh_batcher.d.ts +0 -1
- package/build/video/webgpu/batchers/mesh_batcher.d.ts.map +1 -1
- package/build/video/webgpu/batchers/primitive_batcher.d.ts.map +1 -1
- package/build/video/webgpu/batchers/quad_batcher.d.ts.map +1 -1
- package/build/video/webgpu/effect_binding.d.ts.map +1 -1
- package/build/video/webgpu/renderers/tmxlayer/orthogonal.d.ts.map +1 -1
- package/build/video/webgpu/webgpu_renderer.d.ts +6 -10
- package/build/video/webgpu/webgpu_renderer.d.ts.map +1 -1
- package/package.json +1 -1
- package/skills/AGENTS.md +2 -1
- package/skills/melonjs/SKILL.md +3 -2
- package/skills/melonjs-audio/SKILL.md +31 -8
- package/skills/melonjs-camera-and-drawing/SKILL.md +41 -1
- package/skills/melonjs-display-and-scaling/SKILL.md +19 -5
- package/skills/melonjs-effects-and-shaders/SKILL.md +53 -17
- package/skills/melonjs-events/SKILL.md +3 -1
- package/skills/melonjs-getting-started/SKILL.md +1 -2
- package/skills/melonjs-input/SKILL.md +61 -3
- package/skills/melonjs-lighting/SKILL.md +4 -3
- package/skills/melonjs-loading-assets/SKILL.md +57 -15
- package/skills/melonjs-math/SKILL.md +14 -3
- package/skills/melonjs-particles-and-trails/SKILL.md +72 -8
- package/skills/melonjs-performance/SKILL.md +3 -0
- package/skills/melonjs-physics/SKILL.md +53 -4
- package/skills/melonjs-plugins/SKILL.md +25 -1
- package/skills/melonjs-renderables/SKILL.md +11 -3
- package/skills/melonjs-renderer-backends/SKILL.md +2 -2
- package/skills/melonjs-scenes-and-state/SKILL.md +32 -2
- package/skills/melonjs-sprites-and-animation/SKILL.md +14 -0
- package/skills/melonjs-tilemaps/SKILL.md +96 -2
- package/skills/melonjs-ui-and-text/SKILL.md +560 -23
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-camera-and-drawing
|
|
3
|
-
description: "Use this skill for camera control and immediate-mode drawing in melonJS — following a target, viewport bounds, shake and fade, secondary cameras and what each one draws, and drawing shapes, lines and gradients inside a custom draw(). Covers world versus screen coordinate conversion, clipping and masking, baking with CanvasRenderTarget, and reading pixels back. Triggers on: Camera2d, viewport, follow, offset, look ahead, setBounds, minimap, second camera, split screen, cullMask, visibilityLayer, hide from a camera, shake, fadeIn, fadeOut, worldToLocal, localToWorld, colorMatrix, renderer.fill, renderer.stroke, Rect, Ellipse, Polygon, Line, Gradient, clipRect, mask, CanvasRenderTarget, NoiseTexture2d, lookAt, setBasis, toImageData, getImageData, toBlob, toDataURL, screenshot, read pixels."
|
|
3
|
+
description: "Use this skill for camera control and immediate-mode drawing in melonJS — following a target, viewport bounds, shake and fade, secondary cameras and what each one draws, and drawing shapes, lines and gradients inside a custom draw(). Covers world versus screen coordinate conversion, clipping and masking, baking with CanvasRenderTarget, and reading pixels back. Triggers on: Camera2d, viewport, follow, offset, look ahead, setBounds, minimap, second camera, split screen, cullMask, visibilityLayer, hide from a camera, shake, fadeIn, fadeOut, worldToLocal, localToWorld, colorMatrix, renderer.fill, renderer.stroke, setGlobalAlpha, fillArc, Rect, Ellipse, Polygon, Line, Gradient, clipRect, mask, CanvasRenderTarget, NoiseTexture2d, lookAt, setBasis, toImageData, getImageData, toBlob, toDataURL, screenshot, read pixels."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -143,6 +143,12 @@ app.viewport.worldToLocal(x, y); // world → screen
|
|
|
143
143
|
app.viewport.localToWorld(x, y); // screen → world
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
+
They agree with where the camera draws: a level smaller than the view is
|
|
147
|
+
centred by offsetting the world container (`world.pos`), which the default
|
|
148
|
+
camera draws and a minimap, corner or zoomed camera leaves out, and each
|
|
149
|
+
camera's conversions follow suit (20.13 and later; before, a minimap's were off
|
|
150
|
+
by `world.pos`).
|
|
151
|
+
|
|
146
152
|
Both return a `Vector2d`; with no third argument that vector comes from the
|
|
147
153
|
shared pool, so in a per-frame path pass your own `v` as the third argument, or
|
|
148
154
|
release it with `getPool("vector2d").release(v)`.
|
|
@@ -205,6 +211,14 @@ and an overridden `postDraw`:
|
|
|
205
211
|
this.cameras.set("minimap", new MinimapCamera());
|
|
206
212
|
```
|
|
207
213
|
|
|
214
|
+
A stage's cameras draw in the order they were added, each over the ones before
|
|
215
|
+
it, so a full-screen second camera is a layer: a `Camera3d` drawing a 3D
|
|
216
|
+
background first, the game's own camera over it, each kept to its part of the
|
|
217
|
+
world with `visibilityLayer` / `cullMask` (below). A camera's post effects
|
|
218
|
+
apply to what that camera draws: the first camera's pass starts the frame, and
|
|
219
|
+
a later camera's is blended over what is already there (20.13 and later;
|
|
220
|
+
before, it wiped out the cameras before it).
|
|
221
|
+
|
|
208
222
|
View state is per camera, not per scene: a `Camera3d` carries its own distance
|
|
209
223
|
fog (`setFog`), so a minimap or split-screen view fogs independently of the main
|
|
210
224
|
one — and a `Camera2d` never fogs. See `melonjs-3d`.
|
|
@@ -228,6 +242,9 @@ minimap.cullMask = ~PARTICLES; // the minimap draws every layer but 2
|
|
|
228
242
|
set it on an emitter and none of its particles are drawn. The world itself
|
|
229
243
|
obeys it too, and so does what it contains: a child is drawn only if it and
|
|
230
244
|
every container above it share a bit with the camera.
|
|
245
|
+
- Particles are on their emitter's layers, and follow a change to them on the
|
|
246
|
+
emitter's next update, so a camera that draws only the emitter's layer draws
|
|
247
|
+
its particles too.
|
|
231
248
|
- A renderable on layer `0` is drawn by no camera; a `cullMask` of `0` draws
|
|
232
249
|
nothing.
|
|
233
250
|
- It decides drawing only: what a camera leaves out still updates, collides and
|
|
@@ -256,6 +273,23 @@ draw(renderer) {
|
|
|
256
273
|
}
|
|
257
274
|
```
|
|
258
275
|
|
|
276
|
+
**The colour's alpha and the global alpha are two things**, as on a 2D
|
|
277
|
+
context. A fill or a stroke is drawn at the alpha of `setColor` times
|
|
278
|
+
`setGlobalAlpha` (which is where the renderable's opacity comes in), an image at
|
|
279
|
+
the global alpha (times the tint) alone, and `getGlobalAlpha()` never moves with
|
|
280
|
+
`setColor`. So to fade a translucent shape, lower the global alpha and keep the
|
|
281
|
+
colour; folding the colour's alpha into `setGlobalAlpha` as well counts it twice.
|
|
282
|
+
|
|
283
|
+
`lineWidth`, `lineJoin`, the colour and the global alpha are all part of what
|
|
284
|
+
`save()` / `restore()` keep, and every renderable's `draw()` runs between the
|
|
285
|
+
two, so set them freely there: the next renderable gets them back.
|
|
286
|
+
|
|
287
|
+
`fillArc` / `strokeArc` read their angles like `CanvasRenderingContext2D.arc`:
|
|
288
|
+
an end before the start wraps round (`end + 2π`), and a span of 2π or more in
|
|
289
|
+
the arc's own direction is the whole circle. A filled part-circle differs by
|
|
290
|
+
backend, though: Canvas closes it with a straight chord, the GPU renderers fill
|
|
291
|
+
a pie slice from the centre.
|
|
292
|
+
|
|
259
293
|
Geometry available: `Rect`, `RoundRect`, `Ellipse`, `Polygon`, `Line`, `Point`
|
|
260
294
|
(and a `Bounds`). `renderer.fill(shape)` and `renderer.stroke(shape)` take any
|
|
261
295
|
of them and throw `Invalid geometry for fill/stroke` for anything else.
|
|
@@ -383,6 +417,7 @@ const data = await rt.toImageData(0, 0, w, h);
|
|
|
383
417
|
|
|
384
418
|
| symptom | cause |
|
|
385
419
|
|---|---|
|
|
420
|
+
| a second camera's post effect wipes out what the first camera drew | before 20.13 a later camera's post-effect pass replaced the whole frame; update, or put the effects on a container instead of the camera |
|
|
386
421
|
| `follow()` set but the view never moves | viewport bounds still at renderer size |
|
|
387
422
|
| `invalid target for me.Camera2d.follow` | passed `player.pos` — pass the renderable, a `Vector2d` or a `Vector3d` |
|
|
388
423
|
| camera drag jumps around | using world (`gameWorldX`) instead of screen (`gameScreenX`) coordinates |
|
|
@@ -390,6 +425,11 @@ const data = await rt.toImageData(0, 0, w, h);
|
|
|
390
425
|
| custom drawing appears at the origin | `draw()` not reading `this.pos` |
|
|
391
426
|
| custom drawing offset by half its size | centred `anchorPoint` not zeroed |
|
|
392
427
|
| `setLineWidth` is not a function | it is the `lineWidth` property |
|
|
428
|
+
| after a translucent `setColor`, images, later colours and the next renderables come out faded (WebGL / WebGPU) | before 20.13 the colour's alpha leaked into the global alpha; update |
|
|
429
|
+
| a translucent colour comes out fainter than asked | its alpha folded into `setGlobalAlpha` as well, so it counts twice |
|
|
430
|
+
| a `lineWidth` set in one `draw()` thickens everything drawn after it (WebGL / WebGPU) | before 20.13 `restore()` did not put `lineWidth` back; update, or put it back yourself |
|
|
431
|
+
| `fillArc` with the end angle before the start fills the wrong part of the circle (WebGL / WebGPU) | before 20.13 the GPU renderers did not wrap the angles like Canvas; update |
|
|
432
|
+
| a minimap's `worldToLocal` / `localToWorld` are off by the level's centring offset | before 20.13 they applied `world.pos`, which a non-default camera does not draw; update |
|
|
393
433
|
| clipping in the wrong place inside a container | container clips in local coordinates, after its own translate |
|
|
394
434
|
| `container.clipping = true` does nothing | container has no explicit size, so `width`/`height` are `Infinity` |
|
|
395
435
|
| baked target draws nothing / throws | passed the `CanvasRenderTarget`, not its `.canvas` |
|
|
@@ -195,11 +195,25 @@ A repeating pattern is left to wrap.
|
|
|
195
195
|
|
|
196
196
|
## `subPixel` is about position, not scale
|
|
197
197
|
|
|
198
|
-
`subPixel: false` (the default)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
`subPixel: false` (the default) draws everything on whole pixels. The snap
|
|
199
|
+
happens **once, where a draw lands**, the same on every renderer: each edge of
|
|
200
|
+
a sprite, a shape or a clip is rounded to the nearest pixel after every
|
|
201
|
+
translation and scale it is drawn under. So:
|
|
202
|
+
|
|
203
|
+
- nested containers add their positions up exactly first (10.6 then -6.5
|
|
204
|
+
lands on 4, the pixel 4.1 is in);
|
|
205
|
+
- a flipped sprite covers the pixels it covers unflipped;
|
|
206
|
+
- a sprite and a `fillRect` at the same position cover the same pixels, and a
|
|
207
|
+
shape has no half-grey edge on the Canvas renderer;
|
|
208
|
+
- a position floating point error leaves a hair short of a pixel, such as a
|
|
209
|
+
body resting on the floor at `223.99999999999997` after a collision
|
|
210
|
+
push-out, draws on that pixel (224), so it does not shake every other frame.
|
|
211
|
+
|
|
212
|
+
The camera's view starts on a whole pixel too, in world units (zoomed 2x, it
|
|
213
|
+
moves in 2 px steps), and culling and the pointer use that same position:
|
|
214
|
+
things standing still stay still against each other while the view scrolls,
|
|
215
|
+
and a click lands on what is drawn under it. A rotated draw, and anything a
|
|
216
|
+
`Camera3d` draws, is left where it is.
|
|
203
217
|
|
|
204
218
|
It does nothing about how the frame reaches the screen. A game that carefully
|
|
205
219
|
snaps positions and is then scaled by 2.667 has thrown that work away at the
|
|
@@ -138,18 +138,44 @@ as transparent, as if the sprite were drawn on its own, so they never show
|
|
|
138
138
|
along its edges and the atlas needs no padding for them. Past the texture's own
|
|
139
139
|
edge (a standalone image, or the outside of an atlas) nothing changes.
|
|
140
140
|
|
|
141
|
+
**The effects that place a pattern place it on the renderable they draw.**
|
|
142
|
+
`SwayEffect`'s planted row, `WaveEffect`'s `anchor`, the centre of
|
|
143
|
+
`VignetteEffect`, `RadialGradientEffect` and `ScanlineEffect`'s curvature all
|
|
144
|
+
land on the sprite itself: cut from an atlas, flipped, rotated, or with other
|
|
145
|
+
effects after it, the pattern sits where it sits on a sprite with a texture of
|
|
146
|
+
its own and that one effect. What is measured in the texture stays measured in
|
|
147
|
+
it: `ShineEffect`'s sweep, bands and width, `WaveEffect`'s amplitude and
|
|
148
|
+
frequency, `DissolveEffect`'s grain, the lines of `HologramEffect` and
|
|
149
|
+
`ScanlineEffect` (on an atlas sprite, fractions of the atlas, so a single shine
|
|
150
|
+
crosses an atlas sprite only part of the time). With other effects after them
|
|
151
|
+
they all stay on the sprite. `SwayEffect` measures everything across the
|
|
152
|
+
sprite.
|
|
153
|
+
|
|
141
154
|
**Their widths, offsets and sizes are in pixels of the texture they draw, with
|
|
142
155
|
nothing to pass.** Each of those effects takes the size of what it draws: the
|
|
143
156
|
sprite's whole texture (the atlas image, not the frame), the screen for a
|
|
144
|
-
camera's effect, a mesh's texture.
|
|
157
|
+
camera's effect, a mesh's texture. With other effects after it, it still
|
|
158
|
+
measures in the sprite's own pixels, at any scale and under a camera's zoom
|
|
159
|
+
(on a container, in the container's own), so adding a second effect never
|
|
160
|
+
changes how wide the first comes out. Do not pass `textureSize` to get this: a
|
|
145
161
|
`textureSize` (or `setTextureSize()`) fixes the size instead, for a width
|
|
146
162
|
meant in some other texture's pixels.
|
|
147
163
|
|
|
148
164
|
```js
|
|
149
|
-
// 2 pixels of the sprite's art, whatever the size of its atlas
|
|
165
|
+
// 2 pixels of the sprite's art, whatever the size of its atlas, its scale,
|
|
166
|
+
// the camera's zoom, or the effects after it
|
|
150
167
|
sprite.addPostEffect(new OutlineEffect(app.renderer, { width: 2 }));
|
|
168
|
+
sprite.addPostEffect(new GlowEffect(app.renderer, { width: 6 }));
|
|
151
169
|
```
|
|
152
170
|
|
|
171
|
+
With two or more effects, the sprite is drawn into a capture and each effect
|
|
172
|
+
reads that, at the game's texture filter, as a sprite reads its own texture:
|
|
173
|
+
under `"nearest"` (or `antiAlias` off) a width that lands between two screen
|
|
174
|
+
pixels, such as 2 pixels of a sprite drawn at 0.3, stays crisp, and under
|
|
175
|
+
`"linear"` it blends, on WebGL and WebGPU alike. The result lands inside the
|
|
176
|
+
clip the sprite is drawn under, a minimap camera's view or a clipping
|
|
177
|
+
container, as with one effect.
|
|
178
|
+
|
|
153
179
|
`SwayEffect` leans a sprite or a layer from side to side, as if in a breeze: the
|
|
154
180
|
top sways most and everything from its `anchor` row down stays put, so a tree's
|
|
155
181
|
trunk or a reed's root stays planted. It suits anything planted at its bottom;
|
|
@@ -166,17 +192,18 @@ tree.addPostEffect(sway);
|
|
|
166
192
|
sway.setTime(timer.getTime() / 1000);
|
|
167
193
|
```
|
|
168
194
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
195
|
+
Its strength (a fraction of the sprite's width), its `anchor` and how it bends
|
|
196
|
+
are all measured across the sprite, so a sprite cut from an atlas sways exactly
|
|
197
|
+
like the same art with a texture of its own. `WaveEffect`'s `anchor` is a row of
|
|
198
|
+
the sprite too, but its amplitude and frequency are measured across the texture,
|
|
199
|
+
so on an atlas sprite they are fractions of the atlas.
|
|
172
200
|
|
|
173
|
-
`RadialGradientEffect` is a procedural soft spot: solid at the centre of
|
|
174
|
-
|
|
175
|
-
on a non-square
|
|
176
|
-
a canvas, for a pickup highlight, a trigger marker or a damage flash
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
a sub-region of an atlas, where the centre would land off-quad.
|
|
201
|
+
`RadialGradientEffect` is a procedural soft spot: solid at the centre of the
|
|
202
|
+
renderable it draws, fading linearly to transparent at the edge, and elliptical
|
|
203
|
+
for free on a non-square one. It is the one to reach for instead of baking a
|
|
204
|
+
disc into a canvas, for a pickup highlight, a trigger marker or a damage flash,
|
|
205
|
+
on a standalone image, `Renderer.getWhitePixel()` or a sprite cut from an atlas
|
|
206
|
+
alike.
|
|
180
207
|
|
|
181
208
|
`SquishEffect` bulges a sprite's middle out (`setBulge(0.3)`) or pinches it in
|
|
182
209
|
(`setBulge(-0.3)`), its top and bottom rows staying put. Bulging out widens the
|
|
@@ -524,6 +551,10 @@ has changes nothing). What splits the batch:
|
|
|
524
551
|
- a different effect instance, or a sprite without the effect in between;
|
|
525
552
|
- a `noise_uv` effect on sprites of different frames or sizes (its rect is
|
|
526
553
|
per sprite);
|
|
554
|
+
- one of the effects that place a pattern on the sprite (`SwayEffect`,
|
|
555
|
+
`WaveEffect`, `ShineEffect`, `VignetteEffect`, `RadialGradientEffect`,
|
|
556
|
+
`ScanlineEffect`, `DissolveEffect`, `HologramEffect`): each sprite is told
|
|
557
|
+
where it is, so each draws on its own;
|
|
527
558
|
- a `screen_texture` effect, which draws each sprite on its own, as each one
|
|
528
559
|
samples a capture of everything drawn before it.
|
|
529
560
|
|
|
@@ -603,7 +634,9 @@ For refraction, distortion and anything that samples what is already drawn:
|
|
|
603
634
|
- **`screen_texture`** — a sampler kept filled with everything drawn so far
|
|
604
635
|
(GLSL: annotate the sampler `uniform sampler2D tex : screen_texture;`)
|
|
605
636
|
- **`screen_uv`** — this fragment's 0..1 position in that capture
|
|
606
|
-
- **`noise_uv`** — 0..1 across the sprite regardless of its atlas frame
|
|
637
|
+
- **`noise_uv`** — 0..1 across the sprite regardless of its atlas frame, and
|
|
638
|
+
across the renderable when it has several effects (it does not slide with
|
|
639
|
+
the camera)
|
|
607
640
|
|
|
608
641
|
In WGSL the capture is sampled through `screen_sampler` (clamped) or
|
|
609
642
|
`screen_sampler_repeat` (wrapping) instead of GLSL's `: screen_texture`
|
|
@@ -698,11 +731,13 @@ that against your request detects the fallback.
|
|
|
698
731
|
|
|
699
732
|
## The Canvas fallback
|
|
700
733
|
|
|
701
|
-
`ShaderEffect` on a Canvas renderer
|
|
702
|
-
|
|
703
|
-
|
|
734
|
+
`ShaderEffect` on a Canvas renderer leaves `enabled = false` and turns every
|
|
735
|
+
method into a no-op. Nothing throws, and the console gets one warning, from the
|
|
736
|
+
first such effect; the rest stay quiet. The same happens on a GPU backend when
|
|
737
|
+
no body matches its language — a GLSL-only effect under WebGPU.
|
|
704
738
|
|
|
705
|
-
That is fine for decoration
|
|
739
|
+
That is fine for decoration, which needs no guard: create and add the effect as
|
|
740
|
+
on any renderer. It is **not** fine when the shader draws your
|
|
706
741
|
content — the carrier renderable then shows through raw. A 1×1 white texture
|
|
707
742
|
stretched to 280×156 renders as a white box where the effect should be.
|
|
708
743
|
|
|
@@ -723,6 +758,7 @@ same question after construction.
|
|
|
723
758
|
|
|
724
759
|
| symptom | cause |
|
|
725
760
|
|---|---|
|
|
761
|
+
| a camera's post effect hides another camera drawn before it | before 20.13 a later camera's pass replaced the whole frame; update. Since then each camera's effects apply to what it draws, over the cameras before it |
|
|
726
762
|
| effect does nothing, warning in console | Canvas fallback — no programmable pipeline |
|
|
727
763
|
| effect does nothing on some machines only | GLSL-only shader, `video.AUTO` chose WebGPU |
|
|
728
764
|
| a white or solid box where the effect should be | carrier renderable drawn while the effect is disabled |
|
|
@@ -87,7 +87,7 @@ right times and there is nothing to unsubscribe:
|
|
|
87
87
|
| callback | when |
|
|
88
88
|
|---|---|
|
|
89
89
|
| `onActivateEvent()` | `addChild`-ed into a container that is already attached to the world root (a `Container` forwards it to its children) |
|
|
90
|
-
| `onDeactivateEvent()` | removed from its container — including the automatic world clear on a state change |
|
|
90
|
+
| `onDeactivateEvent()` | removed from its container — including the automatic world clear on a state change. Once per `onActivateEvent`: the children of a removed container, at any depth, are told once |
|
|
91
91
|
| `onDestroyEvent()` | `destroy()` ran; called with no arguments from the container-removal path |
|
|
92
92
|
| `onCollisionStart` / `onCollisionActive` / `onCollisionEnd` | contacts |
|
|
93
93
|
|
|
@@ -154,6 +154,8 @@ Use `removeChild()`, or flag the work and drain it from `Stage.update`.
|
|
|
154
154
|
| crash after a collision | `removeChildNow()` inside a contact callback — use `removeChild()` |
|
|
155
155
|
| progress callbacks never fire | assigned `loader.onProgress` (removed in 20.3) instead of subscribing to `LOADER_PROGRESS` |
|
|
156
156
|
| `onActivateEvent` never runs | the child was added to a container not yet attached to the world |
|
|
157
|
+
| `onDeactivateEvent` runs twice when a group is removed (a count goes negative, a light or listener is released twice) | the group's `onDeactivateEvent` and its `destroy()` both reached the children, before 20.13.0 — now once per `onActivateEvent` |
|
|
158
|
+
| `onDeactivateEvent` never runs on a child of a group built off-world and destroyed | it never had `onActivateEvent` either: the pair is only sent to children that entered the world |
|
|
157
159
|
|
|
158
160
|
## Related skills
|
|
159
161
|
|
|
@@ -137,7 +137,6 @@ with the formats you ship before any audio asset is preloaded.
|
|
|
137
137
|
```js
|
|
138
138
|
import { audio, loader } from "melonjs";
|
|
139
139
|
|
|
140
|
-
loader.setOptions({ crossOrigin: "anonymous" }); // needed for remote assets
|
|
141
140
|
audio.init("mp3,ogg"); // BEFORE preloading audio
|
|
142
141
|
await loader.preload([
|
|
143
142
|
{ name: "tileset", type: "image", src: "data/img/tileset.png" },
|
|
@@ -173,7 +172,7 @@ placement.
|
|
|
173
172
|
| scene content appears once and never resets | scene built in the constructor instead of `onResetEvent` |
|
|
174
173
|
| everything offset by half a sprite | top-left math against the centred `anchorPoint` default |
|
|
175
174
|
| a `Tween` or `ParticleEmitter` does nothing | `.start()` / `streamParticles()` never called |
|
|
176
|
-
|
|
|
175
|
+
| an asset on another origin fails to load, the error naming it | that server sends no `Access-Control-Allow-Origin` header (see melonjs-loading-assets) |
|
|
177
176
|
|
|
178
177
|
## Related skills
|
|
179
178
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-input
|
|
3
|
-
description: "Use this skill for keyboard, pointer, mouse, touch and gamepad input in melonJS. Covers the bindKey action indirection, registerPointerEvent on regions, the isKinematic requirement that silently blocks pointer events, which of several overlapping objects receives an event and how to consume it, world vs screen pointer coordinates, and gamepad mapping onto the same actions. Triggers on: input, bindKey, isKeyPressed, KEY, registerPointerEvent, releasePointerEvent, pointerdown, pointerup, pointermove, POINTERMOVE, gamepad, bindGamepad, triggerKeyEvent, KEYDOWN, KEYUP, press any key, splash screen, click, touch, drag, hit test, event propagation, stop propagating, overlapping, which object gets the click."
|
|
3
|
+
description: "Use this skill for keyboard, pointer, mouse, touch and gamepad input in melonJS. Covers the bindKey action indirection, registerPointerEvent on regions, the isKinematic requirement that silently blocks pointer events, which of several overlapping objects receives an event and how to consume it, world vs screen pointer coordinates, and gamepad mapping onto the same actions. Triggers on: input, bindKey, isKeyPressed, KEY, registerPointerEvent, releasePointerEvent, pointerdown, pointerup, pointermove, POINTERMOVE, gamepad, bindGamepad, triggerKeyEvent, virtual gamepad, touch controls, joystick, gamepad-plugin, floating stick, floating button, floating joystick, two thumbs, tap to fire, hideAtRest, mobile controls, KEYDOWN, KEYUP, press any key, splash screen, click, touch, drag, hit test, event propagation, stop propagating, overlapping, which object gets the click."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -273,7 +273,60 @@ device, so a pad unplugged and plugged back into the same slot keeps working.
|
|
|
273
273
|
|
|
274
274
|
## Virtual controls
|
|
275
275
|
|
|
276
|
-
|
|
276
|
+
For on-screen touch controls, use `@melonjs/gamepad-plugin` rather than
|
|
277
|
+
writing them: sticks and buttons drawn over the game and reported as a pad in
|
|
278
|
+
a gamepad slot, so the `bindGamepad` bindings above answer to them unchanged.
|
|
279
|
+
|
|
280
|
+
```js
|
|
281
|
+
import { GamepadPlugin } from "@melonjs/gamepad-plugin";
|
|
282
|
+
|
|
283
|
+
// once the application is created: a stick and two buttons, on touch screens
|
|
284
|
+
plugin.register(GamepadPlugin, "gamepad", {
|
|
285
|
+
buttons: [{ label: "A" }, { label: "B" }],
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The left stick presses the D-pad buttons (`GAMEPAD.BUTTONS.UP` ...) in 8
|
|
290
|
+
directions and reports `AXES.LX`/`LY`; the buttons press `FACE_1`, `FACE_2`...
|
|
291
|
+
in turn, or a key with `{ key: input.KEY.SPACE }`. Each finger is followed on
|
|
292
|
+
its own, a touch starting on the controls does not reach the game, and they
|
|
293
|
+
hide while a real pad is in use. `plugin.cache.gamepad.visible` says whether
|
|
294
|
+
they show, to word a prompt ("tap" or a key name).
|
|
295
|
+
|
|
296
|
+
Sticks and buttons are fixed in a corner by default. **`floating: true`**
|
|
297
|
+
puts one under the finger wherever it lands inside its `area` (`"half"`, its
|
|
298
|
+
own half of the screen, by default; `"screen"`; or `{ x, y, width, height }`),
|
|
299
|
+
and **`hideAtRest: true`** draws it only while held. The usual mobile
|
|
300
|
+
two-thumb layout, nothing on screen until touched (1.1):
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
plugin.register(GamepadPlugin, "gamepad", {
|
|
304
|
+
sticks: [{ floating: true, hideAtRest: true }], // left half
|
|
305
|
+
buttons: [{ floating: true, hideAtRest: true, label: "A" }], // right half
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Or one thumb for both: a stick over the whole screen whose quick tap (lifted
|
|
310
|
+
within `tap.time`, 250 ms, without leaning to `dpadThreshold`) presses a key
|
|
311
|
+
or button for one frame, and no button at all:
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
plugin.register(GamepadPlugin, "gamepad", {
|
|
315
|
+
sticks: [{ floating: true, area: "screen", hideAtRest: true,
|
|
316
|
+
tap: { key: input.KEY.SPACE } }],
|
|
317
|
+
buttons: 0,
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Where areas overlap, a finger takes the first free control there: fixed
|
|
322
|
+
buttons, fixed sticks, floating buttons, floating sticks. So a floating button
|
|
323
|
+
on its `"half"` carves the right half out of a `"screen"` stick, and several
|
|
324
|
+
floating buttons sharing an area split it by distance to where each rests.
|
|
325
|
+
Every press lasts at least one frame, so a 15 ms thumb tap still reaches
|
|
326
|
+
`isKeyPressed`, and Safari's pinch gesture is cancelled while the controls
|
|
327
|
+
show, so a second thumb works on iOS.
|
|
328
|
+
|
|
329
|
+
A single key can also be pressed directly, reaching the same actions:
|
|
277
330
|
|
|
278
331
|
```js
|
|
279
332
|
input.triggerKeyEvent(input.KEY.LEFT, true); // press
|
|
@@ -293,12 +346,17 @@ input.triggerKeyEvent(input.KEY.LEFT, false); // release
|
|
|
293
346
|
| jump fires every frame while held | `bindKey` without its third `lock` argument |
|
|
294
347
|
| `invalid event type` thrown at registration | non-pointer name (`"click"`, `"mousedown"`, …) passed to `registerPointerEvent` |
|
|
295
348
|
| `no action defined for keycode N` | `bindGamepad` called before `bindKey` for that keycode |
|
|
349
|
+
| floating stick ignores the right half of the screen | its `area` defaults to `"half"`, its own side — use `area: "screen"` |
|
|
350
|
+
| `area: "screen"` stick still ignores part of the screen | a floating button's area comes first there — the overlap order is fixed buttons, fixed sticks, floating buttons, floating sticks |
|
|
351
|
+
| a quick tap on a stick moves instead of acting | it leaned past `dpadThreshold` or outlasted `tap.time` before lifting |
|
|
296
352
|
| stick has to be pushed well past its `threshold` before it registers | `threshold` is ADDED to the deadzone, not used instead of it |
|
|
297
353
|
| one half of a stick does nothing | each half is a separate `bindGamepad`, chosen by the sign of `threshold` |
|
|
298
|
-
| a widget under an overlapping panel still
|
|
354
|
+
| a widget under an overlapping panel still lights up | the panel consumes no hover: return `false` from its `onOver` (presses already stop at UI elements; `UIPanel` with `blocking` does both) |
|
|
299
355
|
| a widget stays highlighted after the pointer moves onto a panel over it | the panel consumes no `pointermove`, so nothing takes the pointer off the widget |
|
|
300
356
|
| a child's big `z` does not put it in front of another container | `pos.z` is container-local — use `ancestor.moveToTop()` on the container |
|
|
301
357
|
| every click in the game stops working | a handler on `app.viewport` returning `false`, which is asked first |
|
|
358
|
+
| touch controls do nothing | nothing bound to their slot — `bindGamepad` on the plugin's `index` (0 by default), or give the buttons a `key` |
|
|
359
|
+
| touch controls never show on a desktop | the gamepad plugin's `enabled` defaults to `device.touch` — pass `enabled: true` to try them there |
|
|
302
360
|
|
|
303
361
|
## Related skills
|
|
304
362
|
|
|
@@ -174,9 +174,10 @@ the soft one is one shader pass on the GPU backends and an offscreen canvas on
|
|
|
174
174
|
Canvas. So the lit-spot look, soft or hard, works everywhere, Canvas included.
|
|
175
175
|
|
|
176
176
|
What Canvas cannot do is **per-pixel normal-map lighting**:
|
|
177
|
-
`renderer.setLightUniforms()` is a no-op there, and the first
|
|
178
|
-
|
|
179
|
-
unlit — no
|
|
177
|
+
`renderer.setLightUniforms()` is a no-op there, and the first normal-mapped
|
|
178
|
+
sprite drawn while lights are on logs a one-shot warning (a scene with lights
|
|
179
|
+
and no normal maps stays quiet). Normal-mapped sprites simply render unlit — no
|
|
180
|
+
error.
|
|
180
181
|
|
|
181
182
|
```js
|
|
182
183
|
if (app.renderer.shaderLanguage === null) {
|
|
@@ -13,8 +13,6 @@ Every asset is `{ name, type, src }` plus type-specific extras:
|
|
|
13
13
|
```js
|
|
14
14
|
import { loader } from "melonjs";
|
|
15
15
|
|
|
16
|
-
loader.setOptions({ crossOrigin: "anonymous" }); // needed for remote assets
|
|
17
|
-
|
|
18
16
|
await loader.preload([
|
|
19
17
|
{ name: "tiles", type: "image", src: "data/img/tiles.png" },
|
|
20
18
|
{ name: "map1", type: "tmx", src: "data/map/map1.tmx" },
|
|
@@ -183,7 +181,8 @@ the hard way:
|
|
|
183
181
|
### `DefaultLoadingScreen` lives in the WORLD, not on the screen
|
|
184
182
|
|
|
185
183
|
The built-in loading screen is written for a `Camera2d`. Its progress bar and
|
|
186
|
-
logo are added
|
|
184
|
+
logo are added together, once the logo is decoded (a frame or so after the
|
|
185
|
+
screen comes up), with `app.world.addChild(bar, 1)` / `addChild(logo, 2)`, and are
|
|
187
186
|
**`floating === false`** — ordinary world-space renderables. They read as an
|
|
188
187
|
overlay only because that stage's world is otherwise empty.
|
|
189
188
|
|
|
@@ -195,7 +194,7 @@ in front of a bar sitting at z = 1. What does NOT rescue it, all tried:
|
|
|
195
194
|
depth, so the scene's own z values are geometry, not layering, and the bar
|
|
196
195
|
still loses to the depth test.
|
|
197
196
|
- **`moveToTop()`** — it early-returns for a child already at index 0, which the
|
|
198
|
-
loading screen's children
|
|
197
|
+
loading screen's children usually are, having been added before anything else.
|
|
199
198
|
It silently does nothing.
|
|
200
199
|
- **Flipping them to `floating = true`** for the duration — the scene still
|
|
201
200
|
covers them.
|
|
@@ -348,6 +347,20 @@ event.on(event.LOADER_COMPLETE, () => { /* … */ });
|
|
|
348
347
|
event.on(event.LOADER_ERROR, (res) => { /* … */ });
|
|
349
348
|
```
|
|
350
349
|
|
|
350
|
+
**Each `preload()` has only its own callback.** One given none calls nothing
|
|
351
|
+
on completion, so a later `await loader.preload(levelAssets)` does not re-run
|
|
352
|
+
the boot callback. Before 20.13 the loader kept the last callback it was given
|
|
353
|
+
and called it again for a preload that had none — usually a `state.change`
|
|
354
|
+
straight back to the first screen. `loader.reload(src)` after a failure
|
|
355
|
+
completes the preload that asset failed in, with that preload's callback, once
|
|
356
|
+
all of its assets are in.
|
|
357
|
+
|
|
358
|
+
**Progress is per preload:** `LOADER_PROGRESS` goes from that call's first
|
|
359
|
+
asset to `1` with its last, every time. A preload started while another is
|
|
360
|
+
still loading joins it, and the value covers both until everything in flight
|
|
361
|
+
has settled; the next preload starts from zero again. A failed asset keeps
|
|
362
|
+
its preload short of `1`, and only that preload.
|
|
363
|
+
|
|
351
364
|
## Unloading
|
|
352
365
|
|
|
353
366
|
```js
|
|
@@ -376,20 +389,45 @@ state.change(state.PLAY, true);
|
|
|
376
389
|
|
|
377
390
|
## Cross-origin and authenticated assets
|
|
378
391
|
|
|
379
|
-
|
|
380
|
-
|
|
392
|
+
**Nothing to set for the usual cases.** Every fetched asset (image, json,
|
|
393
|
+
binary, tmx, tsx, shader, gltf/glb, obj, mtl, aseprite, buffered audio) is
|
|
394
|
+
requested the same way:
|
|
395
|
+
|
|
396
|
+
- **On the game's own origin**, it carries the browser's cookies and HTTP
|
|
397
|
+
auth, as the page and its scripts do. A game behind a login or a session
|
|
398
|
+
cookie loads its own assets with no option.
|
|
399
|
+
- **On another origin** (a CDN), it is a CORS request: it loads when that
|
|
400
|
+
server sends `Access-Control-Allow-Origin`, and fails at load when not, with
|
|
401
|
+
an error naming the asset and the header it needs. No `crossOrigin` needed.
|
|
402
|
+
|
|
403
|
+
| asset on… | default | `withCredentials: true` |
|
|
404
|
+
|---|---|---|
|
|
405
|
+
| the game's own origin | loads, with its cookies | loads, with its cookies |
|
|
406
|
+
| another origin sending `Access-Control-Allow-Origin` | loads, no cookies | **blocked** if it answers `*`; needs the page's exact origin plus `Access-Control-Allow-Credentials: true` |
|
|
407
|
+
| another origin allowing credentials for the page | loads, no cookies | loads, with that origin's cookies |
|
|
408
|
+
| another origin with no CORS headers | fails at load, naming the asset | fails at load, naming the asset |
|
|
409
|
+
|
|
410
|
+
Two global settings cover the rest, for every asset type — there is no
|
|
411
|
+
per-asset or per-type spelling to remember:
|
|
381
412
|
|
|
382
413
|
```js
|
|
383
414
|
loader.setOptions({
|
|
384
|
-
|
|
385
|
-
|
|
415
|
+
withCredentials: true, // send cookies / auth to OTHER origins too
|
|
416
|
+
crossOrigin: "anonymous", // attribute of the <video>, <script> and glTF
|
|
417
|
+
// image elements the loader creates
|
|
386
418
|
});
|
|
387
419
|
```
|
|
388
420
|
|
|
389
|
-
`withCredentials` is
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
421
|
+
`withCredentials` is for assets on **another** origin behind a session cookie
|
|
422
|
+
or a login; that server must then allow credentials for the page's origin, or
|
|
423
|
+
the browser blocks the request. `crossOrigin: "use-credentials"` does the same
|
|
424
|
+
for fetched assets, and sets the attribute on the elements.
|
|
425
|
+
|
|
426
|
+
Before 20.13 both were needed far more often: assets on the game's own origin
|
|
427
|
+
went out with no cookies unless `withCredentials` was set (a 401 or 403 behind
|
|
428
|
+
a login), and an asset on another origin without `crossOrigin: "anonymous"`
|
|
429
|
+
was read as an empty response, failing later with "Unexpected end of input" or
|
|
430
|
+
"The source image could not be decoded" even when its server allowed it.
|
|
393
431
|
|
|
394
432
|
**Set them through `setOptions`.** `loader.crossOrigin` and
|
|
395
433
|
`loader.withCredentials` are read-only module bindings; assigning to them
|
|
@@ -397,7 +435,7 @@ throws, the same trap as the removed `loader.onload` / `onProgress`.
|
|
|
397
435
|
|
|
398
436
|
| | |
|
|
399
437
|
| --- | --- |
|
|
400
|
-
| one streamed clip (`stream: true` / `html5: true`) ignores `withCredentials` | it plays through an `<audio>` element, which needs a `crossorigin` attribute rather than fetch credentials. Preload it buffered if it is behind auth |
|
|
438
|
+
| one streamed clip (`stream: true` / `html5: true` / `preload: false`) ignores `withCredentials` | it plays through an `<audio>` element, which needs a `crossorigin` attribute rather than fetch credentials. Preload it buffered if it is behind auth |
|
|
401
439
|
| credentials silently dropped for audio before 20.4 | the loader forwarded them under a pre-20.3 name the backend never read — a hung preload rather than an error |
|
|
402
440
|
|
|
403
441
|
## Symptom → cause
|
|
@@ -407,13 +445,17 @@ throws, the same trap as the removed `loader.onload` / `onProgress`.
|
|
|
407
445
|
| audio 404s | `src` given as a file instead of a directory |
|
|
408
446
|
| audio fails, `preload()` rejects with "Failed loading resource" | `audio.init()` not called before preloading — the real message is replaced by the loader's |
|
|
409
447
|
| blank level, missing tiles | `.tsx` or tileset image not listed in the resources |
|
|
410
|
-
|
|
|
411
|
-
|
|
|
448
|
+
| `Failed to load "…" (Failed to fetch). An asset on another origin loads only if its server allows it…` | that origin sends no `Access-Control-Allow-Origin` header — or, with `withCredentials`, sends `*` or no `Access-Control-Allow-Credentials: true`. Fix the server's headers, or serve the asset from the game's own origin |
|
|
449
|
+
| an asset on another origin behind a login 401s / audio preload hangs | missing `loader.setOptions({ withCredentials: true })` — one switch, all asset types. Assets on the game's own origin need nothing (before 20.13 they needed it too) |
|
|
450
|
+
| a remote JSON fails with "Unexpected end of input", or an image with "The source image could not be decoded" | melonJS before 20.13, which read assets on another origin without CORS unless `crossOrigin: "anonymous"` was set |
|
|
412
451
|
| `TypeError` assigning `loader.withCredentials` / `crossOrigin` | read-only module bindings; use `loader.setOptions({ … })` |
|
|
413
452
|
| preload never finishes, blank screen, no error | a failing sound with `audio.setStopOnAudioError(false)` before 20.4 — it disabled audio *and* failed the preload |
|
|
414
453
|
| text renders in a fallback font | web font not preloaded as `"fontface"` |
|
|
415
454
|
| `BitmapText` renders nothing | the `.fnt` (as `"binary"`) or its image is missing |
|
|
416
455
|
| `TypeError` assigning `loader.onProgress` / `onload` | removed in 20.3; they were never writable — use the `LOADER_*` events |
|
|
456
|
+
| `await loader.preload(level)` sends the game back to its title screen, or runs the boot setup twice (before 20.13) | a preload with no callback re-ran the previous preload's callback; update, each preload now calls only its own |
|
|
457
|
+
| the loading bar starts part-full on a second preload, or never reaches the end after one asset failed (before 20.13) | the progress counters ran for the whole session; update, progress is per preload |
|
|
458
|
+
| `loader.reload(src)` of a custom asset type throws `unload : unknown or invalid resource type` (before 20.13) | update: a type registered with `setParser` can now be retried |
|
|
417
459
|
| `load : unknown or invalid resource type` | a custom type in the manifest before its `loader.setParser` call |
|
|
418
460
|
| black or frozen screen when a stage is first entered, the profile shows your own code (noise, a canvas bake) | a costly derived asset built on stage entry — bake it inside the preload as a [custom asset type](#custom-asset-types) |
|
|
419
461
|
| a custom asset preloads but the result is nowhere to be found | `onload` takes no value and the loader stores nothing for custom types — keep the result in your own module |
|
|
@@ -187,13 +187,22 @@ which is how a renderable knows its bounds went stale.
|
|
|
187
187
|
|
|
188
188
|
```js
|
|
189
189
|
v.setCallback(() => { ... });
|
|
190
|
-
v.setMuted(
|
|
190
|
+
v.setMuted(x, y, z); // write without firing it
|
|
191
|
+
v.setCallback(undefined); // stop observing
|
|
191
192
|
```
|
|
192
193
|
|
|
193
194
|
**`setMuted` is what you want when writing several components**, so a
|
|
194
195
|
half-updated vector does not fire a callback that reads the other half.
|
|
195
196
|
`set()` already does this internally: it disables the callback, writes all
|
|
196
|
-
three, fires once, and re-enables.
|
|
197
|
+
three, fires once, and re-enables. Every mutating method (`add`, `scale`,
|
|
198
|
+
`negateSelf`, `floorSelf`, `clampSelf`, `normalize`, `lerp`, …) goes through
|
|
199
|
+
`set()`, so it fires once and returns the observable vector itself. Before
|
|
200
|
+
20.13 `negateSelf`, `floorSelf`, `ceilSelf` and `clampSelf` (and the 3D
|
|
201
|
+
`normalize` and `perp`) fired once per component and returned the wrapped
|
|
202
|
+
plain vector instead.
|
|
203
|
+
|
|
204
|
+
`clone()` takes from a pool, and gets the callback you pass it or none: before
|
|
205
|
+
20.13 a recycled clone kept the callback of the vector released before it.
|
|
197
206
|
|
|
198
207
|
Note the observable `set(x = 0, y = 0, z = 0)` defaults EVERY argument, so
|
|
199
208
|
`set(10, 20)` zeroes z exactly as the plain vector does.
|
|
@@ -286,7 +295,7 @@ math.degToRad(deg); math.radToDeg(rad);
|
|
|
286
295
|
math.random(lo, hi); // integer
|
|
287
296
|
math.randomFloat(lo, hi);
|
|
288
297
|
math.weightedRandom(lo, hi); // integer, biased toward LO
|
|
289
|
-
math.round(n, decimals);
|
|
298
|
+
math.round(n, decimals); // nearest; a tie goes up, as Math.round: -2.5 → -2
|
|
290
299
|
math.pow(n); // n squared
|
|
291
300
|
math.DEG_TO_RAD math.RAD_TO_DEG math.TAU math.ETA math.EPSILON
|
|
292
301
|
```
|
|
@@ -317,6 +326,8 @@ and `Math.random()` every frame jitters instead of drifting.
|
|
|
317
326
|
| z unchanged after scaling | `scale(s)` leaves z alone; pass `scale(s, s, s)` |
|
|
318
327
|
| z became 0 for no reason | `set(x, y)` defaults z to 0 |
|
|
319
328
|
| the original vector changed | the method mutates; `copy()` into something first |
|
|
329
|
+
| a position callback fires twice and sees a half-updated vector, or `pos.floorSelf()` returns something that is not `pos` | before 20.13 the observable `negateSelf` / `floorSelf` / `ceilSelf` / `clampSelf` wrote component by component; upgrade |
|
|
330
|
+
| `math.round(-2.7)` is -2, or a large value comes back negative | before 20.13 `round` truncated toward zero through 32-bit integers; upgrade |
|
|
320
331
|
| memory climbs in a loop | `clone()` or `negate()`/`floor()`/`ceil()` per frame |
|
|
321
332
|
| two helper results are the same object | a shared scratch vector was returned |
|
|
322
333
|
| `.z` is a type error | `Renderable.pos` is declared `Vector2d` (#1510) |
|