melonjs 20.12.0 → 20.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -22
- package/build/application/application.d.ts +6 -1
- package/build/application/application.d.ts.map +1 -1
- package/build/application/header.d.ts.map +1 -1
- package/build/application/resize.d.ts.map +1 -1
- package/build/application/settings.d.ts +13 -8
- package/build/application/settings.d.ts.map +1 -1
- package/build/audio/audio.d.ts +8 -4
- package/build/audio/audio.d.ts.map +1 -1
- package/build/audio/backend/engine.d.ts.map +1 -1
- package/build/audio/playback.d.ts +2 -2
- package/build/audio/playback.d.ts.map +1 -1
- package/build/audio/spatial.d.ts.map +1 -1
- package/build/audio/types.d.ts +15 -5
- package/build/audio/types.d.ts.map +1 -1
- package/build/camera/camera2d.d.ts +12 -9
- package/build/camera/camera2d.d.ts.map +1 -1
- package/build/camera/camera3d.d.ts +11 -8
- package/build/camera/camera3d.d.ts.map +1 -1
- package/build/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/path2d.d.ts +9 -8
- package/build/geometries/path2d.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 +14 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +50215 -45051
- package/build/index.js.map +4 -4
- package/build/input/gamepad.d.ts.map +1 -1
- package/build/input/keyboard.d.ts.map +1 -1
- package/build/input/pointerevent.d.ts.map +1 -1
- package/build/level/gltf/GLTFModel.d.ts.map +1 -1
- package/build/level/level.d.ts.map +1 -1
- package/build/level/tiled/TMXLayer.d.ts +50 -15
- package/build/level/tiled/TMXLayer.d.ts.map +1 -1
- package/build/level/tiled/TMXTileMap.d.ts +14 -4
- 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 +1 -0
- 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 +7 -3
- package/build/level/tiled/renderer/TMXRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXStaggeredRenderer.d.ts.map +1 -1
- package/build/lighting/light3d.d.ts +55 -0
- package/build/lighting/light3d.d.ts.map +1 -1
- package/build/loader/cache.d.ts +146 -97
- package/build/loader/cache.d.ts.map +1 -1
- package/build/loader/internal-types.d.ts +69 -0
- package/build/loader/internal-types.d.ts.map +1 -0
- package/build/loader/loader.d.ts +589 -253
- package/build/loader/loader.d.ts.map +1 -1
- package/build/loader/parsers/audio.d.ts.map +1 -1
- package/build/loader/parsers/fontface.d.ts.map +1 -1
- package/build/loader/parsers/gltf.d.ts.map +1 -1
- package/build/loader/parsers/tmx.d.ts.map +1 -1
- package/build/math/color.d.ts +4 -0
- package/build/math/color.d.ts.map +1 -1
- package/build/math/math.d.ts +6 -1
- package/build/math/math.d.ts.map +1 -1
- package/build/math/noise.d.ts +16 -16
- package/build/math/noise.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/adapter.d.ts +64 -25
- package/build/physics/adapter.d.ts.map +1 -1
- package/build/physics/builtin/body.d.ts +23 -4
- package/build/physics/builtin/body.d.ts.map +1 -1
- package/build/physics/builtin/builtin-adapter.d.ts +25 -3
- package/build/physics/builtin/builtin-adapter.d.ts.map +1 -1
- package/build/physics/builtin/detector.d.ts.map +1 -1
- package/build/physics/builtin/raycast.d.ts.map +1 -1
- package/build/physics/world.d.ts +40 -10
- package/build/physics/world.d.ts.map +1 -1
- package/build/renderable/container.d.ts +41 -6
- package/build/renderable/container.d.ts.map +1 -1
- package/build/renderable/draggable.d.ts.map +1 -1
- package/build/renderable/frameAnimation.d.ts.map +1 -1
- package/build/renderable/instanced_mesh.d.ts +16 -14
- package/build/renderable/instanced_mesh.d.ts.map +1 -1
- package/build/renderable/mesh.d.ts +62 -5
- package/build/renderable/mesh.d.ts.map +1 -1
- package/build/renderable/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/trigger.d.ts +5 -0
- package/build/renderable/trigger.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 +443 -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/device.d.ts +11 -2
- package/build/system/device.d.ts.map +1 -1
- package/build/system/fullscreen.d.ts.map +1 -1
- package/build/system/pool.d.ts.map +1 -1
- package/build/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/utils/utils.d.ts.map +1 -1
- package/build/video/canvas/canvas_renderer.d.ts +16 -19
- package/build/video/canvas/canvas_renderer.d.ts.map +1 -1
- package/build/video/canvas_factory.d.ts.map +1 -1
- package/build/video/effects/shadereffect.d.ts +127 -0
- package/build/video/effects/shadereffect.d.ts.map +1 -1
- package/build/video/renderer.d.ts +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/batchers/lit_mesh_batcher.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/lit_mesh_batcher.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 +41 -17
- package/skills/melonjs-20-migration/SKILL.md +50 -7
- package/skills/melonjs-3d/SKILL.md +138 -38
- package/skills/melonjs-3d-assets/SKILL.md +83 -33
- package/skills/melonjs-audio/SKILL.md +62 -19
- package/skills/melonjs-camera-and-drawing/SKILL.md +135 -8
- package/skills/melonjs-deployment/SKILL.md +92 -14
- package/skills/melonjs-display-and-scaling/SKILL.md +8 -3
- package/skills/melonjs-effects-and-shaders/SKILL.md +47 -15
- package/skills/melonjs-events/SKILL.md +6 -3
- package/skills/melonjs-getting-started/SKILL.md +6 -2
- package/skills/melonjs-input/SKILL.md +102 -16
- package/skills/melonjs-lighting/SKILL.md +13 -1
- package/skills/melonjs-loading-assets/SKILL.md +255 -39
- package/skills/melonjs-math/SKILL.md +26 -8
- package/skills/melonjs-particles-and-trails/SKILL.md +77 -9
- package/skills/melonjs-performance/SKILL.md +18 -8
- package/skills/melonjs-physics/SKILL.md +435 -128
- package/skills/melonjs-plugins/SKILL.md +50 -11
- package/skills/melonjs-renderables/SKILL.md +31 -7
- package/skills/melonjs-renderer-backends/SKILL.md +38 -17
- package/skills/melonjs-scenes-and-state/SKILL.md +45 -5
- package/skills/melonjs-sprites-and-animation/SKILL.md +37 -2
- package/skills/melonjs-tilemaps/SKILL.md +149 -17
- package/skills/melonjs-ui-and-text/SKILL.md +674 -42
|
@@ -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, setParser, custom asset type, generated texture, procedural texture."
|
|
3
|
+
description: "Use this skill for loading and managing game assets in melonJS — the resource descriptor format, the full asset type list, organising a multi-level game's assets as bundles (an assets file exporting common plus one bundle per level, passed to loader.preload), loading and freeing a level's bundle, preload versus load, retrieving loaded assets, base URLs and cross-origin settings, and the loading screen. Triggers on: loader, loader.preload, loader.load, resources, resources.js, assets file, getImage, getJSON, getTMX, getGLTF, getVideo, getFont, setBaseURL, setOptions, crossOrigin, withCredentials, credentials, cookies, authenticated assets, CORS, asset, preload, LOADER_COMPLETE, loading screen, unload, unloadAll, bundle, asset bundle, setBundle, getBundle, isLoaded, several levels, per-level assets, free memory between levels, setParser, custom asset type, generated texture, procedural texture."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,28 +8,13 @@ license: MIT
|
|
|
8
8
|
|
|
9
9
|
## The resource descriptor
|
|
10
10
|
|
|
11
|
-
Every asset is `{ name, type, src }` plus type-specific extras
|
|
11
|
+
Every asset is `{ name, type, src }` plus type-specific extras. The name is
|
|
12
|
+
what the asset is read back by; the type picks the parser. Type and name
|
|
13
|
+
together identify an asset, so an atlas's image and its JSON share a name:
|
|
12
14
|
|
|
13
15
|
```js
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
loader.setOptions({ crossOrigin: "anonymous" }); // needed for remote assets
|
|
17
|
-
|
|
18
|
-
await loader.preload([
|
|
19
|
-
{ name: "tiles", type: "image", src: "data/img/tiles.png" },
|
|
20
|
-
{ name: "map1", type: "tmx", src: "data/map/map1.tmx" },
|
|
21
|
-
{ name: "tileset", type: "tsx", src: "data/map/tileset.tsx" },
|
|
22
|
-
{ name: "sfx", type: "audio", src: "data/sfx/" },
|
|
23
|
-
{ name: "font", type: "fontface", src: "data/font/PressStart2P.ttf" },
|
|
24
|
-
{ name: "atlas", type: "json", src: "data/img/atlas.json" },
|
|
25
|
-
]);
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
`preload()` has been awaitable since 19.8. The callback form still works and
|
|
29
|
-
takes a third argument to suppress the built-in loading screen:
|
|
30
|
-
|
|
31
|
-
```js
|
|
32
|
-
loader.preload(resources, onLoaded, /* switchToLoadState */ false);
|
|
16
|
+
{ name: "texture", type: "image", src: "data/img/texture.png" }
|
|
17
|
+
{ name: "texture", type: "json", src: "data/img/texture.json" }
|
|
33
18
|
```
|
|
34
19
|
|
|
35
20
|
## The type list
|
|
@@ -38,7 +23,7 @@ The type strings are melonJS-specific — this is not a generic loader:
|
|
|
38
23
|
|
|
39
24
|
| type | for |
|
|
40
25
|
|---|---|
|
|
41
|
-
| `"image"` | textures, sprite sheets, normal maps |
|
|
26
|
+
| `"image"` | textures, sprite sheets, normal maps; `src` may be an array tried in order, such as compressed formats (`.ktx`, `.ktx2`, `.dds`, `.pvr`, `.pkm`) by priority with a PNG last |
|
|
42
27
|
| `"json"` | data, texture-atlas descriptors, collision shapes from a shape editor |
|
|
43
28
|
| `"tmx"` / `"tsx"` | Tiled maps and external tilesets |
|
|
44
29
|
| `"audio"` | sound — **`src` is a directory**, see below |
|
|
@@ -52,6 +37,128 @@ The type strings are melonJS-specific — this is not a generic loader:
|
|
|
52
37
|
| `"js"` | scripts |
|
|
53
38
|
| any type you register | your own formats, and things you generate rather than download — see [Custom asset types](#custom-asset-types) |
|
|
54
39
|
|
|
40
|
+
## Organise the assets: `common` plus one bundle per level
|
|
41
|
+
|
|
42
|
+
For any game with **more than one level or screen**, the assets file exports a
|
|
43
|
+
**record of bundles**: what every level uses under `common`, and each level's
|
|
44
|
+
own assets in a bundle of its own. The boot call takes the record as it is:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
// resources.js
|
|
48
|
+
export const resources = {
|
|
49
|
+
common: [ // everything every level uses
|
|
50
|
+
{ name: "player", type: "image", src: "data/img/player.png" },
|
|
51
|
+
{ name: "tileset", type: "image", src: "data/img/tileset.png" },
|
|
52
|
+
{ name: "tileset", type: "tsx", src: "data/map/tileset.tsx" },
|
|
53
|
+
{ name: "music", type: "audio", src: "data/bgm/" },
|
|
54
|
+
{ name: "jump", type: "audio", src: "data/sfx/" },
|
|
55
|
+
],
|
|
56
|
+
level1: [
|
|
57
|
+
{ name: "level1", type: "tmx", src: "data/map/level1.tmx" },
|
|
58
|
+
{ name: "hills", type: "image", src: "data/img/hills.png" },
|
|
59
|
+
],
|
|
60
|
+
level2: [
|
|
61
|
+
{ name: "level2", type: "tmx", src: "data/map/level2.tmx" },
|
|
62
|
+
{ name: "hills", type: "image", src: "data/img/hills.png" }, // shared: listed again
|
|
63
|
+
{ name: "lava", type: "image", src: "data/img/lava.png" },
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
// game.js
|
|
70
|
+
import { audio, loader, state } from "melonjs";
|
|
71
|
+
import { resources } from "./resources.js";
|
|
72
|
+
|
|
73
|
+
audio.init("mp3,ogg"); // before any audio is loaded
|
|
74
|
+
await loader.preload(resources); // every bundle, behind the loading screen
|
|
75
|
+
state.change(state.PLAY);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`preload(record)` declares each key as a bundle and loads them all, with one
|
|
79
|
+
progress bar; an asset several bundles list is fetched once. Nothing else
|
|
80
|
+
changes: a bundle is a lifetime, not a namespace, so assets keep their own
|
|
81
|
+
names, and `getImage("hills")`, `new Sprite(x, y, { image: "player" })` and
|
|
82
|
+
`level.load("level1")` work as with a flat list. What bundles add is that a
|
|
83
|
+
level can later be freed and loaded again **by its id** (next section). Starting
|
|
84
|
+
with bundles costs nothing even if the game never unloads anything.
|
|
85
|
+
|
|
86
|
+
- **Name a level's bundle after its map.** Bundle ids and asset names are
|
|
87
|
+
separate, so `"level1"` can be both, and `level.load(id)` and
|
|
88
|
+
`loader.load(id)` then take the same string.
|
|
89
|
+
- **List a shared asset in every bundle that uses it.** It is still fetched
|
|
90
|
+
once, and it is what keeps the asset alive when one of those bundles is
|
|
91
|
+
unloaded.
|
|
92
|
+
- **A map's dependencies go with it**: its `.tsx` and tileset images in the
|
|
93
|
+
same bundle, or in `common`.
|
|
94
|
+
- **Effects that outlive a level go in `common`** (a HUD's or a camera's
|
|
95
|
+
shader): unloading the level's bundle would destroy them under whatever
|
|
96
|
+
still draws with them.
|
|
97
|
+
|
|
98
|
+
**A flat array is the simplest form**, and is fine for a single-screen game, a
|
|
99
|
+
prototype or a jam game. It works as it always has, and everything in it stays
|
|
100
|
+
in memory for the whole game:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
await loader.preload([
|
|
104
|
+
{ name: "tiles", type: "image", src: "data/img/tiles.png" },
|
|
105
|
+
{ name: "map1", type: "tmx", src: "data/map/map1.tmx" },
|
|
106
|
+
{ name: "sfx", type: "audio", src: "data/sfx/" },
|
|
107
|
+
]);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Moving to bundles later is a change to the assets file alone: wrap the list in
|
|
111
|
+
`{ common: [ … ] }` and split the levels out.
|
|
112
|
+
|
|
113
|
+
Either way, `preload()` can be awaited (since 19.8) or given a callback, and a
|
|
114
|
+
third argument suppresses the built-in loading screen:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
loader.preload(resources, onLoaded, /* switchToLoadState */ false);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Level by level: load before, unload after
|
|
121
|
+
|
|
122
|
+
With every bundle preloaded at boot, nothing more is needed to play. To keep
|
|
123
|
+
memory bounded in a game whose levels are many or large:
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
import { level, loader } from "melonjs";
|
|
127
|
+
|
|
128
|
+
// leaving level1 for good: load the next, switch, then free the previous
|
|
129
|
+
await loader.load("level2"); // fetches nothing if still in memory
|
|
130
|
+
await level.load("level2", { async: true });
|
|
131
|
+
loader.unload("level1"); // "hills" stays: level2 lists it too
|
|
132
|
+
|
|
133
|
+
// going back to a level that was unloaded
|
|
134
|
+
await loader.load("level1"); // fetches what unloading freed
|
|
135
|
+
await level.load("level1", { async: true });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- **`await loader.load(id)` before entering a level that may not be in
|
|
139
|
+
memory.** When the bundle is still loaded it resolves at once with no
|
|
140
|
+
request, so it is cheap to call every time. It does not show the loading
|
|
141
|
+
screen (the game keeps running); use `loader.preload([id])` for that, or
|
|
142
|
+
`loader.isLoaded(id)` to decide.
|
|
143
|
+
- **`loader.unload(id)` for a level the player will not come back to soon,
|
|
144
|
+
after the next level is in.** It frees what no other loaded bundle lists.
|
|
145
|
+
Unloading the level being played leaves its world as it is, but with no
|
|
146
|
+
current level, so `level.reload()` has nothing to reload.
|
|
147
|
+
|
|
148
|
+
**When not to unload:**
|
|
149
|
+
|
|
150
|
+
- **Levels that loop back** — a hub, a retry after game over, a last level
|
|
151
|
+
whose exit leads to the first. Unloading one means fetching it again, and a
|
|
152
|
+
hitch, every time the player returns.
|
|
153
|
+
- **A small game.** If every level fits comfortably in memory, keep
|
|
154
|
+
everything loaded. The platformer example does exactly this: `resources.ts`
|
|
155
|
+
exports `{ common, map1, map2 }`, `preload(resources)` loads it all at boot,
|
|
156
|
+
and since map2's exit leads back to map1, nothing is ever unloaded.
|
|
157
|
+
|
|
158
|
+
Unload when the levels are large (big tilesets and backgrounds, per-level
|
|
159
|
+
music, 3D scenes), numerous, or linear (a level behind the player for good),
|
|
160
|
+
and on memory-constrained targets such as phones and mini-game platforms.
|
|
161
|
+
|
|
55
162
|
## What belongs in the manifest — and what cannot
|
|
56
163
|
|
|
57
164
|
Preloading is not only for things with a URL. Three tiers, and the middle one
|
|
@@ -119,8 +226,8 @@ const app = new Application(1280, 720, {
|
|
|
119
226
|
|
|
120
227
|
**You do not call this yourself.** `preload()` calls `renderer.prewarm()` once
|
|
121
228
|
the assets are in and **before** `LOADER_COMPLETE` fires, so it happens while
|
|
122
|
-
the progress bar is still up. Every `preload()` warms, not just the first,
|
|
123
|
-
|
|
229
|
+
the progress bar is still up. Every `preload()` warms, not just the first, and
|
|
230
|
+
so does `load(id)`, so loading a level's bundle warms whatever it brought.
|
|
124
231
|
|
|
125
232
|
The one precondition for this part: **a game that never calls
|
|
126
233
|
`loader.preload()` does not get it**. If you load assets another way, call
|
|
@@ -349,15 +456,85 @@ event.on(event.LOADER_COMPLETE, () => { /* … */ });
|
|
|
349
456
|
event.on(event.LOADER_ERROR, (res) => { /* … */ });
|
|
350
457
|
```
|
|
351
458
|
|
|
352
|
-
|
|
459
|
+
**Each `preload()` has only its own callback.** One given none calls nothing
|
|
460
|
+
on completion, so a later `await loader.preload(levelAssets)` does not re-run
|
|
461
|
+
the boot callback. Before 20.13 the loader kept the last callback it was given
|
|
462
|
+
and called it again for a preload that had none — usually a `state.change`
|
|
463
|
+
straight back to the first screen. `loader.reload(src)` after a failure
|
|
464
|
+
completes the preload that asset failed in, with that preload's callback, once
|
|
465
|
+
all of its assets are in.
|
|
466
|
+
|
|
467
|
+
**Progress is per preload:** `LOADER_PROGRESS` goes from that call's first
|
|
468
|
+
asset to `1` with its last, every time. A preload started while another is
|
|
469
|
+
still loading joins it, and the value covers both until everything in flight
|
|
470
|
+
has settled; the next preload starts from zero again. A failed asset keeps
|
|
471
|
+
its preload short of `1`, and only that preload.
|
|
472
|
+
|
|
473
|
+
## Bundle and unloading reference
|
|
474
|
+
|
|
475
|
+
Bundles can also be declared one by one, which is what `preload(record)` does
|
|
476
|
+
for each key:
|
|
353
477
|
|
|
354
478
|
```js
|
|
355
|
-
loader.
|
|
356
|
-
|
|
479
|
+
loader.setBundle("level3", [
|
|
480
|
+
{ name: "level3", type: "tmx", src: "data/map/level3.tmx" },
|
|
481
|
+
{ name: "hills", type: "image", src: "data/img/hills.png" },
|
|
482
|
+
]);
|
|
483
|
+
await loader.preload(["common", "level3"]); // ids and descriptors mix freely
|
|
357
484
|
```
|
|
358
485
|
|
|
359
|
-
|
|
360
|
-
|
|
486
|
+
- **Strings are bundles, objects are assets.** `loader.unload("map1")`
|
|
487
|
+
releases the bundle `"map1"`; `loader.unload({ name: "map1", type: "tmx" })`
|
|
488
|
+
frees that one asset at once, whoever holds it (the bundles listing it then
|
|
489
|
+
report `isLoaded(id) === false`, and loading one again fetches it again).
|
|
490
|
+
- **`load(id)`** goes through the same path as `preload()`: `LOADER_PROGRESS`
|
|
491
|
+
for the bundle's own assets, the shader warm-up, then `LOADER_COMPLETE`. It
|
|
492
|
+
does not switch to the loading screen, and always returns a promise
|
|
493
|
+
(`onload` / `onerror` callbacks are optional extras).
|
|
494
|
+
- **`load(id, assets)` declares and loads in one call** — `setBundle(id, assets)`
|
|
495
|
+
then `load(id)`. Called again with the same assets (type, name, src) it just
|
|
496
|
+
loads; with different ones it redefines the bundle, which throws while that
|
|
497
|
+
bundle is loaded. Handy where a level's assets are declared next to the code
|
|
498
|
+
that enters it.
|
|
499
|
+
- **`preload(record)` again** fetches nothing already in memory. A bundle
|
|
500
|
+
already declared with the same assets is left as it is. A single descriptor
|
|
501
|
+
passed by mistake throws: wrap it in an array.
|
|
502
|
+
- **Progress:** each asset of the bundle is one step; one already in memory
|
|
503
|
+
counts as a step done at once. An asset listed twice is one asset, and an
|
|
504
|
+
empty bundle is allowed (it is always loaded).
|
|
505
|
+
- **Nothing is fetched twice.** An asset already in memory is not requested
|
|
506
|
+
again, however it got there, and two bundles loading the same asset at once
|
|
507
|
+
share one request.
|
|
508
|
+
- **Ownership.** Each loaded bundle listing an asset holds it; `unload(id)`
|
|
509
|
+
frees the assets no other loaded bundle lists. An asset loaded the old way —
|
|
510
|
+
a flat `preload` list, or `load(asset)` — is **pinned**: no bundle unload
|
|
511
|
+
frees it, only `unload(asset)` or `unloadAll()`.
|
|
512
|
+
- **`isLoaded(id)`** is true when every asset of the bundle is in memory,
|
|
513
|
+
however it got there. `getBundle(id)` returns a copy of the list, or
|
|
514
|
+
`undefined` for an id never declared.
|
|
515
|
+
- **Freeing a map unregisters its level.** After `unload(id)`, `level.load` of
|
|
516
|
+
a map (or glTF scene) that bundle freed throws "level … not found" until the
|
|
517
|
+
bundle is loaded again, which registers it again.
|
|
518
|
+
- **Freeing releases the GPU side too:** an image's texture (since 20.14), a
|
|
519
|
+
shader's program, a font's place in `document.fonts`. `getShader()` hands out
|
|
520
|
+
the loader's shared instance and does not track who took it, hence
|
|
521
|
+
"effects that outlive a level go in `common`".
|
|
522
|
+
- **A failing asset rejects `load(id)`**, like `preload`; the assets that did
|
|
523
|
+
load stay, held by the bundle, and `loader.reload(src)` completes it.
|
|
524
|
+
- **Errors are synchronous.** An unknown id throws in `load`, `unload`,
|
|
525
|
+
`preload` and `isLoaded`. Redefining a bundle with `setBundle` throws while
|
|
526
|
+
it is loading or loaded: unload it first. A bundle lists descriptors only,
|
|
527
|
+
not other bundle ids.
|
|
528
|
+
- **`unloadAll()`** frees every asset, audio included, whoever holds it. The
|
|
529
|
+
bundles stay declared, all back to not loaded, ready to be loaded again.
|
|
530
|
+
Levels stay registered (unlike a bundle unload), so load a level's bundle
|
|
531
|
+
again before `level.load` of it.
|
|
532
|
+
- **`load(asset)`, with a descriptor**, loads one asset while the game runs:
|
|
533
|
+
no loading screen, progress or loader event, only its promise (or the
|
|
534
|
+
`onload` / `onerror` callbacks). It is pinned. One already in memory
|
|
535
|
+
resolves at once with no request (the callback form returns `0` and never
|
|
536
|
+
calls `onload`), except a `"binary"`, `"fontface"`, `"aseprite"` or `"js"`
|
|
537
|
+
asset, which is fetched again.
|
|
361
538
|
|
|
362
539
|
## Loading between scenes
|
|
363
540
|
|
|
@@ -377,20 +554,45 @@ state.change(state.PLAY, true);
|
|
|
377
554
|
|
|
378
555
|
## Cross-origin and authenticated assets
|
|
379
556
|
|
|
380
|
-
|
|
381
|
-
|
|
557
|
+
**Nothing to set for the usual cases.** Every fetched asset (image, json,
|
|
558
|
+
binary, tmx, tsx, shader, gltf/glb, obj, mtl, aseprite, buffered audio) is
|
|
559
|
+
requested the same way:
|
|
560
|
+
|
|
561
|
+
- **On the game's own origin**, it carries the browser's cookies and HTTP
|
|
562
|
+
auth, as the page and its scripts do. A game behind a login or a session
|
|
563
|
+
cookie loads its own assets with no option.
|
|
564
|
+
- **On another origin** (a CDN), it is a CORS request: it loads when that
|
|
565
|
+
server sends `Access-Control-Allow-Origin`, and fails at load when not, with
|
|
566
|
+
an error naming the asset and the header it needs. No `crossOrigin` needed.
|
|
567
|
+
|
|
568
|
+
| asset on… | default | `withCredentials: true` |
|
|
569
|
+
|---|---|---|
|
|
570
|
+
| the game's own origin | loads, with its cookies | loads, with its cookies |
|
|
571
|
+
| 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` |
|
|
572
|
+
| another origin allowing credentials for the page | loads, no cookies | loads, with that origin's cookies |
|
|
573
|
+
| another origin with no CORS headers | fails at load, naming the asset | fails at load, naming the asset |
|
|
574
|
+
|
|
575
|
+
Two global settings cover the rest, for every asset type — there is no
|
|
576
|
+
per-asset or per-type spelling to remember:
|
|
382
577
|
|
|
383
578
|
```js
|
|
384
579
|
loader.setOptions({
|
|
385
|
-
|
|
386
|
-
|
|
580
|
+
withCredentials: true, // send cookies / auth to OTHER origins too
|
|
581
|
+
crossOrigin: "anonymous", // attribute of the <video>, <script> and glTF
|
|
582
|
+
// image elements the loader creates
|
|
387
583
|
});
|
|
388
584
|
```
|
|
389
585
|
|
|
390
|
-
`withCredentials` is
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
586
|
+
`withCredentials` is for assets on **another** origin behind a session cookie
|
|
587
|
+
or a login; that server must then allow credentials for the page's origin, or
|
|
588
|
+
the browser blocks the request. `crossOrigin: "use-credentials"` does the same
|
|
589
|
+
for fetched assets, and sets the attribute on the elements.
|
|
590
|
+
|
|
591
|
+
Before 20.13 both were needed far more often: assets on the game's own origin
|
|
592
|
+
went out with no cookies unless `withCredentials` was set (a 401 or 403 behind
|
|
593
|
+
a login), and an asset on another origin without `crossOrigin: "anonymous"`
|
|
594
|
+
was read as an empty response, failing later with "Unexpected end of input" or
|
|
595
|
+
"The source image could not be decoded" even when its server allowed it.
|
|
394
596
|
|
|
395
597
|
**Set them through `setOptions`.** `loader.crossOrigin` and
|
|
396
598
|
`loader.withCredentials` are read-only module bindings; assigning to them
|
|
@@ -408,16 +610,30 @@ throws, the same trap as the removed `loader.onload` / `onProgress`.
|
|
|
408
610
|
| audio 404s | `src` given as a file instead of a directory |
|
|
409
611
|
| audio fails, `preload()` rejects with "Failed loading resource" | `audio.init()` not called before preloading — the real message is replaced by the loader's |
|
|
410
612
|
| blank level, missing tiles | `.tsx` or tileset image not listed in the resources |
|
|
411
|
-
|
|
|
412
|
-
|
|
|
613
|
+
| `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 |
|
|
614
|
+
| 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) |
|
|
615
|
+
| 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
616
|
| `TypeError` assigning `loader.withCredentials` / `crossOrigin` | read-only module bindings; use `loader.setOptions({ … })` |
|
|
414
617
|
| 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
618
|
| text renders in a fallback font | web font not preloaded as `"fontface"` |
|
|
416
619
|
| `BitmapText` renders nothing | the `.fnt` (as `"binary"`) or its image is missing |
|
|
417
620
|
| `TypeError` assigning `loader.onProgress` / `onload` | removed in 20.3; they were never writable — use the `LOADER_*` events |
|
|
621
|
+
| `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 |
|
|
622
|
+
| 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 |
|
|
623
|
+
| `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
624
|
| `load : unknown or invalid resource type` | a custom type in the manifest before its `loader.setParser` call |
|
|
419
625
|
| 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
626
|
| 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 |
|
|
627
|
+
| `level X not found` after switching levels with bundles | that map's bundle was unloaded (it unregisters the level), or never loaded: `await loader.load(id)` before `level.load` |
|
|
628
|
+
| `loader: unknown bundle "…"` | a typo, or `setBundle` not called yet; ids and asset names are separate, a bundle is not found by an asset's name |
|
|
629
|
+
| `setBundle(…): the bundle is loaded` | redefining a loaded bundle, by `setBundle` or by `load(id, assets)` with a different list: `loader.unload(id)` first |
|
|
630
|
+
| a shared image disappears (blank layer) after unloading the previous level's bundle | the next level's bundle does not list it: list a shared asset in every bundle that uses it |
|
|
631
|
+
| an asset survives `unload(id)` and memory stays up | it was also loaded the old way (a flat `preload` list, or `load(asset)`), which pins it; load it only through bundles, or `unload(asset)` |
|
|
632
|
+
| a HUD or camera effect goes blank, or throws, after a level's bundle is unloaded | its shader came from that bundle; effects that outlive a level belong in a bundle that outlives it (`"common"`) |
|
|
633
|
+
| `level.reload()` throws `level undefined not found` | the current level's own bundle was unloaded; unload the previous level's bundle after the next level is in |
|
|
634
|
+
| a pause and new network requests every time the player goes back to an earlier level | that level's bundle is unloaded on leaving it although the game loops back to it; keep it loaded (see [When not to unload](#level-by-level-load-before-unload-after)) |
|
|
635
|
+
| memory grows level after level although assets are unloaded (before 20.14) | unloading an image left its GPU texture in the renderer; update |
|
|
636
|
+
| an asset given as a `data:` URI fails, or the default loading screen shows no logo, in a sandboxed embed or under a strict Content-Security-Policy (before 20.11) | the `data:` URI was fetched, which a `connect-src` without `data:` refuses; update, it is decoded in place |
|
|
421
637
|
|
|
422
638
|
## Related skills
|
|
423
639
|
|
|
@@ -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
|
|
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, setPosition, 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
|
|
|
@@ -97,6 +97,9 @@ v.scale(2, 2, 2); // (1,2,3) -> (2,4,6) what you meant
|
|
|
97
97
|
v.set(10, 20); // z is now 0, whatever it was
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
+
To move a renderable, `setPosition(x, y)` (20.8) is the call that keeps its
|
|
101
|
+
depth: leave out `z` and it stays as it was, where `pos.set(x, y)` zeroes it.
|
|
102
|
+
|
|
100
103
|
**`Vector3d.pos` on a renderable is typed `Vector2d`.** `Renderable.pos` is an
|
|
101
104
|
`ObservableVector3d` at run time but is declared through `Polygon` as a
|
|
102
105
|
`Vector2d`, so `.z`, `.setMuted` and `.setCallback` need a cast from
|
|
@@ -117,7 +120,7 @@ The 2D one is the z component of the 3D cross, which is the useful scalar for
|
|
|
117
120
|
```js
|
|
118
121
|
a.lerp(b, t); // straight line, t in 0..1
|
|
119
122
|
a.slerp(b, t); // along the ARC, constant angular rate
|
|
120
|
-
a.damp(b, lambda, dt); // frame-rate independent ease toward b
|
|
123
|
+
a.damp(b, lambda, dt); // frame-rate independent ease toward b (dt in seconds)
|
|
121
124
|
a.moveTowards(b, step); // fixed distance per call, never overshoots
|
|
122
125
|
```
|
|
123
126
|
|
|
@@ -187,13 +190,22 @@ which is how a renderable knows its bounds went stale.
|
|
|
187
190
|
|
|
188
191
|
```js
|
|
189
192
|
v.setCallback(() => { ... });
|
|
190
|
-
v.setMuted(
|
|
193
|
+
v.setMuted(x, y, z); // write without firing it
|
|
194
|
+
v.setCallback(undefined); // stop observing
|
|
191
195
|
```
|
|
192
196
|
|
|
193
197
|
**`setMuted` is what you want when writing several components**, so a
|
|
194
198
|
half-updated vector does not fire a callback that reads the other half.
|
|
195
199
|
`set()` already does this internally: it disables the callback, writes all
|
|
196
|
-
three, fires once, and re-enables.
|
|
200
|
+
three, fires once, and re-enables. Every mutating method (`add`, `scale`,
|
|
201
|
+
`negateSelf`, `floorSelf`, `clampSelf`, `normalize`, `lerp`, …) goes through
|
|
202
|
+
`set()`, so it fires once and returns the observable vector itself. Before
|
|
203
|
+
20.13 `negateSelf`, `floorSelf`, `ceilSelf` and `clampSelf` (and the 3D
|
|
204
|
+
`normalize` and `perp`) fired once per component and returned the wrapped
|
|
205
|
+
plain vector instead.
|
|
206
|
+
|
|
207
|
+
`clone()` takes from a pool, and gets the callback you pass it or none: before
|
|
208
|
+
20.13 a recycled clone kept the callback of the vector released before it.
|
|
197
209
|
|
|
198
210
|
Note the observable `set(x = 0, y = 0, z = 0)` defaults EVERY argument, so
|
|
199
211
|
`set(10, 20)` zeroes z exactly as the plain vector does.
|
|
@@ -283,11 +295,13 @@ math.clamp(v, lo, hi);
|
|
|
283
295
|
math.lerp(a, b, t); // scalars
|
|
284
296
|
math.damp(a, b, lambda, dt); // frame-rate independent
|
|
285
297
|
math.degToRad(deg); math.radToDeg(rad);
|
|
286
|
-
math.random(lo, hi); // integer
|
|
298
|
+
math.random(lo, hi); // integer, hi EXCLUDED: random(1, 6) never gives 6
|
|
287
299
|
math.randomFloat(lo, hi);
|
|
288
|
-
math.weightedRandom(lo, hi); // integer, biased toward LO
|
|
289
|
-
math.round(n, decimals);
|
|
300
|
+
math.weightedRandom(lo, hi); // integer, biased toward LO, hi excluded
|
|
301
|
+
math.round(n, decimals); // nearest; a tie goes up, as Math.round: -2.5 → -2
|
|
290
302
|
math.pow(n); // n squared
|
|
303
|
+
math.lerpArray([1, 0.5, 0], t); // a value along evenly spaced samples, t in 0..1
|
|
304
|
+
math.isPowerOfTwo(n); math.nextPowerOfTwo(n);
|
|
291
305
|
math.DEG_TO_RAD math.RAD_TO_DEG math.TAU math.ETA math.EPSILON
|
|
292
306
|
```
|
|
293
307
|
|
|
@@ -315,9 +329,13 @@ and `Math.random()` every frame jitters instead of drifting.
|
|
|
315
329
|
| symptom | cause |
|
|
316
330
|
|---|---|
|
|
317
331
|
| z unchanged after scaling | `scale(s)` leaves z alone; pass `scale(s, s, s)` |
|
|
318
|
-
| z became 0 for no reason | `set(x, y)` defaults z to 0 |
|
|
332
|
+
| z became 0 for no reason | `set(x, y)` defaults z to 0; move a renderable with `setPosition(x, y)` |
|
|
319
333
|
| the original vector changed | the method mutates; `copy()` into something first |
|
|
334
|
+
| 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 |
|
|
335
|
+
| `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 |
|
|
336
|
+
| `math.isPowerOfTwo(0)` is true, or a fraction passes | before 20.13 it accepted zero, negative and fractional values; upgrade |
|
|
320
337
|
| memory climbs in a loop | `clone()` or `negate()`/`floor()`/`ceil()` per frame |
|
|
338
|
+
| memory taken by dropped `clone()`s never comes back | before 20.9 the pool kept every instance it handed out; upgrade, and release what you clone |
|
|
321
339
|
| two helper results are the same object | a shared scratch vector was returned |
|
|
322
340
|
| `.z` is a type error | `Renderable.pos` is declared `Vector2d` (#1510) |
|
|
323
341
|
| rotation eases oddly between directions | `lerp` on directions; use `slerp` |
|
|
@@ -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, stopStream, autoDestroyOnComplete, onComplete, sprite sheet particles, framewidth, randomFrame, cone, sphere, 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,51 @@ 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` (deprecated since 20.2, and does nothing), and a
|
|
68
|
+
`Container` as `referenceSpace` (it offers
|
|
69
|
+
`"local"` and `"world"`). `textureAdditive` is left out: `blendMode`
|
|
70
|
+
replaces it.
|
|
71
|
+
- **Changes at run time.** Calling `burstParticles()` when something
|
|
72
|
+
happens, or assigning `emitter.blendMode` while particles are alive, is
|
|
73
|
+
game code. Its "moving emitter" option previews the common case: an
|
|
74
|
+
emitter attached to something that moves.
|
|
75
|
+
|
|
31
76
|
## Sprite-sheet particles
|
|
32
77
|
|
|
33
78
|
Use `framewidth` and `frameheight` (the same names as on `Sprite`) to divide
|
|
@@ -107,15 +152,33 @@ fountain and wrong for an explosion: additive sprites piled on one point clip
|
|
|
107
152
|
all three channels there however well they are tinted, so a burst reads as one
|
|
108
153
|
bright flash rather than as debris.
|
|
109
154
|
|
|
110
|
-
Give it the size of whatever broke. The
|
|
111
|
-
|
|
112
|
-
to the right of its source:
|
|
155
|
+
Give it the size of whatever broke. The constructor centres the area on the
|
|
156
|
+
point it is given, so pass the centre of the source:
|
|
113
157
|
|
|
114
158
|
```js
|
|
115
159
|
const area = radius * 2;
|
|
116
|
-
const emitter = new ParticleEmitter(x
|
|
117
|
-
|
|
118
|
-
|
|
160
|
+
const emitter = new ParticleEmitter(x, y, { width: area, height: area });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
After that, `pos` is the area's top-left corner, as on any renderable. To move
|
|
164
|
+
the emitter to a new source later, offset by half, or the burst sits down and
|
|
165
|
+
to the right of it:
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
emitter.pos.set(x - area / 2, y - area / 2); // re-aimed at (x, y)
|
|
169
|
+
emitter.burstParticles();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Under a `Camera3d`, `depth` (20.13) gives the area its third side: each
|
|
173
|
+
particle is born at a random depth within it, centered on the emitter's own
|
|
174
|
+
depth, unlike `width` and `height`. Without it every particle starts on one
|
|
175
|
+
plane facing the camera, so rain or dust seen from the side is a flat sheet. A
|
|
176
|
+
2D camera does not show depth, though with an order-dependent blend the
|
|
177
|
+
particles are still sorted by it. It is the spawn box's size, not the
|
|
178
|
+
emitter's `depth` property, which is where the emitter sits along Z.
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
new ParticleEmitter(x, y, { width: 600, height: 20, depth: 400 }); // a volume of rain
|
|
119
182
|
```
|
|
120
183
|
|
|
121
184
|
## Particles in 3D
|
|
@@ -196,7 +259,9 @@ debris.
|
|
|
196
259
|
|
|
197
260
|
For one-shot effects, `autoDestroyOnComplete: true` removes the emitter once its
|
|
198
261
|
particles have finished. The `onComplete` callback fires at that same moment
|
|
199
|
-
either way.
|
|
262
|
+
either way. A stream finishes only once it stops emitting: one left on the
|
|
263
|
+
default `duration` (`Infinity`) never completes until you call `stopStream()`,
|
|
264
|
+
so give a stream that should clean up after itself a `duration`.
|
|
200
265
|
|
|
201
266
|
For repeated effects (muzzle flashes, impacts) keep **one** long-lived emitter
|
|
202
267
|
and re-aim it per shot, rather than allocating an emitter per event.
|
|
@@ -321,6 +386,7 @@ answer when you want discrete puffs rather than a continuous ribbon.
|
|
|
321
386
|
|---|---|
|
|
322
387
|
| trail looks flat under a `Camera3d` | `Trail` is 2D; use `Trail3d` |
|
|
323
388
|
| 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`) |
|
|
389
|
+
| 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
390
|
| `Trail3d` draws nothing, with a console warning | it needs a `Camera3d`; the app's `cameraClass` is still the 2D default |
|
|
325
391
|
| `trail.tint` does nothing | `Trail` cannot be tinted at all; `Trail3d` can |
|
|
326
392
|
| assigning `maxPoints` throws | `Trail3d` sizes its geometry once; pass `length` to the constructor |
|
|
@@ -329,13 +395,15 @@ answer when you want discrete puffs rather than a continuous ribbon.
|
|
|
329
395
|
| the cloud follows a moving emitter | default `referenceSpace: "local"` — use `"world"` for trails |
|
|
330
396
|
| particles do not travel far enough | settings tuned pre-20.2, when drawn travel was doubled |
|
|
331
397
|
| one-shot emitters accumulate | missing `autoDestroyOnComplete: true` |
|
|
398
|
+
| a streaming emitter with `autoDestroyOnComplete` never goes away | the stream runs on the default `duration` of `Infinity`: give it a `duration`, or call `stopStream()` |
|
|
332
399
|
| 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
400
|
| a `tintOverLife` colour's transparency has no effect | the list sets the colour only; opacity is the particle's fade |
|
|
334
401
|
| `tintOverLife` colours look muddy | the texture is coloured; author it white so the tints come through as given |
|
|
335
402
|
| frame rate drops with many effects | an emitter per event instead of one re-aimed emitter |
|
|
336
403
|
| a 3D burst is invisible under a `Camera3d` | before 20.8 a particle was culled at twice its depth; update |
|
|
337
404
|
| 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
|
|
405
|
+
| 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) |
|
|
406
|
+
| 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
407
|
| the middle of a burst goes white | additive sprites overlapping — `textureAdditive: false`, or spread them |
|
|
340
408
|
| debris chunks look like squares | the default sprite is a filled square; pass your own `image` |
|
|
341
409
|
| a ring tilts with the camera instead of staying on a surface | the launch axis is in WORLD axes, not the camera basis |
|