melonjs 20.10.1 → 20.12.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 +4 -5
- package/build/application/application.d.ts.map +1 -1
- package/build/application/defaultApplicationSettings.d.ts +1 -0
- package/build/application/defaultApplicationSettings.d.ts.map +1 -1
- package/build/application/settings.d.ts +54 -3
- package/build/application/settings.d.ts.map +1 -1
- package/build/audio/audio.d.ts +14 -4
- package/build/audio/audio.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 +44 -4
- package/build/audio/types.d.ts.map +1 -1
- package/build/camera/camera2d.d.ts +48 -4
- package/build/camera/camera2d.d.ts.map +1 -1
- package/build/camera/camera3d.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/camera/effects/shake_effect.d.ts +3 -1
- package/build/camera/effects/shake_effect.d.ts.map +1 -1
- package/build/geometries/ellipse.d.ts +8 -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 +3 -3
- 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 +5 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +9944 -6234
- 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 +1 -1
- package/build/level/tiled/TMXLayer.d.ts.map +1 -1
- package/build/level/tiled/TMXObject.d.ts +196 -1
- package/build/level/tiled/TMXObject.d.ts.map +1 -1
- package/build/level/tiled/TMXObjectFactory.d.ts +18 -5
- package/build/level/tiled/TMXObjectFactory.d.ts.map +1 -1
- package/build/level/tiled/TMXTile.d.ts.map +1 -1
- package/build/level/tiled/TMXTileMap.d.ts.map +1 -1
- package/build/level/tiled/TMXTileset.d.ts +17 -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/factories/shape.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.map +1 -1
- package/build/lighting/light2d.d.ts +30 -0
- package/build/lighting/light2d.d.ts.map +1 -1
- package/build/loader/loadingscreen.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/particles/emitter.d.ts.map +1 -1
- package/build/particles/particle.d.ts.map +1 -1
- package/build/particles/settings.d.ts +25 -0
- 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 +1 -0
- 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/builtin/sat.d.ts +40 -1
- package/build/physics/builtin/sat.d.ts.map +1 -1
- package/build/renderable/animation.d.ts +16 -2
- package/build/renderable/animation.d.ts.map +1 -1
- package/build/renderable/colorlayer.d.ts.map +1 -1
- package/build/renderable/container.d.ts +22 -2
- 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 +69 -1
- package/build/renderable/renderable.d.ts.map +1 -1
- package/build/renderable/sprite.d.ts +23 -7
- package/build/renderable/sprite.d.ts.map +1 -1
- package/build/renderable/sprite3d.d.ts +5 -3
- package/build/renderable/sprite3d.d.ts.map +1 -1
- package/build/renderable/text/text.d.ts.map +1 -1
- package/build/renderable/trail3d.d.ts.map +1 -1
- package/build/renderable/ui/uipanel.d.ts +3 -3
- package/build/renderable/ui/uipanel.d.ts.map +1 -1
- package/build/state/stage.d.ts +12 -4
- 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/timer.d.ts +1 -0
- package/build/system/timer.d.ts.map +1 -1
- package/build/utils/fetchdata.d.ts.map +1 -1
- package/build/video/buffer/vertex.d.ts.map +1 -1
- package/build/video/canvas/canvas_renderer.d.ts +8 -7
- package/build/video/canvas/canvas_renderer.d.ts.map +1 -1
- package/build/video/effects/ambientlight.d.ts +2 -0
- package/build/video/effects/ambientlight.d.ts.map +1 -0
- package/build/video/effects/bloom.d.ts +8 -9
- package/build/video/effects/bloom.d.ts.map +1 -1
- package/build/video/effects/blur.d.ts +3 -2
- package/build/video/effects/blur.d.ts.map +1 -1
- package/build/video/effects/chromaticAberration.d.ts +3 -2
- package/build/video/effects/chromaticAberration.d.ts.map +1 -1
- package/build/video/effects/dissolve.d.ts +5 -2
- package/build/video/effects/dissolve.d.ts.map +1 -1
- package/build/video/effects/dropShadow.d.ts +15 -3
- package/build/video/effects/dropShadow.d.ts.map +1 -1
- package/build/video/effects/flash.d.ts +6 -2
- package/build/video/effects/flash.d.ts.map +1 -1
- package/build/video/effects/frame.d.ts +48 -0
- package/build/video/effects/frame.d.ts.map +1 -0
- package/build/video/effects/glow.d.ts +8 -4
- package/build/video/effects/glow.d.ts.map +1 -1
- package/build/video/effects/glsl_realization.d.ts.map +1 -1
- package/build/video/effects/hologram.d.ts +5 -2
- 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 +8 -4
- package/build/video/effects/outline.d.ts.map +1 -1
- package/build/video/effects/pixelate.d.ts +3 -2
- package/build/video/effects/pixelate.d.ts.map +1 -1
- package/build/video/effects/radialGradient.d.ts +13 -16
- package/build/video/effects/radialGradient.d.ts.map +1 -1
- package/build/video/effects/rgba.d.ts +2 -0
- package/build/video/effects/rgba.d.ts.map +1 -0
- package/build/video/effects/scanline.d.ts.map +1 -1
- package/build/video/effects/shadereffect.d.ts +13 -0
- package/build/video/effects/shadereffect.d.ts.map +1 -1
- package/build/video/effects/shine.d.ts +10 -2
- package/build/video/effects/shine.d.ts.map +1 -1
- package/build/video/effects/squish.d.ts.map +1 -1
- package/build/video/effects/sway.d.ts +69 -0
- package/build/video/effects/sway.d.ts.map +1 -0
- package/build/video/effects/tintPulse.d.ts +5 -2
- package/build/video/effects/tintPulse.d.ts.map +1 -1
- package/build/video/effects/vignette.d.ts.map +1 -1
- package/build/video/effects/wave.d.ts +21 -2
- 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/gpu/frame.d.ts +2 -0
- package/build/video/gpu/frame.d.ts.map +1 -0
- package/build/video/renderer.d.ts +37 -12
- package/build/video/renderer.d.ts.map +1 -1
- package/build/video/rendertarget/canvasrendertarget.d.ts +14 -4
- package/build/video/rendertarget/canvasrendertarget.d.ts.map +1 -1
- package/build/video/rendertarget/rendertarget.d.ts +12 -16
- package/build/video/rendertarget/rendertarget.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 +47 -28
- package/build/video/texture/atlas.d.ts.map +1 -1
- package/build/video/texture/cache.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/webgl/batchers/batcher.d.ts.map +1 -1
- package/build/video/webgl/batchers/lit_quad_batcher.d.ts.map +1 -1
- package/build/video/webgl/batchers/material_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/glshader.d.ts.map +1 -1
- package/build/video/webgl/renderers/tmxlayer/orthogonal.d.ts.map +1 -1
- package/build/video/webgl/shaders/frameclamp.d.ts +2 -0
- package/build/video/webgl/shaders/frameclamp.d.ts.map +1 -0
- package/build/video/webgl/shaders/multitexture-lit.d.ts.map +1 -1
- package/build/video/webgl/shaders/multitexture.d.ts.map +1 -1
- package/build/video/webgl/utils/uniforms.d.ts.map +1 -1
- package/build/video/webgl/webgl_renderer.d.ts +0 -21
- package/build/video/webgl/webgl_renderer.d.ts.map +1 -1
- package/build/video/webgpu/batchers/lit_quad_batcher.d.ts +2 -1
- package/build/video/webgpu/batchers/lit_quad_batcher.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/quad_batcher.d.ts +2 -2
- package/build/video/webgpu/batchers/quad_batcher.d.ts.map +1 -1
- package/build/video/webgpu/buffer/uniformring.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 +3 -4
- package/build/video/webgpu/webgpu_renderer.d.ts.map +1 -1
- package/package.json +21 -19
- package/skills/AGENTS.md +2 -1
- package/skills/melonjs/SKILL.md +1 -0
- package/skills/melonjs-audio/SKILL.md +47 -5
- package/skills/melonjs-camera-and-drawing/SKILL.md +63 -1
- package/skills/melonjs-display-and-scaling/SKILL.md +117 -7
- package/skills/melonjs-effects-and-shaders/SKILL.md +136 -19
- package/skills/melonjs-input/SKILL.md +24 -2
- package/skills/melonjs-lighting/SKILL.md +43 -8
- package/skills/melonjs-loading-assets/SKILL.md +4 -3
- package/skills/melonjs-math/SKILL.md +21 -1
- package/skills/melonjs-particles-and-trails/SKILL.md +36 -2
- package/skills/melonjs-performance/SKILL.md +4 -0
- package/skills/melonjs-plugins/SKILL.md +20 -1
- package/skills/melonjs-renderables/SKILL.md +46 -6
- package/skills/melonjs-renderer-backends/SKILL.md +2 -2
- package/skills/melonjs-scenes-and-state/SKILL.md +17 -2
- package/skills/melonjs-sprites-and-animation/SKILL.md +19 -3
- package/skills/melonjs-tilemaps/SKILL.md +126 -4
|
@@ -110,23 +110,100 @@ reused across renderables.
|
|
|
110
110
|
|
|
111
111
|
## Built-in effects
|
|
112
112
|
|
|
113
|
-
Twenty-
|
|
114
|
-
every one shipping both a GLSL and a WGSL body, so they all run on either
|
|
115
|
-
backend: `VignetteEffect`, `ScanlineEffect` (optional CRT curvature),
|
|
113
|
+
Twenty-three presets, every one constructed as `new XEffect(renderer, options)`
|
|
114
|
+
and every one shipping both a GLSL and a WGSL body, so they all run on either
|
|
115
|
+
GPU backend: `VignetteEffect`, `ScanlineEffect` (optional CRT curvature),
|
|
116
116
|
`BloomEffect`, `GlowEffect`, `BlurEffect`, `PixelateEffect`,
|
|
117
117
|
`ChromaticAberrationEffect`, `DropShadowEffect`, `FlashEffect`,
|
|
118
118
|
`OutlineEffect`, `ColorMatrixEffect`, `DesaturateEffect`, `DissolveEffect`,
|
|
119
|
-
`TintPulseEffect`, `WaveEffect`, `InvertEffect`, `SepiaEffect`,
|
|
119
|
+
`TintPulseEffect`, `WaveEffect`, `SwayEffect`, `InvertEffect`, `SepiaEffect`,
|
|
120
120
|
`HologramEffect`, `ShineEffect`, `RadialGradientEffect`, `SquishEffect`,
|
|
121
121
|
`ToneMappingEffect`.
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
**Colors take an alpha.** Every color option and `setColor` takes `[r, g, b]`
|
|
124
|
+
or `[r, g, b, a]`, each 0.0 to 1.0, and the alpha is the color's own opacity: a
|
|
125
|
+
half-transparent outline, a glow at half strength, a flash that only half
|
|
126
|
+
covers. `[r, g, b]` is opaque, as it always was. Any other length throws.
|
|
127
|
+
`RadialGradientEffect` takes a `Color` instead, and its alpha scales the spot.
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
sprite.addPostEffect(new OutlineEffect(app.renderer, { color: [1, 1, 1, 0.5] }));
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**On a sprite cut from an atlas or a tileset, the effects that read around a
|
|
134
|
+
pixel keep to the sprite's own frame.** `GlowEffect`, `OutlineEffect`,
|
|
135
|
+
`BlurEffect`, `DropShadowEffect`, `ChromaticAberrationEffect`, `WaveEffect`,
|
|
136
|
+
`SwayEffect`, `PixelateEffect` and `BloomEffect` read the neighbouring frames
|
|
137
|
+
as transparent, as if the sprite were drawn on its own, so they never show
|
|
138
|
+
along its edges and the atlas needs no padding for them. Past the texture's own
|
|
139
|
+
edge (a standalone image, or the outside of an atlas) nothing changes.
|
|
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
|
+
|
|
154
|
+
**Their widths, offsets and sizes are in pixels of the texture they draw, with
|
|
155
|
+
nothing to pass.** Each of those effects takes the size of what it draws: the
|
|
156
|
+
sprite's whole texture (the atlas image, not the frame), the screen for a
|
|
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
|
|
161
|
+
`textureSize` (or `setTextureSize()`) fixes the size instead, for a width
|
|
162
|
+
meant in some other texture's pixels.
|
|
163
|
+
|
|
164
|
+
```js
|
|
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
|
|
167
|
+
sprite.addPostEffect(new OutlineEffect(app.renderer, { width: 2 }));
|
|
168
|
+
sprite.addPostEffect(new GlowEffect(app.renderer, { width: 6 }));
|
|
169
|
+
```
|
|
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
|
+
|
|
179
|
+
`SwayEffect` leans a sprite or a layer from side to side, as if in a breeze: the
|
|
180
|
+
top sways most and everything from its `anchor` row down stays put, so a tree's
|
|
181
|
+
trunk or a reed's root stays planted. It suits anything planted at its bottom;
|
|
182
|
+
something hanging from its top, like a banner, needs its own shader. It is a
|
|
183
|
+
`WaveEffect`, which takes the same `anchor`, so it adds no shader. Drive it with
|
|
184
|
+
`setTime(seconds)` every frame, like any time-based effect; it wraps the phase
|
|
185
|
+
itself, so a raw clock never costs precision on a phone's medium-precision
|
|
186
|
+
floats.
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
const sway = new SwayEffect(app.renderer, { strength: 0.02, anchor: 0.9 });
|
|
190
|
+
tree.addPostEffect(sway);
|
|
191
|
+
// each frame
|
|
192
|
+
sway.setTime(timer.getTime() / 1000);
|
|
193
|
+
```
|
|
194
|
+
|
|
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.
|
|
200
|
+
|
|
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.
|
|
130
207
|
|
|
131
208
|
`SquishEffect` bulges a sprite's middle out (`setBulge(0.3)`) or pinches it in
|
|
132
209
|
(`setBulge(-0.3)`), its top and bottom rows staying put. Bulging out widens the
|
|
@@ -178,9 +255,8 @@ app.viewport.addPostEffect(
|
|
|
178
255
|
);
|
|
179
256
|
```
|
|
180
257
|
|
|
181
|
-
Its
|
|
182
|
-
|
|
183
|
-
when applying it to something smaller than the frame.
|
|
258
|
+
Its `radius` is in texels of what it filters, the screen on a camera, and it
|
|
259
|
+
follows a resize by itself.
|
|
184
260
|
|
|
185
261
|
**`ToneMappingEffect` goes after it, and goes last.** Bloom spreads the bright
|
|
186
262
|
parts; the tone map is what stops the sum of the two clipping flat at white,
|
|
@@ -460,6 +536,42 @@ loader.preload([{ name: "ramp", type: "shader", data: myFragmentBody }]);
|
|
|
460
536
|
label.addPostEffect(loader.getShader("ramp"));
|
|
461
537
|
```
|
|
462
538
|
|
|
539
|
+
### Sprites sharing one effect share one draw
|
|
540
|
+
|
|
541
|
+
Sprites drawn one after the other through the **same** effect instance are one
|
|
542
|
+
draw call, as plain sprites are, even when they come from different frames of
|
|
543
|
+
an atlas: a hundred enemies outlined by one shared `OutlineEffect` draw once.
|
|
544
|
+
Each still draws with the values it was given: a uniform set to a **new**
|
|
545
|
+
value draws the sprites queued before it first (setting the value it already
|
|
546
|
+
has changes nothing). What splits the batch:
|
|
547
|
+
|
|
548
|
+
- a uniform taking a new value between two sprites: give a few sprites the
|
|
549
|
+
same value one after the other, rather than a different one each;
|
|
550
|
+
- a different texture (the effect path samples one texture per draw);
|
|
551
|
+
- a different effect instance, or a sprite without the effect in between;
|
|
552
|
+
- a `noise_uv` effect on sprites of different frames or sizes (its rect is
|
|
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;
|
|
558
|
+
- a `screen_texture` effect, which draws each sprite on its own, as each one
|
|
559
|
+
samples a capture of everything drawn before it.
|
|
560
|
+
|
|
561
|
+
So share one instance (the loader's `"shader"` type hands one back, see
|
|
562
|
+
above) rather than constructing an effect per sprite: *n* instances are *n*
|
|
563
|
+
draws as well as *n* programs. One you construct yourself and share needs
|
|
564
|
+
`effect.shared = true`, or the first renderable destroyed destroys it for the
|
|
565
|
+
others (the loader's are marked shared already):
|
|
566
|
+
|
|
567
|
+
```js
|
|
568
|
+
const outline = new OutlineEffect(renderer, { color: [1, 1, 1] });
|
|
569
|
+
outline.shared = true; // you destroy it, when no enemy is left
|
|
570
|
+
for (const enemy of enemies) {
|
|
571
|
+
enemy.addPostEffect(outline);
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
463
575
|
### Declare a level's effects with that level's assets
|
|
464
576
|
|
|
465
577
|
An effect the loader knows about can be prepared before anything draws it; an
|
|
@@ -522,7 +634,9 @@ For refraction, distortion and anything that samples what is already drawn:
|
|
|
522
634
|
- **`screen_texture`** — a sampler kept filled with everything drawn so far
|
|
523
635
|
(GLSL: annotate the sampler `uniform sampler2D tex : screen_texture;`)
|
|
524
636
|
- **`screen_uv`** — this fragment's 0..1 position in that capture
|
|
525
|
-
- **`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)
|
|
526
640
|
|
|
527
641
|
In WGSL the capture is sampled through `screen_sampler` (clamped) or
|
|
528
642
|
`screen_sampler_repeat` (wrapping) instead of GLSL's `: screen_texture`
|
|
@@ -617,11 +731,13 @@ that against your request detects the fallback.
|
|
|
617
731
|
|
|
618
732
|
## The Canvas fallback
|
|
619
733
|
|
|
620
|
-
`ShaderEffect` on a Canvas renderer
|
|
621
|
-
|
|
622
|
-
|
|
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.
|
|
623
738
|
|
|
624
|
-
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
|
|
625
741
|
content — the carrier renderable then shows through raw. A 1×1 white texture
|
|
626
742
|
stretched to 280×156 renders as a white box where the effect should be.
|
|
627
743
|
|
|
@@ -650,6 +766,7 @@ same question after construction.
|
|
|
650
766
|
| ported shader renders upside down | a hand-bound `toFrameTexture()` capture — GL is bottom-up, WebGPU top-down |
|
|
651
767
|
| shadow/smear offset flips on some draws | vertical UV offset not multiplied by `uUVYDir` |
|
|
652
768
|
| frame rate collapses with many blended sprites | an *advanced* mode (overlay, darken, …) — each draw is a capture plus a composite |
|
|
769
|
+
| many sprites with the same effect still cost a draw each | an effect instance per sprite, or a uniform set to a different value per sprite — share one instance and group sprites by value |
|
|
653
770
|
| animated shader never moves | `setTime` fed milliseconds, or the shader declares no `uTime` |
|
|
654
771
|
| squash and stretch only bulges, the height never changes, under Matter | the adapter rewrites `currentTransform` every step; set the scale from the sprite's `update()` |
|
|
655
772
|
| `SquishEffect` squishes the whole screen instead of the sprite | it is chained with another effect, or on the camera; give the sprite it alone |
|
|
@@ -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, 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,27 @@ 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
|
+
A single key can also be pressed directly, reaching the same actions:
|
|
277
297
|
|
|
278
298
|
```js
|
|
279
299
|
input.triggerKeyEvent(input.KEY.LEFT, true); // press
|
|
@@ -299,6 +319,8 @@ input.triggerKeyEvent(input.KEY.LEFT, false); // release
|
|
|
299
319
|
| 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
320
|
| 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
321
|
| every click in the game stops working | a handler on `app.viewport` returning `false`, which is asked first |
|
|
322
|
+
| touch controls do nothing | nothing bound to their slot — `bindGamepad` on the plugin's `index` (0 by default), or give the buttons a `key` |
|
|
323
|
+
| touch controls never show on a desktop | the gamepad plugin's `enabled` defaults to `device.touch` — pass `enabled: true` to try them there |
|
|
302
324
|
|
|
303
325
|
## Related skills
|
|
304
326
|
|
|
@@ -41,6 +41,34 @@ normal-mapped sprites.
|
|
|
41
41
|
returns immediately while its alpha is 0 — so with no ambient colour set there
|
|
42
42
|
is nothing to cut through and the lights just look like soft additive blobs.
|
|
43
43
|
|
|
44
|
+
## Soft edges: `softness`
|
|
45
|
+
|
|
46
|
+
A light's hole in the darkness is hard-edged by default: full light inside its
|
|
47
|
+
radius, full darkness outside. `light.softness` (0 to 1) fades it instead:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const torch = new Light2d(x, y, 220, 220, "#ffb46a", 0.4);
|
|
51
|
+
torch.softness = 0.8; // lit core of 20% of the radius, then fading out
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `1` thins the darkness straight from the centre to the edge, the same falloff
|
|
55
|
+
as the glow; in between, a fully lit core of `1 - softness` of the radius.
|
|
56
|
+
- Hard and soft lights mix in one scene. Where lights overlap, what is left of the
|
|
57
|
+
darkness is what each leaves, multiplied, so two half-lights make three
|
|
58
|
+
quarters light.
|
|
59
|
+
- The hole is the light's size. For an opening wider than the glow (a torch whose
|
|
60
|
+
light reaches further than its flame's halo), add a second, bigger light with
|
|
61
|
+
`illuminationOnly = true`: it cuts the darkness and draws nothing.
|
|
62
|
+
- Up to 32 lights in view are cut by one soft pass; lights outside the view do not
|
|
63
|
+
count. A scene with no soft light draws exactly as before.
|
|
64
|
+
- To keep a camera out of the darkness (a minimap showing the level lit),
|
|
65
|
+
override `drawLighting` on the stage and skip that camera.
|
|
66
|
+
- To keep one light out of a camera, put it, or a container it is in, on a
|
|
67
|
+
`visibilityLayer` the camera's `cullMask` leaves out: there it neither glows,
|
|
68
|
+
nor cuts the darkness, nor lights normal-mapped sprites (see
|
|
69
|
+
`melonjs-camera-and-drawing`). This is for a `Light2d`: a `Light3d` lights
|
|
70
|
+
the meshes of every camera.
|
|
71
|
+
|
|
44
72
|
## Multiple lights
|
|
45
73
|
|
|
46
74
|
Add as many as you need — the glow quads and the ambient cutouts accumulate with
|
|
@@ -131,8 +159,9 @@ Unlit areas of a normal-mapped sprite render pure black unless you raise
|
|
|
131
159
|
`Stage.ambientLightingColor` (default black) — that is the base level added to
|
|
132
160
|
every lit pixel, and it is a different knob from `Stage.ambientLight`.
|
|
133
161
|
|
|
134
|
-
Without a normal map a sprite is
|
|
135
|
-
|
|
162
|
+
Without a normal map a sprite is not shaded at all: it takes the unlit path on
|
|
163
|
+
every backend, and a light changes how it looks only through its additive glow
|
|
164
|
+
and its cutout in the ambient darkness.
|
|
136
165
|
|
|
137
166
|
## What needs a GPU backend, and what does not
|
|
138
167
|
|
|
@@ -140,13 +169,15 @@ Less than you would expect. `Light2d` is renderer-agnostic: it just calls
|
|
|
140
169
|
`renderer.drawLight(this)`, and every backend implements it — the GPU backends
|
|
141
170
|
through a shared procedural radial-falloff shader, the Canvas renderer by
|
|
142
171
|
rasterising a cached `Gradient` into one shared render target and compositing it
|
|
143
|
-
with `drawImage`. The ambient cutout pass is plain `setMask` + `fillRect
|
|
144
|
-
|
|
172
|
+
with `drawImage`. The hard ambient cutout pass is plain `setMask` + `fillRect`;
|
|
173
|
+
the soft one is one shader pass on the GPU backends and an offscreen canvas on
|
|
174
|
+
Canvas. So the lit-spot look, soft or hard, works everywhere, Canvas included.
|
|
145
175
|
|
|
146
176
|
What Canvas cannot do is **per-pixel normal-map lighting**:
|
|
147
|
-
`renderer.setLightUniforms()` is a no-op there, and the first
|
|
148
|
-
|
|
149
|
-
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.
|
|
150
181
|
|
|
151
182
|
```js
|
|
152
183
|
if (app.renderer.shaderLanguage === null) {
|
|
@@ -171,7 +202,11 @@ See the `melonjs-3d` skill.
|
|
|
171
202
|
| symptom | cause |
|
|
172
203
|
|---|---|
|
|
173
204
|
| lights added but the scene never darkens | no ambient colour set — `ambientLight` alpha is 0, so the pass returns early |
|
|
174
|
-
|
|
|
205
|
+
| the lit area has a hard rim | `softness` is 0 (the default): set it on the light |
|
|
206
|
+
| the darkness opens no wider than the glow | the hole is the light's size: add a bigger `illuminationOnly` light for the opening |
|
|
207
|
+
| a minimap or second camera is dark too | every camera draws the stage's darkness: override `drawLighting` and skip that camera |
|
|
208
|
+
| a light should show in one camera only | put it on a `visibilityLayer` the other camera's `cullMask` leaves out |
|
|
209
|
+
| sprite not shaded by a light, only glowed over | no `normalMap` on the sprite, or the Canvas backend is active |
|
|
175
210
|
| normal-mapped sprite is pure black where unlit | `Stage.ambientLightingColor` left at black |
|
|
176
211
|
| the 33rd light shades nothing | `MAX_LIGHTS = 32` for the normal-map pipeline |
|
|
177
212
|
| `TypeError` assigning `normalMap` | not an image-like value (a video element is rejected outright) |
|
|
@@ -183,7 +183,8 @@ the hard way:
|
|
|
183
183
|
### `DefaultLoadingScreen` lives in the WORLD, not on the screen
|
|
184
184
|
|
|
185
185
|
The built-in loading screen is written for a `Camera2d`. Its progress bar and
|
|
186
|
-
logo are added
|
|
186
|
+
logo are added together, once the logo is decoded (a frame or so after the
|
|
187
|
+
screen comes up), with `app.world.addChild(bar, 1)` / `addChild(logo, 2)`, and are
|
|
187
188
|
**`floating === false`** — ordinary world-space renderables. They read as an
|
|
188
189
|
overlay only because that stage's world is otherwise empty.
|
|
189
190
|
|
|
@@ -195,7 +196,7 @@ in front of a bar sitting at z = 1. What does NOT rescue it, all tried:
|
|
|
195
196
|
depth, so the scene's own z values are geometry, not layering, and the bar
|
|
196
197
|
still loses to the depth test.
|
|
197
198
|
- **`moveToTop()`** — it early-returns for a child already at index 0, which the
|
|
198
|
-
loading screen's children
|
|
199
|
+
loading screen's children usually are, having been added before anything else.
|
|
199
200
|
It silently does nothing.
|
|
200
201
|
- **Flipping them to `floating = true`** for the duration — the scene still
|
|
201
202
|
covers them.
|
|
@@ -397,7 +398,7 @@ throws, the same trap as the removed `loader.onload` / `onProgress`.
|
|
|
397
398
|
|
|
398
399
|
| | |
|
|
399
400
|
| --- | --- |
|
|
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 |
|
|
401
|
+
| 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
402
|
| 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
403
|
|
|
403
404
|
## Symptom → cause
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-math
|
|
3
|
-
description: "Use this skill for vector and matrix maths in melonJS — Vector2d, Vector3d, their observable variants, Matrix2d, Matrix3d, and the math utility functions. Covers which methods mutate and which allocate, the vector pool and when clone() is the wrong tool, interpolation with lerp, slerp, damp and moveTowards, and the argument defaults that silently drop a component. Triggers on: Vector2d, Vector3d, ObservableVector2d, ObservableVector3d, Matrix2d, Matrix3d, vector, vector maths, dot, cross, normalize, length, distance, angle, lerp, slerp, damp, moveTowards, project, clamp, degToRad, radToDeg, clone, vector pool, vector3dPool, setMuted, setCallback, my vector is wrong, z is zero, scale ignores z., moveTowards ignores z, projectN, currentTransform, setTransform, matrix, transform matrix, column-major, val, Float32Array, basis matrix, orientation matrix."
|
|
3
|
+
description: "Use this skill for vector and matrix maths in melonJS — Vector2d, Vector3d, their observable variants, Matrix2d, Matrix3d, and the math utility functions. Covers which methods mutate and which allocate, the vector pool and when clone() is the wrong tool, interpolation with lerp, slerp, damp and moveTowards, and the argument defaults that silently drop a component. Triggers on: Vector2d, Vector3d, ObservableVector2d, ObservableVector3d, Matrix2d, Matrix3d, vector, vector maths, dot, cross, normalize, length, distance, angle, lerp, slerp, damp, moveTowards, project, clamp, degToRad, radToDeg, clone, vector pool, vector3dPool, setMuted, setCallback, my vector is wrong, z is zero, scale ignores z., moveTowards ignores z, projectN, currentTransform, setTransform, matrix, transform matrix, column-major, val, Float32Array, basis matrix, orientation matrix, Noise, getNoise1d, flicker, wobble."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -291,6 +291,25 @@ math.pow(n); // n squared
|
|
|
291
291
|
math.DEG_TO_RAD math.RAD_TO_DEG math.TAU math.ETA math.EPSILON
|
|
292
292
|
```
|
|
293
293
|
|
|
294
|
+
### Values that wobble over time
|
|
295
|
+
|
|
296
|
+
`Noise`, the generator behind `NoiseTexture2d`, also samples directly. For a
|
|
297
|
+
value that should drift without repeating (a torch's flicker, a sway, a camera
|
|
298
|
+
wander), sample it with time:
|
|
299
|
+
|
|
300
|
+
```js
|
|
301
|
+
import { Noise } from "melonjs";
|
|
302
|
+
|
|
303
|
+
const noise = new Noise({ seed: 7, frequency: 1.5, octaves: 3 });
|
|
304
|
+
// around 1; give each object its own offset so they do not move together
|
|
305
|
+
const flicker = 1 + 0.2 * noise.getNoise1d(seconds + offset);
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
`getNoise1d` returns `[-1, 1]` (with fractal octaves most values fall within
|
|
309
|
+
about ±0.6), `frequency` is in changes per unit of the input, and the same seed
|
|
310
|
+
gives the same field every run. A sum of sines repeats where people notice it,
|
|
311
|
+
and `Math.random()` every frame jitters instead of drifting.
|
|
312
|
+
|
|
294
313
|
## Symptoms
|
|
295
314
|
|
|
296
315
|
| symptom | cause |
|
|
@@ -305,6 +324,7 @@ math.DEG_TO_RAD math.RAD_TO_DEG math.TAU math.ETA math.EPSILON
|
|
|
305
324
|
| you built your own 16-number array | `Renderable.currentTransform` is already a `Matrix3d`; write its `val` |
|
|
306
325
|
| a value read back from a matrix differs | `val` is `Float32Array`; it holds float32 |
|
|
307
326
|
| easing differs at 120fps | per-frame `lerp`; use `damp` |
|
|
327
|
+
| a flicker or sway looks mechanical, or visibly repeats | a sum of sines or a per-frame `Math.random()`: sample `Noise#getNoise1d(time)` |
|
|
308
328
|
|
|
309
329
|
## Related skills
|
|
310
330
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-particles-and-trails
|
|
3
|
-
description: "Use this skill for particle effects and motion trails in melonJS — ParticleEmitter configuration, burst versus stream emission, the referenceSpace frame of reference introduced in 20.2, 3D particle motion through elevation and minSpread/maxSpread in 20.8, and the Trail renderable. Covers the emitter that silently does nothing until you call an emit method. Triggers on: Trail3d, ribbon, exhaust trail, speed streak, ski track, tyre mark, ribbonNormal, ParticleEmitter, particles, burstParticles, streamParticles, referenceSpace, ParticleEmitterSettings, elevation, minSpread, maxSpread, spawn area, Trail, explosion, smoke, sparks, debris, ring, exhaust, emitter."
|
|
3
|
+
description: "Use this skill for particle effects and motion trails in melonJS — ParticleEmitter configuration, burst versus stream emission, the referenceSpace frame of reference introduced in 20.2, 3D particle motion through elevation and minSpread/maxSpread in 20.8, and the Trail renderable. Covers the emitter that silently does nothing until you call an emit method. Triggers on: Trail3d, ribbon, exhaust trail, speed streak, ski track, tyre mark, ribbonNormal, ParticleEmitter, particles, burstParticles, streamParticles, referenceSpace, ParticleEmitterSettings, elevation, minSpread, maxSpread, spawn area, Trail, explosion, smoke, sparks, debris, ring, exhaust, emitter, tintOverLife, colour over life, color over life, flame, fire."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -60,7 +60,8 @@ pair it with `framewidth`/`frameheight` or it does nothing.
|
|
|
60
60
|
|
|
61
61
|
Note the variety is in SHAPE, not colour. `tint` belongs to the emitter, so
|
|
62
62
|
every particle from one emitter is tinted alike however many frames it picks
|
|
63
|
-
between.
|
|
63
|
+
between. For a colour that changes over each particle's life, use
|
|
64
|
+
`tintOverLife` (below) rather than drawing one frame per colour.
|
|
64
65
|
|
|
65
66
|
Frame dimensions must be positive integers that fit inside the image, and an
|
|
66
67
|
explicit `frames` sequence must be nonempty and contain valid integer indices.
|
|
@@ -69,6 +70,35 @@ Invalid configurations throw `RangeError` before changing an emitter during
|
|
|
69
70
|
only future particles, so ones already alive finish with their original image,
|
|
70
71
|
frame size and sequence.
|
|
71
72
|
|
|
73
|
+
## Colour over life: `tintOverLife`
|
|
74
|
+
|
|
75
|
+
`tintOverLife` is the list of tints a particle takes over its life: the first at
|
|
76
|
+
birth, the last at the end, evenly spaced and blended in between. Give as many
|
|
77
|
+
CSS colours as you like (repeat one to hold it longer); it replaces `tint`.
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
// a flame: white hot, then orange, then deep red as it rises and dies
|
|
81
|
+
const flame = new ParticleEmitter(x, y, {
|
|
82
|
+
image: softDot, // a white texture takes the tints as they are
|
|
83
|
+
tintOverLife: ["#fff3b0", "#ffc455", "#ff8a26", "#e45414", "#a0280c"],
|
|
84
|
+
textureAdditive: true,
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- **Colour only.** Opacity is still the built-in fade over the particle's life;
|
|
89
|
+
the alpha of a colour in the list is ignored. On an additive emitter, a darker
|
|
90
|
+
colour is a dimmer particle, so fold a "fainter" step into the colour itself.
|
|
91
|
+
- **32 steps.** However many colours, the blend is taken in 32 steps, the same on
|
|
92
|
+
every renderer: a particle changes colour at most 32 times. That matters on
|
|
93
|
+
Canvas, which keeps a tinted copy of the texture for every colour it is asked
|
|
94
|
+
to draw: 32 at most per texture.
|
|
95
|
+
- **Author the texture white.** `tint` multiplies, so a coloured texture muddies
|
|
96
|
+
every step.
|
|
97
|
+
- It works alongside sprite-sheet `frames` (one picks the image, the other the
|
|
98
|
+
colour), and a list that is empty, not an array, or holds a non-string throws a
|
|
99
|
+
`TypeError` from the constructor or `reset()`, which then leaves the emitter as
|
|
100
|
+
it was. The list is copied: change it through `reset()`.
|
|
101
|
+
|
|
72
102
|
## Where particles are born
|
|
73
103
|
|
|
74
104
|
`width` and `height` are the spawn AREA, and both default to `1` — so by
|
|
@@ -290,6 +320,7 @@ answer when you want discrete puffs rather than a continuous ribbon.
|
|
|
290
320
|
| symptom | cause |
|
|
291
321
|
|---|---|
|
|
292
322
|
| trail looks flat under a `Camera3d` | `Trail` is 2D; use `Trail3d` |
|
|
323
|
+
| particles clutter a minimap or a second camera | every camera draws them: set the emitter's `visibilityLayer` to a layer that camera's `cullMask` leaves out (see `melonjs-camera-and-drawing`) |
|
|
293
324
|
| `Trail3d` draws nothing, with a console warning | it needs a `Camera3d`; the app's `cameraClass` is still the 2D default |
|
|
294
325
|
| `trail.tint` does nothing | `Trail` cannot be tinted at all; `Trail3d` can |
|
|
295
326
|
| assigning `maxPoints` throws | `Trail3d` sizes its geometry once; pass `length` to the constructor |
|
|
@@ -298,6 +329,9 @@ answer when you want discrete puffs rather than a continuous ribbon.
|
|
|
298
329
|
| the cloud follows a moving emitter | default `referenceSpace: "local"` — use `"world"` for trails |
|
|
299
330
|
| particles do not travel far enough | settings tuned pre-20.2, when drawn travel was doubled |
|
|
300
331
|
| one-shot emitters accumulate | missing `autoDestroyOnComplete: true` |
|
|
332
|
+
| every particle of an emitter is the same colour all its life | `tint` is one value for the emitter: use `tintOverLife` for a colour that changes as it ages |
|
|
333
|
+
| a `tintOverLife` colour's transparency has no effect | the list sets the colour only; opacity is the particle's fade |
|
|
334
|
+
| `tintOverLife` colours look muddy | the texture is coloured; author it white so the tints come through as given |
|
|
301
335
|
| frame rate drops with many effects | an emitter per event instead of one re-aimed emitter |
|
|
302
336
|
| a 3D burst is invisible under a `Camera3d` | before 20.8 a particle was culled at twice its depth; update |
|
|
303
337
|
| the whole burst launches from one pixel | `width`/`height` are the spawn area and default to `1` |
|
|
@@ -222,6 +222,9 @@ queries one at a time, usually not. See `melonjs-3d` for the decision table.
|
|
|
222
222
|
|
|
223
223
|
- `stream: true` on long music tracks plays through a streaming element instead
|
|
224
224
|
of decoding the whole file into memory.
|
|
225
|
+
- `preload: false` on music a player may never reach (a boss theme, the
|
|
226
|
+
ending) keeps it out of the initial download: it is fetched and streamed
|
|
227
|
+
when first played.
|
|
225
228
|
- `pool` on an audio clip sizes the reuse pool for finished instances — it is
|
|
226
229
|
**not** a concurrency cap.
|
|
227
230
|
- `loader.unloadAll()` between large levels drops every loaded asset and
|
|
@@ -373,6 +376,7 @@ In rough order for a typical 2D game:
|
|
|
373
376
|
| a freeze the first time a level shows, never again | shader variants linking on first draw — warm them behind the loading screen |
|
|
374
377
|
| a freeze the first time an effect appears mid-play (a shield, a hit flash) | the effect attached in the same frame it is drawn — attach it when the pool is built, so the warm-up has a frame to finish |
|
|
375
378
|
| the same scene stutters once per entry | a derived texture rebuilt per stage: the texture cache keys on the image object, so each rebuild is a new upload |
|
|
379
|
+
| steady movement stutters while the frame rate reads 60 | not a cost: frame-time jitter gives a frame no update and the next two; set `syncToDisplay: true` (see `melonjs-display-and-scaling`) |
|
|
376
380
|
|
|
377
381
|
## Related skills
|
|
378
382
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-plugins
|
|
3
|
-
description: "Use this skill when adding a melonJS plugin or adapter — the debug panel, Spine skeletal animation, compressed Tiled map support, Capacitor packaging — or when writing your own plugin. Covers plugin.register and its version gate, plugin.cache, plugin.patch, and why physics adapters are not plugins. Triggers on: plugin, plugin.register, plugin.cache, plugin.get, plugin.patch, BasePlugin, debug-plugin, DebugPanelPlugin, SpinePlugin, TiledInflatePlugin, CapacitorPlugin, adapter, custom plugin, plugin version mismatch, already registered."
|
|
3
|
+
description: "Use this skill when adding a melonJS plugin or adapter — the debug panel, Spine skeletal animation, compressed Tiled map support, on-screen touch controls, Capacitor packaging — or when writing your own plugin. Covers plugin.register and its version gate, plugin.cache, plugin.patch, and why physics adapters are not plugins. Triggers on: plugin, plugin.register, plugin.cache, plugin.get, plugin.patch, BasePlugin, debug-plugin, DebugPanelPlugin, SpinePlugin, TiledInflatePlugin, GamepadPlugin, gamepad-plugin, touch controls, virtual joystick, CapacitorPlugin, adapter, custom plugin, plugin version mismatch, already registered."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -88,6 +88,25 @@ 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
|
+
### `@melonjs/gamepad-plugin`
|
|
92
|
+
|
|
93
|
+
On-screen touch controls, reported as a gamepad: one or two sticks and any
|
|
94
|
+
number of buttons, drawn over the game and read by the engine as a pad in a
|
|
95
|
+
gamepad slot. The game's `bindGamepad` bindings drive it unchanged, so one set
|
|
96
|
+
of bindings covers a real pad and a touch screen.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import { GamepadPlugin } from "@melonjs/gamepad-plugin";
|
|
100
|
+
plugin.register(GamepadPlugin, "gamepad", {
|
|
101
|
+
buttons: [{ label: "A" }, { label: "B" }],
|
|
102
|
+
});
|
|
103
|
+
// plugin.cache.gamepad.visible — showing, to word prompts ("tap" or a key)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Shown on touch screens by default (`enabled`), hidden while a real pad is in
|
|
107
|
+
use. The left stick also presses the D-pad; a button can press a key instead
|
|
108
|
+
of a pad button. See `melonjs-input` for the bindings.
|
|
109
|
+
|
|
91
110
|
### `@melonjs/capacitor-plugin`
|
|
92
111
|
|
|
93
112
|
Bridges Capacitor's native lifecycle and hardware-back events into the engine:
|
|
@@ -96,9 +96,44 @@ world.addChild(sprite, 10); // ✓ the only reliable form
|
|
|
96
96
|
2. **Setting `depth` *before* `addChild` is overwritten.** With `autoDepth` on
|
|
97
97
|
(the default), `addChild` without an explicit `z` assigns
|
|
98
98
|
`child.pos.z = children.length` after the push, discarding whatever you set.
|
|
99
|
-
3. **Setting `depth` *after* `addChild` does not reorder.**
|
|
100
|
-
a child is added (`autoSort`, deferred to the next tick), not when
|
|
101
|
-
changes. If you must change it later, call `container.sort()`
|
|
99
|
+
3. **Setting `depth` *after* `addChild` does not reorder.** A `"z"` sort runs
|
|
100
|
+
when a child is added (`autoSort`, deferred to the next tick), not when
|
|
101
|
+
`pos.z` changes. If you must change it later, call `container.sort()`
|
|
102
|
+
yourself, or use `moveToTop` / `moveUp` / `moveDown` / `moveToBottom`, which
|
|
103
|
+
reorder the children themselves. (An `"x"` / `"y"` sort is different: see
|
|
104
|
+
below.)
|
|
105
|
+
|
|
106
|
+
### Sorting on `"y"` (or `"x"`), and `sortOffset`
|
|
107
|
+
|
|
108
|
+
A container with `sortOn = "y"` draws its children from the smallest `pos.y` to
|
|
109
|
+
the largest, so whatever is lower on screen covers what is above it (top-down and
|
|
110
|
+
isometric games). `"x"` does the same along x. A different `pos.z` still decides
|
|
111
|
+
first, so keep the children that should interleave at one z.
|
|
112
|
+
|
|
113
|
+
- **Moving a child re-sorts by itself.** With `autoSort` on (the default), the
|
|
114
|
+
container checks its order at the end of every update, after physics and the
|
|
115
|
+
children's own updates, and re-sorts when anything is out of place: a walking
|
|
116
|
+
character needs no `sort()` call. A frame where nothing moved costs one pass
|
|
117
|
+
over the children and does not cause a redraw. Turn `autoSort` off to keep
|
|
118
|
+
the children in the order you put them.
|
|
119
|
+
- **Ties keep insertion order, and the child added LAST is drawn BEHIND.** A
|
|
120
|
+
torch placed at its wall's exact position disappears behind the wall.
|
|
121
|
+
|
|
122
|
+
`sortOffset` changes the ORDER only, never where anything is: the child is lined
|
|
123
|
+
up as if it stood that many pixels lower (positive) or higher (negative), and is
|
|
124
|
+
drawn exactly where it was. Use it when `pos` is not the point to sort by:
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
torch.sortOffset = 1; // win the tie with the wall it hangs on
|
|
128
|
+
dust.sortOffset = door.pos.y - dust.pos.y; // an emitter's pos is the top of its
|
|
129
|
+
// spawn area: line it up with the door
|
|
130
|
+
prop.sortOffset = prop.height; // sort by the bottom of the image
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
A container that keeps itself sorted picks a change up at its next update (with
|
|
134
|
+
`autoSort` off, call its `sort()`). `"z"` and `"depth"` sorting ignore it. It must be a finite number (a string
|
|
135
|
+
or `NaN` throws, rather than scrambling every sibling), and a pooled renderable
|
|
136
|
+
starts again at `0`.
|
|
102
137
|
|
|
103
138
|
The exception: under `Camera3d` the world's `sortOn` is `"depth"`, and the
|
|
104
139
|
camera calls `container.sortNow(true)` every frame. Note that mode does **not**
|
|
@@ -139,9 +174,10 @@ Use `addPostEffect()` / `getPostEffect()` / `removePostEffect()`. Assigning
|
|
|
139
174
|
`renderable.shader = …` is deprecated since 19.2.0.
|
|
140
175
|
|
|
141
176
|
Post effects need a programmable pipeline. A `ShaderEffect` constructed against
|
|
142
|
-
the Canvas renderer (`renderer.shaderLanguage === null`)
|
|
143
|
-
|
|
144
|
-
`beginPostEffect` filters disabled effects out.
|
|
177
|
+
the Canvas renderer (`renderer.shaderLanguage === null`) stays at
|
|
178
|
+
`enabled === false` and no-ops every method rather than throwing — and
|
|
179
|
+
`beginPostEffect` filters disabled effects out. The first one logs a warning,
|
|
180
|
+
the rest stay quiet, so a decorative effect needs no renderer check. Since `video.AUTO` picks the
|
|
145
181
|
backend at runtime, an effect that works on your machine may do nothing on
|
|
146
182
|
another.
|
|
147
183
|
|
|
@@ -262,6 +298,10 @@ other correct ending.
|
|
|
262
298
|
- `floating = true` pins a renderable to the screen rather than the world, which
|
|
263
299
|
is what you want for HUD elements. It opts the object out of camera transforms
|
|
264
300
|
and out of ancestor position accumulation.
|
|
301
|
+
- `visibilityLayer` decides which cameras draw a renderable: a camera draws it
|
|
302
|
+
only if it shares a bit with the camera's `cullMask` (every camera, by
|
|
303
|
+
default). See `melonjs-camera-and-drawing` for keeping something off a
|
|
304
|
+
minimap.
|
|
265
305
|
|
|
266
306
|
## Prefer built-in features
|
|
267
307
|
|
|
@@ -47,8 +47,8 @@ once and then degrades or no-ops, so the game runs and looks wrong.
|
|
|
47
47
|
Two Canvas rows are worth spelling out because they are easy to misread as total
|
|
48
48
|
failures. A `Light2d` still *draws* — the Canvas renderer bakes a radial gradient
|
|
49
49
|
per light and blits it — what disappears is per-pixel normal-map shading, which
|
|
50
|
-
warns once (`Light2d normal-map lighting requires
|
|
51
|
-
renders sprites unlit. A `Mesh` still draws too, sorted with the painter's
|
|
50
|
+
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, sorted with the painter's
|
|
52
52
|
algorithm and no depth buffer, so convex models look right and anything
|
|
53
53
|
self-overlapping does not. `"none"` (replace, alpha included) is the one blend
|
|
54
54
|
mode Canvas cannot express — there is no `globalCompositeOperation` that
|