melonjs 20.12.0 → 20.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/build/camera/camera2d.d.ts +2 -0
- package/build/camera/camera2d.d.ts.map +1 -1
- package/build/camera/effects/fade_effect.d.ts.map +1 -1
- package/build/geometries/ellipse.d.ts +3 -0
- package/build/geometries/ellipse.d.ts.map +1 -1
- package/build/geometries/line.d.ts.map +1 -1
- package/build/geometries/polygon.d.ts +2 -1
- package/build/geometries/polygon.d.ts.map +1 -1
- package/build/index.d.ts +13 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +5409 -1500
- 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/level/tiled/TMXLayer.d.ts +48 -14
- package/build/level/tiled/TMXLayer.d.ts.map +1 -1
- package/build/level/tiled/TMXTileMap.d.ts +1 -1
- package/build/level/tiled/TMXTileMap.d.ts.map +1 -1
- package/build/level/tiled/TMXTileset.d.ts.map +1 -1
- package/build/level/tiled/TMXUtils.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXHexagonalRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXIsometricRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXObliqueRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXOrthogonalRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXRenderer.d.ts +1 -1
- package/build/level/tiled/renderer/TMXRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXStaggeredRenderer.d.ts.map +1 -1
- package/build/loader/loader.d.ts +55 -11
- package/build/loader/loader.d.ts.map +1 -1
- package/build/math/color.d.ts.map +1 -1
- package/build/math/math.d.ts +6 -1
- package/build/math/math.d.ts.map +1 -1
- package/build/math/observableVector2d.d.ts +6 -6
- package/build/math/observableVector2d.d.ts.map +1 -1
- package/build/math/observableVector3d.d.ts +8 -8
- package/build/math/observableVector3d.d.ts.map +1 -1
- package/build/particles/emitter.d.ts +15 -3
- package/build/particles/emitter.d.ts.map +1 -1
- package/build/particles/particle.d.ts +6 -0
- package/build/particles/particle.d.ts.map +1 -1
- package/build/particles/settings.d.ts +22 -5
- package/build/particles/settings.d.ts.map +1 -1
- package/build/physics/builtin/body.d.ts +14 -4
- package/build/physics/builtin/body.d.ts.map +1 -1
- package/build/physics/builtin/raycast.d.ts.map +1 -1
- package/build/physics/world.d.ts.map +1 -1
- package/build/renderable/container.d.ts +40 -6
- package/build/renderable/container.d.ts.map +1 -1
- package/build/renderable/frameAnimation.d.ts.map +1 -1
- package/build/renderable/renderable.d.ts.map +1 -1
- package/build/renderable/sprite.d.ts +4 -2
- package/build/renderable/sprite.d.ts.map +1 -1
- package/build/renderable/text/bitmaptext.d.ts +12 -3
- package/build/renderable/text/bitmaptext.d.ts.map +1 -1
- package/build/renderable/text/text.d.ts +147 -4
- package/build/renderable/text/text.d.ts.map +1 -1
- package/build/renderable/text/textmetrics.d.ts +10 -2
- package/build/renderable/text/textmetrics.d.ts.map +1 -1
- package/build/renderable/text/textstyle.d.ts.map +1 -1
- package/build/renderable/ui/panelpaint.d.ts +5 -1
- package/build/renderable/ui/panelpaint.d.ts.map +1 -1
- package/build/renderable/ui/progressbar.d.ts +267 -95
- package/build/renderable/ui/progressbar.d.ts.map +1 -1
- package/build/renderable/ui/uibaseelement.d.ts +436 -50
- package/build/renderable/ui/uibaseelement.d.ts.map +1 -1
- package/build/renderable/ui/uicheckbox.d.ts +383 -0
- package/build/renderable/ui/uicheckbox.d.ts.map +1 -0
- package/build/renderable/ui/uifocus.d.ts +6 -0
- package/build/renderable/ui/uifocus.d.ts.map +1 -0
- package/build/renderable/ui/uilabel.d.ts +31 -0
- package/build/renderable/ui/uilabel.d.ts.map +1 -0
- package/build/renderable/ui/uipanel.d.ts +589 -116
- package/build/renderable/ui/uipanel.d.ts.map +1 -1
- package/build/renderable/ui/uiskin.d.ts +25 -0
- package/build/renderable/ui/uiskin.d.ts.map +1 -0
- package/build/renderable/ui/uislider.d.ts +518 -0
- package/build/renderable/ui/uislider.d.ts.map +1 -0
- package/build/renderable/ui/uispriteelement.d.ts +161 -85
- package/build/renderable/ui/uispriteelement.d.ts.map +1 -1
- package/build/renderable/ui/uitextbutton.d.ts +369 -63
- package/build/renderable/ui/uitextbutton.d.ts.map +1 -1
- package/build/state/stage.d.ts.map +1 -1
- package/build/system/pool.d.ts.map +1 -1
- package/build/system/save.d.ts +2 -1
- package/build/system/save.d.ts.map +1 -1
- package/build/tweens/tween.d.ts +5 -2
- package/build/tweens/tween.d.ts.map +1 -1
- package/build/utils/fetchdata.d.ts.map +1 -1
- package/build/video/canvas/canvas_renderer.d.ts +0 -5
- package/build/video/canvas/canvas_renderer.d.ts.map +1 -1
- package/build/video/renderer.d.ts +20 -4
- package/build/video/renderer.d.ts.map +1 -1
- package/build/video/renderstate.d.ts +26 -3
- package/build/video/renderstate.d.ts.map +1 -1
- package/build/video/utils/tessellation.d.ts +11 -0
- package/build/video/utils/tessellation.d.ts.map +1 -1
- package/build/video/webgl/webgl_renderer.d.ts +3 -6
- package/build/video/webgl/webgl_renderer.d.ts.map +1 -1
- package/build/video/webgpu/batchers/primitive_batcher.d.ts.map +1 -1
- package/build/video/webgpu/webgpu_renderer.d.ts +3 -6
- package/build/video/webgpu/webgpu_renderer.d.ts.map +1 -1
- package/package.json +1 -1
- package/skills/melonjs/SKILL.md +3 -3
- package/skills/melonjs-audio/SKILL.md +7 -5
- package/skills/melonjs-camera-and-drawing/SKILL.md +41 -1
- package/skills/melonjs-effects-and-shaders/SKILL.md +1 -0
- package/skills/melonjs-events/SKILL.md +3 -1
- package/skills/melonjs-getting-started/SKILL.md +1 -2
- package/skills/melonjs-input/SKILL.md +38 -2
- package/skills/melonjs-loading-assets/SKILL.md +53 -12
- package/skills/melonjs-math/SKILL.md +14 -3
- package/skills/melonjs-particles-and-trails/SKILL.md +72 -8
- package/skills/melonjs-physics/SKILL.md +53 -4
- package/skills/melonjs-plugins/SKILL.md +7 -2
- package/skills/melonjs-renderables/SKILL.md +7 -0
- package/skills/melonjs-scenes-and-state/SKILL.md +15 -0
- package/skills/melonjs-sprites-and-animation/SKILL.md +14 -0
- package/skills/melonjs-tilemaps/SKILL.md +96 -2
- package/skills/melonjs-ui-and-text/SKILL.md +560 -23
|
@@ -13,8 +13,6 @@ Every asset is `{ name, type, src }` plus type-specific extras:
|
|
|
13
13
|
```js
|
|
14
14
|
import { loader } from "melonjs";
|
|
15
15
|
|
|
16
|
-
loader.setOptions({ crossOrigin: "anonymous" }); // needed for remote assets
|
|
17
|
-
|
|
18
16
|
await loader.preload([
|
|
19
17
|
{ name: "tiles", type: "image", src: "data/img/tiles.png" },
|
|
20
18
|
{ name: "map1", type: "tmx", src: "data/map/map1.tmx" },
|
|
@@ -349,6 +347,20 @@ event.on(event.LOADER_COMPLETE, () => { /* … */ });
|
|
|
349
347
|
event.on(event.LOADER_ERROR, (res) => { /* … */ });
|
|
350
348
|
```
|
|
351
349
|
|
|
350
|
+
**Each `preload()` has only its own callback.** One given none calls nothing
|
|
351
|
+
on completion, so a later `await loader.preload(levelAssets)` does not re-run
|
|
352
|
+
the boot callback. Before 20.13 the loader kept the last callback it was given
|
|
353
|
+
and called it again for a preload that had none — usually a `state.change`
|
|
354
|
+
straight back to the first screen. `loader.reload(src)` after a failure
|
|
355
|
+
completes the preload that asset failed in, with that preload's callback, once
|
|
356
|
+
all of its assets are in.
|
|
357
|
+
|
|
358
|
+
**Progress is per preload:** `LOADER_PROGRESS` goes from that call's first
|
|
359
|
+
asset to `1` with its last, every time. A preload started while another is
|
|
360
|
+
still loading joins it, and the value covers both until everything in flight
|
|
361
|
+
has settled; the next preload starts from zero again. A failed asset keeps
|
|
362
|
+
its preload short of `1`, and only that preload.
|
|
363
|
+
|
|
352
364
|
## Unloading
|
|
353
365
|
|
|
354
366
|
```js
|
|
@@ -377,20 +389,45 @@ state.change(state.PLAY, true);
|
|
|
377
389
|
|
|
378
390
|
## Cross-origin and authenticated assets
|
|
379
391
|
|
|
380
|
-
|
|
381
|
-
|
|
392
|
+
**Nothing to set for the usual cases.** Every fetched asset (image, json,
|
|
393
|
+
binary, tmx, tsx, shader, gltf/glb, obj, mtl, aseprite, buffered audio) is
|
|
394
|
+
requested the same way:
|
|
395
|
+
|
|
396
|
+
- **On the game's own origin**, it carries the browser's cookies and HTTP
|
|
397
|
+
auth, as the page and its scripts do. A game behind a login or a session
|
|
398
|
+
cookie loads its own assets with no option.
|
|
399
|
+
- **On another origin** (a CDN), it is a CORS request: it loads when that
|
|
400
|
+
server sends `Access-Control-Allow-Origin`, and fails at load when not, with
|
|
401
|
+
an error naming the asset and the header it needs. No `crossOrigin` needed.
|
|
402
|
+
|
|
403
|
+
| asset on… | default | `withCredentials: true` |
|
|
404
|
+
|---|---|---|
|
|
405
|
+
| the game's own origin | loads, with its cookies | loads, with its cookies |
|
|
406
|
+
| another origin sending `Access-Control-Allow-Origin` | loads, no cookies | **blocked** if it answers `*`; needs the page's exact origin plus `Access-Control-Allow-Credentials: true` |
|
|
407
|
+
| another origin allowing credentials for the page | loads, no cookies | loads, with that origin's cookies |
|
|
408
|
+
| another origin with no CORS headers | fails at load, naming the asset | fails at load, naming the asset |
|
|
409
|
+
|
|
410
|
+
Two global settings cover the rest, for every asset type — there is no
|
|
411
|
+
per-asset or per-type spelling to remember:
|
|
382
412
|
|
|
383
413
|
```js
|
|
384
414
|
loader.setOptions({
|
|
385
|
-
|
|
386
|
-
|
|
415
|
+
withCredentials: true, // send cookies / auth to OTHER origins too
|
|
416
|
+
crossOrigin: "anonymous", // attribute of the <video>, <script> and glTF
|
|
417
|
+
// image elements the loader creates
|
|
387
418
|
});
|
|
388
419
|
```
|
|
389
420
|
|
|
390
|
-
`withCredentials` is
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
421
|
+
`withCredentials` is for assets on **another** origin behind a session cookie
|
|
422
|
+
or a login; that server must then allow credentials for the page's origin, or
|
|
423
|
+
the browser blocks the request. `crossOrigin: "use-credentials"` does the same
|
|
424
|
+
for fetched assets, and sets the attribute on the elements.
|
|
425
|
+
|
|
426
|
+
Before 20.13 both were needed far more often: assets on the game's own origin
|
|
427
|
+
went out with no cookies unless `withCredentials` was set (a 401 or 403 behind
|
|
428
|
+
a login), and an asset on another origin without `crossOrigin: "anonymous"`
|
|
429
|
+
was read as an empty response, failing later with "Unexpected end of input" or
|
|
430
|
+
"The source image could not be decoded" even when its server allowed it.
|
|
394
431
|
|
|
395
432
|
**Set them through `setOptions`.** `loader.crossOrigin` and
|
|
396
433
|
`loader.withCredentials` are read-only module bindings; assigning to them
|
|
@@ -408,13 +445,17 @@ throws, the same trap as the removed `loader.onload` / `onProgress`.
|
|
|
408
445
|
| audio 404s | `src` given as a file instead of a directory |
|
|
409
446
|
| audio fails, `preload()` rejects with "Failed loading resource" | `audio.init()` not called before preloading — the real message is replaced by the loader's |
|
|
410
447
|
| blank level, missing tiles | `.tsx` or tileset image not listed in the resources |
|
|
411
|
-
|
|
|
412
|
-
|
|
|
448
|
+
| `Failed to load "…" (Failed to fetch). An asset on another origin loads only if its server allows it…` | that origin sends no `Access-Control-Allow-Origin` header — or, with `withCredentials`, sends `*` or no `Access-Control-Allow-Credentials: true`. Fix the server's headers, or serve the asset from the game's own origin |
|
|
449
|
+
| an asset on another origin behind a login 401s / audio preload hangs | missing `loader.setOptions({ withCredentials: true })` — one switch, all asset types. Assets on the game's own origin need nothing (before 20.13 they needed it too) |
|
|
450
|
+
| a remote JSON fails with "Unexpected end of input", or an image with "The source image could not be decoded" | melonJS before 20.13, which read assets on another origin without CORS unless `crossOrigin: "anonymous"` was set |
|
|
413
451
|
| `TypeError` assigning `loader.withCredentials` / `crossOrigin` | read-only module bindings; use `loader.setOptions({ … })` |
|
|
414
452
|
| preload never finishes, blank screen, no error | a failing sound with `audio.setStopOnAudioError(false)` before 20.4 — it disabled audio *and* failed the preload |
|
|
415
453
|
| text renders in a fallback font | web font not preloaded as `"fontface"` |
|
|
416
454
|
| `BitmapText` renders nothing | the `.fnt` (as `"binary"`) or its image is missing |
|
|
417
455
|
| `TypeError` assigning `loader.onProgress` / `onload` | removed in 20.3; they were never writable — use the `LOADER_*` events |
|
|
456
|
+
| `await loader.preload(level)` sends the game back to its title screen, or runs the boot setup twice (before 20.13) | a preload with no callback re-ran the previous preload's callback; update, each preload now calls only its own |
|
|
457
|
+
| the loading bar starts part-full on a second preload, or never reaches the end after one asset failed (before 20.13) | the progress counters ran for the whole session; update, progress is per preload |
|
|
458
|
+
| `loader.reload(src)` of a custom asset type throws `unload : unknown or invalid resource type` (before 20.13) | update: a type registered with `setParser` can now be retried |
|
|
418
459
|
| `load : unknown or invalid resource type` | a custom type in the manifest before its `loader.setParser` call |
|
|
419
460
|
| black or frozen screen when a stage is first entered, the profile shows your own code (noise, a canvas bake) | a costly derived asset built on stage entry — bake it inside the preload as a [custom asset type](#custom-asset-types) |
|
|
420
461
|
| a custom asset preloads but the result is nowhere to be found | `onload` takes no value and the loader stores nothing for custom types — keep the result in your own module |
|
|
@@ -187,13 +187,22 @@ which is how a renderable knows its bounds went stale.
|
|
|
187
187
|
|
|
188
188
|
```js
|
|
189
189
|
v.setCallback(() => { ... });
|
|
190
|
-
v.setMuted(
|
|
190
|
+
v.setMuted(x, y, z); // write without firing it
|
|
191
|
+
v.setCallback(undefined); // stop observing
|
|
191
192
|
```
|
|
192
193
|
|
|
193
194
|
**`setMuted` is what you want when writing several components**, so a
|
|
194
195
|
half-updated vector does not fire a callback that reads the other half.
|
|
195
196
|
`set()` already does this internally: it disables the callback, writes all
|
|
196
|
-
three, fires once, and re-enables.
|
|
197
|
+
three, fires once, and re-enables. Every mutating method (`add`, `scale`,
|
|
198
|
+
`negateSelf`, `floorSelf`, `clampSelf`, `normalize`, `lerp`, …) goes through
|
|
199
|
+
`set()`, so it fires once and returns the observable vector itself. Before
|
|
200
|
+
20.13 `negateSelf`, `floorSelf`, `ceilSelf` and `clampSelf` (and the 3D
|
|
201
|
+
`normalize` and `perp`) fired once per component and returned the wrapped
|
|
202
|
+
plain vector instead.
|
|
203
|
+
|
|
204
|
+
`clone()` takes from a pool, and gets the callback you pass it or none: before
|
|
205
|
+
20.13 a recycled clone kept the callback of the vector released before it.
|
|
197
206
|
|
|
198
207
|
Note the observable `set(x = 0, y = 0, z = 0)` defaults EVERY argument, so
|
|
199
208
|
`set(10, 20)` zeroes z exactly as the plain vector does.
|
|
@@ -286,7 +295,7 @@ math.degToRad(deg); math.radToDeg(rad);
|
|
|
286
295
|
math.random(lo, hi); // integer
|
|
287
296
|
math.randomFloat(lo, hi);
|
|
288
297
|
math.weightedRandom(lo, hi); // integer, biased toward LO
|
|
289
|
-
math.round(n, decimals);
|
|
298
|
+
math.round(n, decimals); // nearest; a tie goes up, as Math.round: -2.5 → -2
|
|
290
299
|
math.pow(n); // n squared
|
|
291
300
|
math.DEG_TO_RAD math.RAD_TO_DEG math.TAU math.ETA math.EPSILON
|
|
292
301
|
```
|
|
@@ -317,6 +326,8 @@ and `Math.random()` every frame jitters instead of drifting.
|
|
|
317
326
|
| z unchanged after scaling | `scale(s)` leaves z alone; pass `scale(s, s, s)` |
|
|
318
327
|
| z became 0 for no reason | `set(x, y)` defaults z to 0 |
|
|
319
328
|
| the original vector changed | the method mutates; `copy()` into something first |
|
|
329
|
+
| a position callback fires twice and sees a half-updated vector, or `pos.floorSelf()` returns something that is not `pos` | before 20.13 the observable `negateSelf` / `floorSelf` / `ceilSelf` / `clampSelf` wrote component by component; upgrade |
|
|
330
|
+
| `math.round(-2.7)` is -2, or a large value comes back negative | before 20.13 `round` truncated toward zero through 32-bit integers; upgrade |
|
|
320
331
|
| memory climbs in a loop | `clone()` or `negate()`/`floor()`/`ceil()` per frame |
|
|
321
332
|
| two helper results are the same object | a shared scratch vector was returned |
|
|
322
333
|
| `.z` is a type error | `Renderable.pos` is declared `Vector2d` (#1510) |
|
|
@@ -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, tintOverLife, colour over life, color over life, flame, fire."
|
|
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, particle editor, tune particles, spawn depth, volume, 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
|
|
|
@@ -28,6 +28,50 @@ emitter.burstParticles(); // one shot
|
|
|
28
28
|
`duration` setting, which defaults to `Infinity` — a stream runs until
|
|
29
29
|
`stopStream()`. `burstParticles(n)` launches `n`, or `totalParticles`.
|
|
30
30
|
|
|
31
|
+
Without an `image`, particles are white squares of `textureSize` pixels (8 by
|
|
32
|
+
default), coloured by `tint`. Every such emitter draws the renderer's one
|
|
33
|
+
shared white pixel, stretched, so an emitter per explosion or a `reset()` on
|
|
34
|
+
every tweak costs no texture.
|
|
35
|
+
|
|
36
|
+
## Tuning an effect: the Particle Editor
|
|
37
|
+
|
|
38
|
+
Settings are easier to find by eye than by reasoning. The
|
|
39
|
+
[Particle Editor](https://melonjs.github.io/melonJS/examples/#/particle-editor)
|
|
40
|
+
runs one emitter live, with every setting on a slider, from a preset (fire,
|
|
41
|
+
smoke, rain, explosions, 3D bursts and rings…), a built-in texture or a sprite
|
|
42
|
+
(sheet) of your own, under a 2D camera or an orbiting 3D one. It writes out the
|
|
43
|
+
matching `new ParticleEmitter(...)` call, listing only what differs from the
|
|
44
|
+
defaults, and the same settings as JSON, in the emitter's own units, to load at
|
|
45
|
+
run time:
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
// preloaded with { name: "smoke-fx", type: "json", src: "data/fx/smoke.json" }
|
|
49
|
+
const effect = loader.getJSON("smoke-fx");
|
|
50
|
+
const emitter = new ParticleEmitter(x, y, {
|
|
51
|
+
...effect.settings,
|
|
52
|
+
// no `image` for the plain square: `getImage` would give null
|
|
53
|
+
...(effect.image ? { image: loader.getImage(effect.image) } : {}),
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Point a user there when they are tuning numbers by trial and error.
|
|
58
|
+
|
|
59
|
+
It covers every setting that changes how one emitter looks, but not:
|
|
60
|
+
|
|
61
|
+
- **Layered effects.** It shows one emitter at a time. A campfire (flames,
|
|
62
|
+
sparks, smoke) or an explosion (flash, debris, smoke) is several emitters
|
|
63
|
+
at the same spot: tune each in the editor, then add them side by side,
|
|
64
|
+
the one drawn on top last (or with a higher `z`).
|
|
65
|
+
- **Trails.** `Trail` and `Trail3d` are separate renderables; see below.
|
|
66
|
+
- **Code-only settings.** `onComplete`, `autoDestroyOnComplete`,
|
|
67
|
+
`accurateBounds`, and a `Container` as `referenceSpace` (it offers
|
|
68
|
+
`"local"` and `"world"`). `textureAdditive` is left out: `blendMode`
|
|
69
|
+
replaces it.
|
|
70
|
+
- **Changes at run time.** Calling `burstParticles()` when something
|
|
71
|
+
happens, or assigning `emitter.blendMode` while particles are alive, is
|
|
72
|
+
game code. Its "moving emitter" option previews the common case: an
|
|
73
|
+
emitter attached to something that moves.
|
|
74
|
+
|
|
31
75
|
## Sprite-sheet particles
|
|
32
76
|
|
|
33
77
|
Use `framewidth` and `frameheight` (the same names as on `Sprite`) to divide
|
|
@@ -107,15 +151,33 @@ fountain and wrong for an explosion: additive sprites piled on one point clip
|
|
|
107
151
|
all three channels there however well they are tinted, so a burst reads as one
|
|
108
152
|
bright flash rather than as debris.
|
|
109
153
|
|
|
110
|
-
Give it the size of whatever broke. The
|
|
111
|
-
|
|
112
|
-
to the right of its source:
|
|
154
|
+
Give it the size of whatever broke. The constructor centres the area on the
|
|
155
|
+
point it is given, so pass the centre of the source:
|
|
113
156
|
|
|
114
157
|
```js
|
|
115
158
|
const area = radius * 2;
|
|
116
|
-
const emitter = new ParticleEmitter(x
|
|
117
|
-
|
|
118
|
-
|
|
159
|
+
const emitter = new ParticleEmitter(x, y, { width: area, height: area });
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
After that, `pos` is the area's top-left corner, as on any renderable. To move
|
|
163
|
+
the emitter to a new source later, offset by half, or the burst sits down and
|
|
164
|
+
to the right of it:
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
emitter.pos.set(x - area / 2, y - area / 2); // re-aimed at (x, y)
|
|
168
|
+
emitter.burstParticles();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Under a `Camera3d`, `depth` (20.13) gives the area its third side: each
|
|
172
|
+
particle is born at a random depth within it, centered on the emitter's own
|
|
173
|
+
depth, unlike `width` and `height`. Without it every particle starts on one
|
|
174
|
+
plane facing the camera, so rain or dust seen from the side is a flat sheet. A
|
|
175
|
+
2D camera does not show depth, though with an order-dependent blend the
|
|
176
|
+
particles are still sorted by it. It is the spawn box's size, not the
|
|
177
|
+
emitter's `depth` property, which is where the emitter sits along Z.
|
|
178
|
+
|
|
179
|
+
```js
|
|
180
|
+
new ParticleEmitter(x, y, { width: 600, height: 20, depth: 400 }); // a volume of rain
|
|
119
181
|
```
|
|
120
182
|
|
|
121
183
|
## Particles in 3D
|
|
@@ -321,6 +383,7 @@ answer when you want discrete puffs rather than a continuous ribbon.
|
|
|
321
383
|
|---|---|
|
|
322
384
|
| trail looks flat under a `Camera3d` | `Trail` is 2D; use `Trail3d` |
|
|
323
385
|
| 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`) |
|
|
386
|
+
| a camera that draws only the emitter's layer shows no particles | before 20.13 particles stayed on layer 1 whatever their emitter was on; update |
|
|
324
387
|
| `Trail3d` draws nothing, with a console warning | it needs a `Camera3d`; the app's `cameraClass` is still the 2D default |
|
|
325
388
|
| `trail.tint` does nothing | `Trail` cannot be tinted at all; `Trail3d` can |
|
|
326
389
|
| assigning `maxPoints` throws | `Trail3d` sizes its geometry once; pass `length` to the constructor |
|
|
@@ -335,7 +398,8 @@ answer when you want discrete puffs rather than a continuous ribbon.
|
|
|
335
398
|
| frame rate drops with many effects | an emitter per event instead of one re-aimed emitter |
|
|
336
399
|
| a 3D burst is invisible under a `Camera3d` | before 20.8 a particle was culled at twice its depth; update |
|
|
337
400
|
| the whole burst launches from one pixel | `width`/`height` are the spawn area and default to `1` |
|
|
338
|
-
| the burst sits down-right of its source | the
|
|
401
|
+
| the burst sits down-right of its source | the emitter was moved by setting `pos` to the source: `pos` is the area's top-left corner, so offset by half (only the constructor centres it) |
|
|
402
|
+
| the burst sits up-left of its source | the constructor was given the top-left corner: it centres the area on (x, y), so pass the centre |
|
|
339
403
|
| the middle of a burst goes white | additive sprites overlapping — `textureAdditive: false`, or spread them |
|
|
340
404
|
| debris chunks look like squares | the default sprite is a filled square; pass your own `image` |
|
|
341
405
|
| a ring tilts with the camera instead of staying on a surface | the launch axis is in WORLD axes, not the camera basis |
|
|
@@ -152,6 +152,11 @@ class Player extends Sprite {
|
|
|
152
152
|
The engine forwards it to the active adapter when the renderable is added with
|
|
153
153
|
`Container.addChild`, and only if that container is already attached to the root
|
|
154
154
|
world; the broadphase picks the renderable up on the next `world.update()`.
|
|
155
|
+
Nesting is fine: adding a container registers the body of every renderable
|
|
156
|
+
below it, at any depth, and removing it — destroyed, pooled or kept alive —
|
|
157
|
+
takes them all out again, so moving a whole group with `other.addChild(group)`
|
|
158
|
+
keeps each body simulated, once. A persistent container's children keep their
|
|
159
|
+
bodies through `world.reset()` (a state change or `level.load`).
|
|
155
160
|
Calling `adapter.addBody()` yourself registers the body but leaves the
|
|
156
161
|
renderable out of the scene graph, so it integrates but never collides.
|
|
157
162
|
|
|
@@ -270,6 +275,13 @@ samples it down and keeps the extent. Anything the 2D backends cannot express
|
|
|
270
275
|
is refused loudly on both, rather than leaving a body with no collision
|
|
271
276
|
geometry that silently never collides.
|
|
272
277
|
|
|
278
|
+
A polygon may be wound either way: its edge normals are oriented outward from
|
|
279
|
+
its signed area. Before 20.13 they pointed inward on a counter-clockwise
|
|
280
|
+
polygon — a Tiled polygon drawn that way, anything mirrored with
|
|
281
|
+
`scale(-1, 1)` or a `Rect` of negative width — and the built-in circle test
|
|
282
|
+
then collided a circle with it from tens of pixels away, as soon as their
|
|
283
|
+
bounding boxes met. Polygon against polygon was never affected.
|
|
284
|
+
|
|
273
285
|
Whatever a backend really simulates is what `getBodyShapes()` reports, so the
|
|
274
286
|
debug panel is the fastest way to see an approximation for what it is.
|
|
275
287
|
|
|
@@ -533,6 +545,15 @@ and only active under a 3D broadphase — i.e. `world.sortOn === "depth"`, which
|
|
|
533
545
|
`world.adapter.capabilities.raycasts3d` before calling either. `raycast` itself
|
|
534
546
|
is capability-gated by `capabilities.raycasts`.
|
|
535
547
|
|
|
548
|
+
On the built-in adapter a 2D ray measures each shape the way the narrowphase
|
|
549
|
+
measures it against a planar shape: polygons, `Rect`, `RoundRect` and `Line`
|
|
550
|
+
by their edges, an `Ellipse` with its rotation, a `Box3d` by its XY footprint
|
|
551
|
+
and a `Sphere` as a circle of its radius (a 2D ray is unbounded along Z). A
|
|
552
|
+
`Point` has no area to cross and is skipped, as is an unknown shape type,
|
|
553
|
+
with a one-time console warning. Before 20.13 a body carrying a `Point`,
|
|
554
|
+
`Sphere` or `Box3d` made every ray that reached it throw, and a rotated
|
|
555
|
+
ellipse was hit where it would have been unrotated.
|
|
556
|
+
|
|
536
557
|
The legacy `collision.rayCast(line, result)` is a different API: it takes a
|
|
537
558
|
`Line`, returns an **array** of every intersecting renderable, and only exists
|
|
538
559
|
on the built-in path.
|
|
@@ -629,8 +650,9 @@ group.resize(width, height);
|
|
|
629
650
|
```
|
|
630
651
|
|
|
631
652
|
`enableChildBoundsUpdate` also gives it one, but it re-measures the whole child
|
|
632
|
-
list on every update
|
|
633
|
-
|
|
653
|
+
list on every update (after the children have moved, and before each culling
|
|
654
|
+
check while it is off screen) — reach for it only when the extent genuinely IS
|
|
655
|
+
the members' union and moves with them (a flock, a squad), not to give a fixed
|
|
634
656
|
group a size.
|
|
635
657
|
|
|
636
658
|
## Built-in world quirks
|
|
@@ -656,13 +678,25 @@ real rigid-body engine:
|
|
|
656
678
|
- **`applyForce(x, y)` is linear only** unless you pass the optional
|
|
657
679
|
`(pointX, pointY)` application point, which generates a torque
|
|
658
680
|
`τ = r × F` into `body.angularVelocity` via `body.pseudoInertia`.
|
|
659
|
-
`applyTorque(τ)` is the direct form.
|
|
681
|
+
`applyTorque(τ)` is the direct form. The point is in **world** coordinates,
|
|
682
|
+
as on matter and planck, and the lever arm is measured from the centre of
|
|
683
|
+
the shapes where they collide: the renderable's drawn frame (so the anchor
|
|
684
|
+
counts) plus any parent container's position. `renderable.getBounds()` is
|
|
685
|
+
in that space, so `b.centerX, b.top` is the top centre.
|
|
660
686
|
- **Force accumulators reset at end-of-step** — for every body, including
|
|
661
687
|
static, paused and off-screen ones — so forces must be applied every frame to
|
|
662
688
|
persist.
|
|
663
689
|
- **`isGrounded` is flag-based, not contact-based** — the adapter returns
|
|
664
690
|
`!body.falling && !body.jumping`, flags updated by the last resolved
|
|
665
|
-
collision, not a live contact test.
|
|
691
|
+
collision, not a live contact test. The DIRECTION of the push-out decides
|
|
692
|
+
them: pushed up out of a floor while not rising is a landing, pushed down
|
|
693
|
+
by a ceiling while rising (or `jumping`) ends the jump and falls, and a
|
|
694
|
+
side contact, a body touched from above while standing, or a body with
|
|
695
|
+
`gravityScale` 0 leaves them alone. Before 20.13 a magnitude test decided
|
|
696
|
+
instead, so anything that sank a pixel or more per step — gravity above
|
|
697
|
+
about `1.02`, mass above about `1.03`, `timer.tick > 1` at a low frame
|
|
698
|
+
rate, a floor moving up under it — stayed `falling` while sitting flush on
|
|
699
|
+
the floor, and could never jump.
|
|
666
700
|
- **`def.density` maps 1:1 to `body.mass`** (default `1`), and `def.friction` is
|
|
667
701
|
**ignored** entirely. `body.friction` is a per-axis `Vector2d` of per-step
|
|
668
702
|
velocity damping fed from `def.frictionAir`, not a surface coefficient.
|
|
@@ -694,6 +728,12 @@ shape that is close enough at every angle (a circle, or a square for
|
|
|
694
728
|
90° steps), or rotate the shapes yourself with `body.rotate(angle)` — which
|
|
695
729
|
does rotate them, unlike `setAngle()`.
|
|
696
730
|
|
|
731
|
+
The visual turn pivots on the centre of the body's shapes, where they are
|
|
732
|
+
drawn and collide, whatever the renderable's `anchorPoint`. Before 20.13 it
|
|
733
|
+
ignored the anchor, so a centre-anchored renderable turned about its
|
|
734
|
+
bottom-right corner and swung away from where it stood; only a `(0, 0)`
|
|
735
|
+
anchor turned in place.
|
|
736
|
+
|
|
697
737
|
`body.rotate()` applies a **delta** and does not update `body.angle`; the two
|
|
698
738
|
are unlinked, so a body driven by `rotate()` still reports an angle of `0`.
|
|
699
739
|
Track it yourself if you need to read it back. Before 20.7 it also threw on a
|
|
@@ -775,12 +815,21 @@ use them. `adapter.capabilities` (`constraints`,
|
|
|
775
815
|
| a small shape sits at the sprite's corner | a shape's offset is measured from the drawn frame's TOP-LEFT; `new Sphere(0, 0, 0, r)` is the corner, `(width / 2, height / 2)` is the centre |
|
|
776
816
|
| a rotated renderable reports an unrotated bounding box | `autoTransform: false` opts `updateBounds()` out of the transform |
|
|
777
817
|
| a body in a pile sinks into the floor (built-in) | fixed in 20.7 for anything with an immovable side; bodies pinned only by other DYNAMIC bodies still overlap by a pixel or two, which is what planck/matter are for |
|
|
818
|
+
| `applyForce` with a point spins a body pushed through its centre, or tips it the wrong way (built-in) | before 20.13 the point was measured against the body's LOCAL bounds, so only a body at the world origin got the right torque; upgrade, and drop any `pos` you subtracted to compensate |
|
|
778
819
|
| forces do nothing after switching adapter | magnitude units differ — re-tune, don't reuse numbers |
|
|
779
820
|
| `body.position` disagrees with `renderable.pos` on matter | matter stores the centroid, melonJS the top-left — the adapter offsets between them |
|
|
821
|
+
| a `bodyDef` two containers down never falls nor collides (`body` stays `undefined`) | before 20.13 only the added container's own children were registered; upgrade |
|
|
822
|
+
| `other.addChild(group)` throws "renderable is already adapter-managed" | before 20.13 a group detached with keepalive (which moving it does) left its children's bodies registered, still simulated off-stage; upgrade |
|
|
823
|
+
| the children of a persistent container stop falling and colliding after a state change or `level.load` | before 20.13 `world.reset()` kept only the bodies whose own renderable was persistent; upgrade, or flag each child `isPersistent` |
|
|
780
824
|
| `bodyDef.shapes: "…"` throws "is not loaded" | the name must match the `name` the JSON was preloaded under, not its filename |
|
|
781
825
|
| everything falls far too fast or too slow after swapping backends | gravity is a different unit per backend; see the table above |
|
|
782
826
|
| debris lands holding its spawn angle and never tumbles | `fixedRotation: true`, or matter-adapter < 1.4.0 / planck-adapter < 1.5.0 |
|
|
783
827
|
| a rotated sprite collides as if upright | the built-in solver rotates visually only — `body.angle` never rotates the shapes. Use planck/matter, or `body.rotate(angle)` |
|
|
828
|
+
| `setAngle` swings a sprite away from where it stood (built-in) | before 20.13 the pivot ignored `anchorPoint`, so a centre-anchored renderable turned about its bottom-right corner; upgrade |
|
|
829
|
+
| `isGrounded()` stays `false` on the floor, so the player cannot jump (built-in) | before 20.13 a body sinking 1px or more per step (gravity or mass above ~1.02, `timer.tick > 1`, a rising floor) never landed; upgrade |
|
|
830
|
+
| a circle body is shoved by a polygon it is nowhere near (built-in) | before 20.13 a counter-clockwise polygon (Tiled, mirrored with `scale(-1, 1)`) had inward edge normals; upgrade |
|
|
831
|
+
| `raycast` throws "reading 'x'" / "reading 'length'" (built-in) | before 20.13 a body carrying a `Point`, `Sphere` or `Box3d` broke the ray walk; upgrade |
|
|
832
|
+
| a ray passes through a rotated ellipse, or stops short of it (built-in) | before 20.13 the raycast ignored `Ellipse#angle`; upgrade |
|
|
784
833
|
| the debug hitbox does not follow a spinning body | pre-1.2.1 matter-adapter / pre-1.3.1 planck-adapter returned the authored shapes rather than the rotated ones |
|
|
785
834
|
| a Tiled polyline becomes a solid box on matter | matter cannot build a zero-area polygon; the adapter falls back to its AABB |
|
|
786
835
|
|
|
@@ -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, 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."
|
|
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, floating joystick, floating button, CapacitorPlugin, adapter, custom plugin, plugin version mismatch, already registered."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -105,7 +105,12 @@ plugin.register(GamepadPlugin, "gamepad", {
|
|
|
105
105
|
|
|
106
106
|
Shown on touch screens by default (`enabled`), hidden while a real pad is in
|
|
107
107
|
use. The left stick also presses the D-pad; a button can press a key instead
|
|
108
|
-
of a pad button.
|
|
108
|
+
of a pad button. Since 1.1 a stick or button can be `floating` (shows up
|
|
109
|
+
under the finger anywhere in its `area`), `hideAtRest` (drawn only while
|
|
110
|
+
held), and a stick can take a `tap` (a quick touch that does not steer presses
|
|
111
|
+
a key or button) — so the mobile layouts, two thumbs or one thumb with
|
|
112
|
+
tap-to-act, need no code of their own. See `melonjs-input` for the bindings,
|
|
113
|
+
the layouts and the overlap rules.
|
|
109
114
|
|
|
110
115
|
### `@melonjs/capacitor-plugin`
|
|
111
116
|
|
|
@@ -291,6 +291,12 @@ other correct ending.
|
|
|
291
291
|
`obj.alwaysUpdate === true`. An off-screen object with the default
|
|
292
292
|
`alwaysUpdate = false` does not tick at all.
|
|
293
293
|
|
|
294
|
+
A `Container` is culled by its own bounds, which do not follow its children
|
|
295
|
+
unless it has a size of its own (`resize`) or `enableChildBoundsUpdate` is on.
|
|
296
|
+
With the flag, the bounds are the children's union, re-measured on every update
|
|
297
|
+
and, while the group is off screen, before each culling check — so a squad that
|
|
298
|
+
walks back into view is drawn again.
|
|
299
|
+
|
|
294
300
|
## Anchors and floating
|
|
295
301
|
|
|
296
302
|
- `anchorPoint` defaults to centre `(0.5, 0.5)` on `Renderable`, but `Container`
|
|
@@ -322,6 +328,7 @@ Hand-rolled equivalents miss the batching and the multi-backend support.
|
|
|
322
328
|
| effect silently does nothing | running on the Canvas renderer |
|
|
323
329
|
| object appears frozen while its state changes | `update()` not returning `true` |
|
|
324
330
|
| off-screen object never ticks | `update()` is gated on `inViewport` — set `alwaysUpdate = true` |
|
|
331
|
+
| a group of moving children vanishes once they leave where they started | a sizeless container's bounds did not follow its children: give it a size, or `enableChildBoundsUpdate` (which only followed them from 20.13.0) |
|
|
325
332
|
| custom draw offset by half the size | centred `anchorPoint` default not zeroed |
|
|
326
333
|
| spatial query never finds an object | `isKinematic` left `true` — not in the broadphase |
|
|
327
334
|
| `z` unexpectedly 0 after a `set` | `Vector3d.set(x, y)` defaults `z` to 0 — `setPosition(x, y)` on the renderable leaves the depth alone |
|
|
@@ -107,6 +107,9 @@ this.HUD.floating = true; // screen coordinates, not world
|
|
|
107
107
|
app.world.addChild(this.HUD, 100); // explicit z — see melonjs-renderables
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
Everything inside a persistent container is kept with it, physics bodies
|
|
111
|
+
included (before 20.13 the children's bodies were dropped by the reset).
|
|
112
|
+
|
|
110
113
|
## Transitions and pausing
|
|
111
114
|
|
|
112
115
|
```js
|
|
@@ -314,6 +317,14 @@ Four traps:
|
|
|
314
317
|
- **Stop a tween before destroying its target**, or `onUpdate` fires against a
|
|
315
318
|
dead renderable.
|
|
316
319
|
|
|
320
|
+
**A target can be an array: a path.** `to({ x: [200, 120, 300] })` runs from
|
|
321
|
+
the current value through each entry in turn, and
|
|
322
|
+
`interpolation: Tween.Interpolation.CatmullRom` smooths the corners. Each
|
|
323
|
+
`start()` builds the path afresh from the current value, so a replay or a chain
|
|
324
|
+
that comes back round runs the same path, and with `yoyo` the return leg plays
|
|
325
|
+
it back the way it came. Before 20.13 a restart ran a longer path each time and
|
|
326
|
+
a `yoyo` path turned the property into `NaN`.
|
|
327
|
+
|
|
317
328
|
**To animate something that is not a property, tween a holder and push the
|
|
318
329
|
value in `onUpdate`.** A shader uniform and a light's intensity are both set
|
|
319
330
|
through a method, so there is nothing for `to()` to interpolate directly. The
|
|
@@ -387,6 +398,7 @@ import { save } from "melonjs";
|
|
|
387
398
|
|
|
388
399
|
save.add({ hiscore: 0 }); // once; idempotent, loads any stored value
|
|
389
400
|
save.hiscore = 1200; // plain assignment writes to localStorage
|
|
401
|
+
save.remove("hiscore"); // gone from localStorage; set it again to save it again
|
|
390
402
|
```
|
|
391
403
|
|
|
392
404
|
**In TypeScript, keep what `add()` returns.** The namespace carries a
|
|
@@ -416,9 +428,12 @@ both.
|
|
|
416
428
|
| the world freezes on blur but the game keeps advancing | simulation lives in `Stage.update`, which the pause does not gate — only `Container.update` checks `state.isPaused()` |
|
|
417
429
|
| the player character is left behind and scrolls off after a lost window | same cause: the stage advanced the camera while its world children were paused |
|
|
418
430
|
| a tween does nothing | `.start()` never called |
|
|
431
|
+
| an array (path) tween wanders further on each replay, or goes `NaN` with `yoyo` | before 20.13 `start()` re-prepended the start value to the path and `yoyo` swapped the array into the start value; upgrade |
|
|
432
|
+
| a repeating or `yoyo` tween runs a single cycle when started again (a pulse pulses once, a yoyo stays at the far end) | before 20.13 `start()` did not restore the `repeat` count the first run had used up; upgrade |
|
|
419
433
|
| effect stops during a hit-stop | tween needs `updateWhenPaused = true` |
|
|
420
434
|
| crash after a stage switch | callback ran during teardown — guard with `state.isCurrent` |
|
|
421
435
|
| HUD scrolls away with the camera | missing `floating = true` |
|
|
436
|
+
| a `save` key set again after `save.remove()` reads back fine, then is gone (or back at its default) after a reload (before 20.13) | the write skipped the list of keys restored at boot; update |
|
|
422
437
|
|
|
423
438
|
## Related skills
|
|
424
439
|
|
|
@@ -95,6 +95,15 @@ Re-selecting the animation that is already current is a no-op, so calling
|
|
|
95
95
|
`setCurrentAnimation` every frame is safe, and an `isCurrentAnimation` check
|
|
96
96
|
around it adds nothing. An unknown id throws.
|
|
97
97
|
|
|
98
|
+
`addAnimation` with an id that exists replaces it, which is how you change an
|
|
99
|
+
animation's frames or speed (`hero.addAnimation("walk", [4, 5, 6, 7], 200)`).
|
|
100
|
+
Redefining the one that is playing takes effect at once: the playing frame is
|
|
101
|
+
kept if the new strip still has it, playback restarts on frame 0 if not, a
|
|
102
|
+
finished `loop: false` run holds the new last frame, and the
|
|
103
|
+
`setCurrentAnimation` options stay. Before 20.13 it kept the old frame count, so
|
|
104
|
+
a shorter strip threw on the next `update()` and a longer one never reached its
|
|
105
|
+
new frames.
|
|
106
|
+
|
|
98
107
|
## Texture atlases
|
|
99
108
|
|
|
100
109
|
`TextureAtlas` reads several formats:
|
|
@@ -125,6 +134,9 @@ sprite.flipX(true); // mirror horizontally
|
|
|
125
134
|
sprite.flipY(true);
|
|
126
135
|
sprite.blendMode = "additive"; // see the effects skill for the full list
|
|
127
136
|
sprite.scale(2, 2); // ← multiplicative, see below
|
|
137
|
+
sprite.flicker(1000, function () { // blink for a second, then…
|
|
138
|
+
this.ancestor.removeChild(this); // …`this` is the sprite; the flicker has ended
|
|
139
|
+
});
|
|
128
140
|
```
|
|
129
141
|
|
|
130
142
|
**`scale()` is multiplicative, not absolute.** Calling it every frame compounds.
|
|
@@ -185,6 +197,8 @@ Two consequences:
|
|
|
185
197
|
| event handlers leak on pooled objects | bound in the constructor rather than `onActivateEvent` |
|
|
186
198
|
| animation never advances | no `framewidth`/`frameheight`, so the sheet is one frame |
|
|
187
199
|
| animation plays once and stops | `{ loop: false }`, or a callback that returned `false` |
|
|
200
|
+
| `TypeError` reading `offset` or `delay` in `update()` after re-adding the playing animation with fewer frames, or a longer one never shows its new frames | before 20.13 redefining the current animation kept its old frame count; upgrade |
|
|
201
|
+
| `this` is wrong in a `flicker(ms, callback)` callback, or a flicker started from it stops at once | before 20.13.0 the callback ran on an internal object and the flicker was stopped after it; now `this` is the sprite and the flicker has already ended |
|
|
188
202
|
| sprites do not batch | more distinct textures in one frame than `renderer.maxTextures` |
|
|
189
203
|
| nine-slice corners stretch anyway | `insetX`/`insetY` instead of `insetx`/`insety` |
|
|
190
204
|
|