melonjs 20.10.0 → 20.11.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 -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 +48 -1
- 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/playback.d.ts.map +1 -1
- package/build/audio/types.d.ts +19 -1
- 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/effects/shake_effect.d.ts +3 -1
- package/build/camera/effects/shake_effect.d.ts.map +1 -1
- package/build/index.d.ts +4 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3559 -1070
- package/build/index.js.map +4 -4
- 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/loader.d.ts +62 -17
- package/build/loader/loader.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/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 +10 -1
- 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/frameAnimation.d.ts.map +1 -1
- package/build/renderable/imagelayer.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 +19 -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/state/stage.d.ts +9 -2
- package/build/state/stage.d.ts.map +1 -1
- package/build/state/state.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 +7 -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 +7 -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/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 +9 -4
- 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/shadereffect.d.ts +13 -0
- package/build/video/effects/shadereffect.d.ts.map +1 -1
- package/build/video/effects/shine.d.ts +5 -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 +64 -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/wave.d.ts +17 -0
- package/build/video/effects/wave.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 +35 -11
- 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/texture/atlas.d.ts +23 -4
- package/build/video/texture/atlas.d.ts.map +1 -1
- package/build/video/texture/cache.d.ts.map +1 -1
- 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/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/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/pipeline/cache.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.map +1 -1
- package/package.json +21 -19
- package/skills/melonjs-audio/SKILL.md +22 -1
- package/skills/melonjs-camera-and-drawing/SKILL.md +63 -1
- package/skills/melonjs-display-and-scaling/SKILL.md +98 -2
- package/skills/melonjs-effects-and-shaders/SKILL.md +89 -7
- package/skills/melonjs-lighting/SKILL.md +39 -5
- package/skills/melonjs-loading-assets/SKILL.md +100 -9
- package/skills/melonjs-math/SKILL.md +21 -1
- package/skills/melonjs-particles-and-trails/SKILL.md +36 -2
- package/skills/melonjs-performance/SKILL.md +7 -2
- package/skills/melonjs-renderables/SKILL.md +42 -3
- package/skills/melonjs-scenes-and-state/SKILL.md +7 -0
- package/skills/melonjs-sprites-and-animation/SKILL.md +19 -3
- package/skills/melonjs-tilemaps/SKILL.md +126 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-loading-assets
|
|
3
|
-
description: "Use this skill for loading and managing game assets in melonJS — the resource descriptor format, the full asset type list, preload versus load, retrieving loaded assets, base URLs and cross-origin settings, and the loading screen. Triggers on: loader, loader.preload, loader.load, resources, getImage, getJSON, getTMX, getGLTF, getVideo, getFont, setBaseURL, setOptions, crossOrigin, withCredentials, credentials, cookies, authenticated assets, CORS, asset, preload, LOADER_COMPLETE, loading screen, unload."
|
|
3
|
+
description: "Use this skill for loading and managing game assets in melonJS — the resource descriptor format, the full asset type list, preload versus load, retrieving loaded assets, base URLs and cross-origin settings, and the loading screen. Triggers on: loader, loader.preload, loader.load, resources, getImage, getJSON, getTMX, getGLTF, getVideo, getFont, setBaseURL, setOptions, crossOrigin, withCredentials, credentials, cookies, authenticated assets, CORS, asset, preload, LOADER_COMPLETE, loading screen, unload, setParser, custom asset type, generated texture, procedural texture."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -50,6 +50,7 @@ The type strings are melonJS-specific — this is not a generic loader:
|
|
|
50
50
|
| `"obj"` / `"mtl"` | Wavefront models and materials |
|
|
51
51
|
| `"gltf"` / `"glb"` | glTF scenes |
|
|
52
52
|
| `"js"` | scripts |
|
|
53
|
+
| any type you register | your own formats, and things you generate rather than download — see [Custom asset types](#custom-asset-types) |
|
|
53
54
|
|
|
54
55
|
## What belongs in the manifest — and what cannot
|
|
55
56
|
|
|
@@ -84,14 +85,24 @@ is compiled at load time, behind the loading screen, and the loader hands out a
|
|
|
84
85
|
renderable's teardown freeing it out from under the others. `clone()` does NOT
|
|
85
86
|
help here — it constructs a new effect, and therefore links a new program.
|
|
86
87
|
|
|
87
|
-
**2. Derived assets — build once,
|
|
88
|
-
`NoiseTexture2d`, `TextureAtlas`: the
|
|
89
|
-
not be rebuilt per scene either. The renderer's texture cache is
|
|
90
|
-
**image object**, so a freshly baked canvas is a brand new GPU
|
|
91
|
-
new mip chain — rebuild one per stage and a title → play →
|
|
92
|
-
uploads four copies of the same pixels. Build
|
|
93
|
-
|
|
94
|
-
`TextureAtlas
|
|
88
|
+
**2. Derived assets — build once, before the first stage.** Canvases you bake,
|
|
89
|
+
`NoiseTexture2d`, `TextureAtlas`: none of the built-in types describes these,
|
|
90
|
+
and they must not be rebuilt per scene either. The renderer's texture cache is
|
|
91
|
+
keyed by the **image object**, so a freshly baked canvas is a brand new GPU
|
|
92
|
+
texture with a new mip chain — rebuild one per stage and a title → play →
|
|
93
|
+
game-over cycle uploads four copies of the same pixels. Build each once and
|
|
94
|
+
share it from a module the scenes read from, the way the platformer example
|
|
95
|
+
builds its `TextureAtlas`.
|
|
96
|
+
|
|
97
|
+
*Where* you build it depends on what it costs. A gradient or a palette strip is
|
|
98
|
+
well under a millisecond: build it after `preload` resolves. A bake that takes
|
|
99
|
+
real time — procedural noise is the usual one: Earth Defender's sky, two fractal
|
|
100
|
+
simplex fields over a 512² tile, takes about 200ms on a desktop — belongs
|
|
101
|
+
**inside** the preload as an asset type of your
|
|
102
|
+
own ([Custom asset types](#custom-asset-types)). It then runs behind the loading
|
|
103
|
+
screen and counts on the progress bar. Built on stage entry instead, it lands on
|
|
104
|
+
the frame the scene is assembled, and with a transition running that frame is a
|
|
105
|
+
black screen.
|
|
95
106
|
|
|
96
107
|
**3. The engine's own shader programs — warmed for you, since 20.6.** The mesh
|
|
97
108
|
batcher compiles a program per *feature combination* (instance colours, instance
|
|
@@ -230,6 +241,83 @@ loader.getBinary("font-fnt");
|
|
|
230
241
|
Most engine classes take the asset **name** directly, so you often do not need
|
|
231
242
|
these — `new Sprite(x, y, { image: "tiles" })` resolves it for you.
|
|
232
243
|
|
|
244
|
+
## Custom asset types
|
|
245
|
+
|
|
246
|
+
`loader.setParser(type, parserFn)` teaches the loader a type of its own. An entry
|
|
247
|
+
of that type then sits in the preload list like any other: it is one step of the
|
|
248
|
+
progress bar, and `preload()` waits for it before `LOADER_COMPLETE`. Use it for a
|
|
249
|
+
file format melonJS does not know, and for anything you **generate** that takes
|
|
250
|
+
long enough to matter.
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
import { loader, Sprite } from "melonjs";
|
|
254
|
+
|
|
255
|
+
const skies = new Map();
|
|
256
|
+
|
|
257
|
+
// once, before preloading: registering a type twice logs a warning
|
|
258
|
+
loader.setParser("sky", (asset, onload, onerror) => {
|
|
259
|
+
bakeSky(asset.name) // your generator, returning a Promise
|
|
260
|
+
.then((canvas) => {
|
|
261
|
+
skies.set(asset.name, canvas);
|
|
262
|
+
onload(); // takes no value, see below
|
|
263
|
+
}, onerror);
|
|
264
|
+
return 1; // one asset, settling later
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
await loader.preload([
|
|
268
|
+
{ name: "nebula", type: "sky" }, // no src: nothing to download
|
|
269
|
+
// ...the rest of the manifest
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
// in the stage, later: already baked
|
|
273
|
+
const backdrop = new Sprite(0, 0, { image: skies.get("nebula") });
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The contract:
|
|
277
|
+
|
|
278
|
+
- **Call `onload()` or `onerror(error)`, exactly once,** now or later. Return `1`
|
|
279
|
+
when you will, `0` when there is nothing to load (the loader then moves on
|
|
280
|
+
without waiting).
|
|
281
|
+
- **The loader keeps nothing for a type it does not know.** `onload` takes no
|
|
282
|
+
value, and `loader.getImage` and friends will not find your result. Keep it
|
|
283
|
+
yourself, keyed by `asset.name`, and read it from there.
|
|
284
|
+
- **`name` and `type` are a complete entry.** `src` is only for a type that
|
|
285
|
+
fetches something. `data` is typed for the built-in uses (a string, or a
|
|
286
|
+
`{glsl, wgsl}` pair), so for a generator's settings, keep a table in your own
|
|
287
|
+
module keyed by name and let the entry carry only the name. That also keeps
|
|
288
|
+
the preload and the stages from disagreeing on a seed.
|
|
289
|
+
- **Base URLs:** a `src` is prefixed only by `setBaseURL(yourType, …)`. The `"*"`
|
|
290
|
+
wildcard does not cover custom types.
|
|
291
|
+
- **Do not hold the main thread for the whole bake.** A parser runs alongside
|
|
292
|
+
the downloads, and a 200ms synchronous loop freezes the loading screen and
|
|
293
|
+
stalls every other asset's progress for that long. Bake in slices of a few
|
|
294
|
+
milliseconds and yield between them, with `setTimeout`, not
|
|
295
|
+
`requestAnimationFrame`, which stops in a background tab and would hold the
|
|
296
|
+
whole preload with it. Skip the yield while the tab is hidden: a background
|
|
297
|
+
tab runs timers about once a second, which turns a bake of 25 slices into 25
|
|
298
|
+
seconds, and there is no loading screen on show to keep smooth:
|
|
299
|
+
|
|
300
|
+
```js
|
|
301
|
+
const bakeSky = async (name) => {
|
|
302
|
+
const { size, seed } = SKIES[name]; // your recipe table
|
|
303
|
+
const tile = startTile(size, seed); // set up the canvas and the noise
|
|
304
|
+
for (let y = 0; y < size; ) {
|
|
305
|
+
const start = performance.now();
|
|
306
|
+
while (y < size && performance.now() - start < 8) {
|
|
307
|
+
tile.fillRow(y++);
|
|
308
|
+
}
|
|
309
|
+
if (!document.hidden) {
|
|
310
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return tile.finish();
|
|
314
|
+
};
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The Earth Defender example does exactly this for its two nebula skies
|
|
318
|
+
(`fx/nebula.ts`): a `"nebula"` type, two name-only entries in its manifest, and
|
|
319
|
+
stages that ask for a sky by name and find it baked.
|
|
320
|
+
|
|
233
321
|
## Base URLs
|
|
234
322
|
|
|
235
323
|
Set a prefix per asset type rather than repeating it:
|
|
@@ -326,6 +414,9 @@ throws, the same trap as the removed `loader.onload` / `onProgress`.
|
|
|
326
414
|
| text renders in a fallback font | web font not preloaded as `"fontface"` |
|
|
327
415
|
| `BitmapText` renders nothing | the `.fnt` (as `"binary"`) or its image is missing |
|
|
328
416
|
| `TypeError` assigning `loader.onProgress` / `onload` | removed in 20.3; they were never writable — use the `LOADER_*` events |
|
|
417
|
+
| `load : unknown or invalid resource type` | a custom type in the manifest before its `loader.setParser` call |
|
|
418
|
+
| 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
|
+
| 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 |
|
|
329
420
|
|
|
330
421
|
## Related skills
|
|
331
422
|
|
|
@@ -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` |
|
|
@@ -249,8 +249,12 @@ game does not use the preloader at all**, which is the one way to miss it — se
|
|
|
249
249
|
pipelines that depend on the renderable (effects, blend modes, render targets)
|
|
250
250
|
are warmed when an effect is attached or a renderable is added to the world, as
|
|
251
251
|
long as that happens at least a frame before it is drawn: a pool built at stage
|
|
252
|
-
start is covered, an object created and drawn in the same frame is not.
|
|
253
|
-
|
|
252
|
+
start is covered, an object created and drawn in the same frame is not. Since
|
|
253
|
+
20.10.1 a state change also holds the new stage's first frame until what its
|
|
254
|
+
`onResetEvent` built is warmed, for at most 1.5s, so the opening frame no longer
|
|
255
|
+
compiles them itself: a few milliseconds once the browser has compiled them
|
|
256
|
+
before, longer on a first visit. None of this covers the rest of what a first
|
|
257
|
+
frame pays for, below.
|
|
254
258
|
|
|
255
259
|
How to tell them apart, all from the page:
|
|
256
260
|
|
|
@@ -369,6 +373,7 @@ In rough order for a typical 2D game:
|
|
|
369
373
|
| a freeze the first time a level shows, never again | shader variants linking on first draw — warm them behind the loading screen |
|
|
370
374
|
| 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 |
|
|
371
375
|
| 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 |
|
|
376
|
+
| 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`) |
|
|
372
377
|
|
|
373
378
|
## Related skills
|
|
374
379
|
|
|
@@ -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**
|
|
@@ -262,6 +297,10 @@ other correct ending.
|
|
|
262
297
|
- `floating = true` pins a renderable to the screen rather than the world, which
|
|
263
298
|
is what you want for HUD elements. It opts the object out of camera transforms
|
|
264
299
|
and out of ancestor position accumulation.
|
|
300
|
+
- `visibilityLayer` decides which cameras draw a renderable: a camera draws it
|
|
301
|
+
only if it shares a bit with the camera's `cullMask` (every camera, by
|
|
302
|
+
default). See `melonjs-camera-and-drawing` for keeping something off a
|
|
303
|
+
minimap.
|
|
265
304
|
|
|
266
305
|
## Prefer built-in features
|
|
267
306
|
|
|
@@ -45,6 +45,13 @@ of on a deferred tick, which is what you want when changing state right after a
|
|
|
45
45
|
preload completes. It is ignored when a transition is configured — a transition
|
|
46
46
|
always drives the switch from its own completion callback.
|
|
47
47
|
|
|
48
|
+
`onResetEvent` runs when the switch is made. The first frame, `STATE_CHANGE` and
|
|
49
|
+
a transition's reveal follow once the shaders the stage queued are ready, which
|
|
50
|
+
is a few milliseconds once the browser has compiled them before, and at most
|
|
51
|
+
1.5s on a first visit. So build the stage in `onResetEvent`: an object a
|
|
52
|
+
`STATE_CHANGE` listener adds is not waited for, and its first draw may still
|
|
53
|
+
compile what it needs.
|
|
54
|
+
|
|
48
55
|
`state.change` **throws** `Undefined Stage for state '<id>'` if nothing was
|
|
49
56
|
registered for that id, and `state.set` throws if the second argument is not a
|
|
50
57
|
`Stage` instance. Neither fails quietly.
|
|
@@ -59,6 +59,10 @@ hero.setCurrentAnimation("walk");
|
|
|
59
59
|
hero.setCurrentAnimation("die", "idle"); // chain to idle when it ends
|
|
60
60
|
hero.setCurrentAnimation("die", { loop: false }); // play once, hold the last frame
|
|
61
61
|
hero.setCurrentAnimation("die", { loop: false, onComplete: fn }); // …and call fn when it does
|
|
62
|
+
hero.setCurrentAnimation("use", { next: "idle", onComplete: fn }); // call fn, then chain to idle
|
|
63
|
+
hero.setCurrentAnimation("run", { onFrame: (frame) => { // each frame playback reaches
|
|
64
|
+
if (frame === 3 || frame === 8) footstep();
|
|
65
|
+
} });
|
|
62
66
|
|
|
63
67
|
if (hero.isCurrentAnimation("walk")) { /* … */ }
|
|
64
68
|
```
|
|
@@ -71,13 +75,25 @@ accepted — passing region names throws.
|
|
|
71
75
|
|
|
72
76
|
The second argument to `setCurrentAnimation` is polymorphic. A **string** chains
|
|
73
77
|
to that animation when this one ends. An **options object** takes
|
|
74
|
-
`{ loop, next, speed, onComplete }`, and only an explicit `loop: false`
|
|
75
|
-
looping. A **bare function** is the legacy form: it fires at *every* cycle end,
|
|
78
|
+
`{ loop, next, speed, onComplete, onFrame }`, and only an explicit `loop: false`
|
|
79
|
+
stops the looping. A **bare function** is the legacy form: it fires at *every* cycle end,
|
|
76
80
|
and only holds the last frame if it returns `false`. Omit the argument entirely
|
|
77
81
|
and the animation loops forever.
|
|
78
82
|
|
|
83
|
+
`onFrame(frame)` is called with frame 0 when the animation starts, then with each
|
|
84
|
+
frame playback reaches, so a footstep or a hit lands on its frame without polling
|
|
85
|
+
`getCurrentAnimationFrame()`. A long tick reports every frame it goes past. It is
|
|
86
|
+
not called for a frame set with `setAnimationFrame`, nor for the frame 0 a
|
|
87
|
+
play-once animation would wrap to. Keep the callback in a field when you call
|
|
88
|
+
`setCurrentAnimation` every frame: re-selecting keeps the first one anyway.
|
|
89
|
+
|
|
90
|
+
With both `next` and `onComplete`, the callback runs first and the chained
|
|
91
|
+
animation starts with none of its own, so a one-off action (a pickup, an
|
|
92
|
+
attack) needs no manual switch back to idle.
|
|
93
|
+
|
|
79
94
|
Re-selecting the animation that is already current is a no-op, so calling
|
|
80
|
-
`setCurrentAnimation` every frame is safe
|
|
95
|
+
`setCurrentAnimation` every frame is safe, and an `isCurrentAnimation` check
|
|
96
|
+
around it adds nothing. An unknown id throws.
|
|
81
97
|
|
|
82
98
|
## Texture atlases
|
|
83
99
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-tilemaps
|
|
3
|
-
description: "Use this skill for Tiled maps in melonJS — loading TMX/TSX levels, spawning entities from Tiled objects, collision shapes authored in Tiled, isometric and hexagonal maps, and image layers. Covers registerTiledObjectClass and the pool.register name contract, camera bounds, compressed maps needing the inflate plugin, and the level director API. Triggers on: Tiled, TMX, TSX, tilemap, level.load, level.load async, await level.load, tileset, ImageLayer, isometric, hexagonal, staggered, pool.register, Collectable, Trigger, object layer, collision layer, parallax, registerTiledObjectClass, registerTiledObjectFactory."
|
|
3
|
+
description: "Use this skill for Tiled maps in melonJS — loading TMX/TSX levels, spawning entities from Tiled objects, collision shapes authored in Tiled, isometric and hexagonal maps, and image layers. Covers registerTiledObjectClass and the pool.register name contract, camera bounds, compressed maps needing the inflate plugin, and the level director API. Triggers on: Tiled, TMX, TSX, tilemap, level.load, level.load async, await level.load, tileset, ImageLayer, isometric, hexagonal, staggered, pool.register, Collectable, Trigger, object layer, collision layer, parallax, registerTiledObjectClass, registerTiledObjectFactory, TMXObject, resolvedProperty, resolvedProperties, tile class, tile properties."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -72,7 +72,8 @@ object group in its own `Container` named after the group.
|
|
|
72
72
|
## Spawning entities from Tiled objects
|
|
73
73
|
|
|
74
74
|
Objects are matched against a factory registry, in this order: the object's
|
|
75
|
-
Tiled **class**, then its **name**, then
|
|
75
|
+
Tiled **class**, then its **name**, then, for a tile object with no class of
|
|
76
|
+
its own, its **tile's class**, then the structural fallbacks `"text"`,
|
|
76
77
|
`"tile"` and `"shape"`.
|
|
77
78
|
|
|
78
79
|
```js
|
|
@@ -122,6 +123,53 @@ registerTiledObjectClass("Trigger", MyTrigger); // whenever you like
|
|
|
122
123
|
The throw is for a clash between two of *your* classes under one name. For
|
|
123
124
|
that case `registerTiledObjectFactory` overwrites and only warns.
|
|
124
125
|
|
|
126
|
+
### Classes and properties set on tiles
|
|
127
|
+
|
|
128
|
+
In Tiled, a tile can carry a class and custom properties (tileset editor), and
|
|
129
|
+
every object placed from that tile shows them as its own. melonJS follows:
|
|
130
|
+
|
|
131
|
+
- **The tile's class.** A tile object with no class is built by the class
|
|
132
|
+
registered under its tile's class, so a designer sets "Chest" once on the
|
|
133
|
+
chest tile and every chest dropped in the map is a `Chest`. The object's
|
|
134
|
+
own class or registered name still comes first, and a tile classed like a
|
|
135
|
+
built-in (`Sprite`, `Trigger`, `shape`...) stays a plain tile.
|
|
136
|
+
- **The tile's properties.** Read them with `settings.resolvedProperty(name)`
|
|
137
|
+
or `settings.resolvedProperties()`, which merge the tile's properties under
|
|
138
|
+
the object's own, as Tiled's scripting API of the same names does. They are
|
|
139
|
+
**not** set on `settings` by name: only the object's own properties are,
|
|
140
|
+
as before, so a tile property named like a sprite setting never reaches a
|
|
141
|
+
plain tile sprite.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { registerTiledObjectClass, Sprite, type TMXObject } from "melonjs";
|
|
145
|
+
|
|
146
|
+
// the custom properties set on the object itself, by name
|
|
147
|
+
interface DoorSettings extends TMXObject {
|
|
148
|
+
locked?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
class Door extends Sprite {
|
|
152
|
+
locked: boolean;
|
|
153
|
+
facing: string;
|
|
154
|
+
constructor(x: number, y: number, settings: DoorSettings) {
|
|
155
|
+
super(x, y, { image: "door" });
|
|
156
|
+
this.locked = settings.locked ?? false;
|
|
157
|
+
// on the object, or else on the door tile in the tileset
|
|
158
|
+
this.facing = settings.resolvedProperty("facing") ?? "N";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
registerTiledObjectClass("Door", Door); // and give the door tile the class "Door"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`TMXObject` is the type of the `settings` a registered class (or factory)
|
|
165
|
+
receives: the object as read from the map, with `tile` (a `Tile`) on a tile
|
|
166
|
+
object. Extend it with the properties your class reads.
|
|
167
|
+
|
|
168
|
+
For a class that is not a `Sprite` itself (a `Container` holding the tile and
|
|
169
|
+
something else, say), `settings.tile.getRenderable()` returns the tile's sprite
|
|
170
|
+
as a plain tile object would get it: from a tileset image, an image collection
|
|
171
|
+
or an animated tile alike, with no region names to build by hand.
|
|
172
|
+
|
|
125
173
|
melonJS pre-registers `Renderable`, `Sprite`, `NineSliceSprite`, `Text`,
|
|
126
174
|
`BitmapText`, `ImageLayer`, `ColorLayer`, `Light2d`, `Collectable`, `Trigger`
|
|
127
175
|
and `Entity` as Tiled classes, so `Collectable` and `Trigger` work out of the
|
|
@@ -157,7 +205,9 @@ opposite of `level.load`'s.
|
|
|
157
205
|
|
|
158
206
|
Draw collision geometry in Tiled on an object layer and melonJS turns it into
|
|
159
207
|
world shapes automatically — rectangles, polygons, polylines, ellipses and
|
|
160
|
-
points all work.
|
|
208
|
+
points all work. On an isometric map they are projected with the map's tile
|
|
209
|
+
ratio; a circle stays an `Ellipse`, but any other ellipse comes out tilted and
|
|
210
|
+
becomes a `Polygon`, so code that checks a shape's type should expect either.
|
|
161
211
|
|
|
162
212
|
The contract is the **layer name**: melonJS lowercases it and checks whether it
|
|
163
213
|
*contains* `"collision"` (so `collision`, `Collision`, `world collision` all
|
|
@@ -171,7 +221,10 @@ An object that matches no factory and carries no text or tile data falls to the
|
|
|
171
221
|
geometry, which the world auto-registers with the active adapter on `addChild`.
|
|
172
222
|
|
|
173
223
|
For per-object physics, set the object's custom properties in Tiled; they arrive
|
|
174
|
-
on the settings object passed to your entity's constructor.
|
|
224
|
+
on the settings object passed to your entity's constructor. Properties set on
|
|
225
|
+
the object's tile are read with `settings.resolvedProperty(name)`. One is also
|
|
226
|
+
applied to the object the map creates: an `int` property named
|
|
227
|
+
`visibilityLayer` sets which cameras draw it (see `melonjs-camera-and-drawing`).
|
|
175
228
|
|
|
176
229
|
## Map orientations
|
|
177
230
|
|
|
@@ -183,10 +236,69 @@ Isometric maps need y-sorting so objects overlap correctly:
|
|
|
183
236
|
app.world.sortOn = "y"; // after level.load
|
|
184
237
|
```
|
|
185
238
|
|
|
239
|
+
The world then keeps its objects in order as they move (`autoSort`, on by
|
|
240
|
+
default): a character walking around passes in front of and behind the scenery
|
|
241
|
+
with no `sort()` call.
|
|
242
|
+
|
|
186
243
|
`sortOn` only accepts `"x"`, `"y"`, `"z"` (the default) or `"depth"` and throws
|
|
187
244
|
on anything else. `Vector2d`, `Vector3d`, their observable variants and `Polygon`
|
|
188
245
|
all carry `.toIso()` / `.to2d()` for converting between screen and map space.
|
|
189
246
|
|
|
247
|
+
### What the y-sort can and cannot sort
|
|
248
|
+
|
|
249
|
+
`sortOn = "y"` sorts **objects**, never tiles. A tile layer is a single
|
|
250
|
+
renderable drawn in one piece, so a character ends up in front of all of it or
|
|
251
|
+
behind all of it. Anything a character walks both in front of and behind
|
|
252
|
+
(walls, trees, furniture) has to be on an **object layer, as tile objects**,
|
|
253
|
+
and in the **same** object layer as the characters: each Tiled layer gets its
|
|
254
|
+
own `z`, and the y-sort compares `z` before `y`. Keep the floor on a tile layer
|
|
255
|
+
below them. This holds for any y-sorted map, isometric or a top-down
|
|
256
|
+
orthogonal one.
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
floor tile layer the ground, drawn first
|
|
260
|
+
dungeon object layer walls, props, chests, doors, the hero
|
|
261
|
+
collision object layer the shapes (not drawn)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Tile objects with no class are built for you: each one is a `Sprite` with its
|
|
265
|
+
default body, static, over its whole image box and typed `ENEMY_OBJECT`. A
|
|
266
|
+
character keeping the default `collisionMask` (every type) collides with all of
|
|
267
|
+
them, so when the scenery's real collision is drawn in the collision layer,
|
|
268
|
+
mask the character to it:
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
this.body.setCollisionMask(collision.types.WORLD_SHAPE);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Give the objects with behaviour a Tiled class and register it, as above.
|
|
275
|
+
|
|
276
|
+
The `y` it sorts on is `pos.y`, and Tiled objects default to a `(0, 0)` anchor,
|
|
277
|
+
so that is the **top** of each image. Sorting by the top only matches sorting by
|
|
278
|
+
the feet when every image is the same height and stands on the bottom of its
|
|
279
|
+
box, the usual layout of an isometric pack. A shorter image sorts in front of
|
|
280
|
+
things it stands behind. Either pad the art to one height, or line every object
|
|
281
|
+
up by the bottom of its image with `sortOffset`, which changes the order and
|
|
282
|
+
never the position:
|
|
283
|
+
|
|
284
|
+
```js
|
|
285
|
+
level.load("map1");
|
|
286
|
+
app.world.sortOn = "y";
|
|
287
|
+
for (const child of app.world.getChildren()) {
|
|
288
|
+
child.sortOffset = child.height; // sort by the bottom of each image
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
A character you create in code follows the same convention as the scenery: by
|
|
293
|
+
the top of a frame as tall as the scenery's (`anchorPoint` `(0, 0)`), or by its
|
|
294
|
+
feet when everything sorts by the bottom.
|
|
295
|
+
|
|
296
|
+
Two equal positions keep insertion order, and the one added **last** is drawn
|
|
297
|
+
**behind**: a decoration placed at its wall's exact position disappears. Give
|
|
298
|
+
it `sortOffset = 1`. A `Container` or a particle emitter is sorted by its top
|
|
299
|
+
left corner (an emitter's by the top of its spawn area); give it the offset to
|
|
300
|
+
the point it belongs with, such as `door.pos.y - dust.pos.y`.
|
|
301
|
+
|
|
190
302
|
## Image layers
|
|
191
303
|
|
|
192
304
|
`ImageLayer` gives parallax backgrounds with a ratio per axis, set in Tiled or in
|
|
@@ -241,7 +353,17 @@ unanimated layer into the offscreen-bake path instead.
|
|
|
241
353
|
| `No inflate function set` at parse time | compressed layers without the tiled-inflate plugin |
|
|
242
354
|
| collision shapes are not solid | the object layer's name does not contain `"collision"`, or the objects are named |
|
|
243
355
|
| isometric objects overlap wrongly | `world.sortOn` not set to `"y"` |
|
|
356
|
+
| a character is always in front of (or behind) every wall | the walls are on a tile layer, or on another object layer than the character: each layer has its own `z`, compared before `y` |
|
|
357
|
+
| a small prop draws over a character standing in front of it | images of different heights: the y-sort uses `pos.y`, the top of a Tiled object's image. Pad the art, or set `sortOffset = height` on every object to sort by the bottom |
|
|
358
|
+
| a decoration placed at the same position as a wall disappears behind it | a tie keeps insertion order and draws the later one behind: `sortOffset = 1` |
|
|
359
|
+
| particles (or a container) at the foot of something draw over a character standing in front of it | they sort by the top of their spawn area (or the container's top left): set `sortOffset` to line them up with what they belong to |
|
|
360
|
+
| a character is stopped short of walls and props placed as tile objects | it collides with their default bodies (static, `ENEMY_OBJECT`, the whole image box); mask it to `collision.types.WORLD_SHAPE` when the collision layer holds the real shapes |
|
|
361
|
+
| a property set on a tile is undefined on the object's `settings` | tile properties are not copied onto `settings`: read them with `settings.resolvedProperty(name)` |
|
|
362
|
+
| objects placed from a classed tile come out as plain sprites | that class is not registered, it is a built-in name, or the object has a class or a registered name of its own, which come first |
|
|
244
363
|
| missing tiles or a blank map | the `.tsx` or tileset image was not preloaded |
|
|
364
|
+
| a tileset's transparent color (`trans`) still shows (the console says it could not be applied) | the image cannot be read back: one from another origin needs CORS headers on the server and `loader.setOptions({ crossOrigin: "anonymous" })` (without them WebGL and WebGPU cannot draw it at all), and a compressed texture cannot be changed: export it with an alpha channel instead |
|
|
365
|
+
| changing tiles at runtime is slow on a pre-rendered layer | outside an orthogonal map whose tiles fit their cells, a changed tile re-bakes the whole layer before it next draws: keep layers you edit often off `preRender` |
|
|
366
|
+
| thin lines between tiles on the Canvas renderer, with smoothing on | filtering blends in the next tile of the tileset or its spacing, which a canvas cannot prevent: set `antiAlias: false`, or `preRender` the layer. WebGL and WebGPU keep each tile to itself |
|
|
245
367
|
| tilemap slow on some machines | layer fell back off the shader path — check the console warning for the reason |
|
|
246
368
|
|
|
247
369
|
## Related skills
|