melonjs 20.2.0 → 20.3.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 +86 -8
- package/build/application/settings.d.ts +3 -2
- package/build/application/settings.d.ts.map +1 -1
- package/build/audio/audio.d.ts +2 -2
- package/build/audio/audio.d.ts.map +1 -1
- package/build/audio/backend/buffer.d.ts +26 -0
- package/build/audio/backend/buffer.d.ts.map +1 -0
- package/build/audio/backend/cache.d.ts +2 -0
- package/build/audio/backend/cache.d.ts.map +1 -0
- package/build/audio/backend/core.d.ts +24 -0
- package/build/audio/backend/core.d.ts.map +1 -0
- package/build/audio/backend/engine.d.ts +54 -0
- package/build/audio/backend/engine.d.ts.map +1 -0
- package/build/audio/backend/sound.d.ts +79 -0
- package/build/audio/backend/sound.d.ts.map +1 -0
- package/build/audio/backend/spatial.d.ts +191 -0
- package/build/audio/backend/spatial.d.ts.map +1 -0
- package/build/audio/backend/types.d.ts +97 -0
- package/build/audio/backend/types.d.ts.map +1 -0
- package/build/audio/backend/voice.d.ts +33 -0
- package/build/audio/backend/voice.d.ts.map +1 -0
- package/build/audio/playback.d.ts +49 -4
- package/build/audio/playback.d.ts.map +1 -1
- package/build/audio/{backend.d.ts → state.d.ts} +13 -11
- package/build/audio/state.d.ts.map +1 -0
- package/build/audio/types.d.ts +93 -1
- package/build/audio/types.d.ts.map +1 -1
- package/build/camera/camera3d.d.ts +5 -5
- package/build/const.d.ts +2 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +8793 -10171
- package/build/index.js.map +4 -4
- package/build/input/input.d.ts +19 -0
- package/build/input/input.d.ts.map +1 -1
- package/build/lang/deprecated.d.ts +4 -2
- package/build/lang/deprecated.d.ts.map +1 -1
- package/build/level/gltf/GLTFScene.d.ts +12 -0
- package/build/level/gltf/GLTFScene.d.ts.map +1 -1
- package/build/level/tiled/TMXObjectFactory.d.ts.map +1 -1
- package/build/loader/loader.d.ts +4 -40
- package/build/loader/loader.d.ts.map +1 -1
- package/build/loader/parsers/audio.d.ts +31 -0
- package/build/loader/parsers/audio.d.ts.map +1 -0
- package/build/loader/parsers/shader.d.ts.map +1 -1
- package/build/physics/adapter.d.ts +3 -3
- package/build/physics/builtin/body.d.ts +23 -24
- package/build/physics/builtin/body.d.ts.map +1 -1
- package/build/physics/builtin/detector.d.ts +1 -1
- package/build/plugin/plugin.d.ts +9 -1
- package/build/plugin/plugin.d.ts.map +1 -1
- package/build/polyfill/index.d.ts +1 -4
- package/build/polyfill/index.d.ts.map +1 -1
- package/build/renderable/mesh.d.ts +16 -0
- package/build/renderable/mesh.d.ts.map +1 -1
- package/build/renderable/renderable.d.ts +16 -10
- package/build/renderable/renderable.d.ts.map +1 -1
- package/build/renderable/sprite.d.ts +7 -7
- package/build/renderable/sprite.d.ts.map +1 -1
- package/build/state/stage.d.ts +4 -2
- package/build/state/stage.d.ts.map +1 -1
- package/build/system/event.d.ts +1 -1
- package/build/system/platform.d.ts +21 -1
- package/build/system/platform.d.ts.map +1 -1
- package/build/utils/fetchdata.d.ts.map +1 -0
- package/build/video/blendmodes.d.ts +1 -1
- package/build/video/canvas/canvas_renderer.d.ts +5 -2
- package/build/video/canvas/canvas_renderer.d.ts.map +1 -1
- package/build/video/effects/blur.d.ts +4 -4
- package/build/video/effects/blur.d.ts.map +1 -1
- package/build/video/effects/chromaticAberration.d.ts +5 -5
- package/build/video/effects/chromaticAberration.d.ts.map +1 -1
- package/build/video/effects/colorMatrix.d.ts +5 -5
- package/build/video/effects/colorMatrix.d.ts.map +1 -1
- package/build/video/effects/desaturate.d.ts +5 -5
- package/build/video/effects/desaturate.d.ts.map +1 -1
- package/build/video/effects/dissolve.d.ts +4 -4
- package/build/video/effects/dissolve.d.ts.map +1 -1
- package/build/video/effects/dropShadow.d.ts +5 -5
- package/build/video/effects/dropShadow.d.ts.map +1 -1
- package/build/video/effects/flash.d.ts +4 -4
- package/build/video/effects/flash.d.ts.map +1 -1
- package/build/video/effects/glow.d.ts +5 -5
- package/build/video/effects/glow.d.ts.map +1 -1
- package/build/video/effects/hologram.d.ts +4 -4
- package/build/video/effects/hologram.d.ts.map +1 -1
- package/build/video/effects/invert.d.ts +5 -5
- package/build/video/effects/invert.d.ts.map +1 -1
- package/build/video/effects/outline.d.ts +6 -6
- package/build/video/effects/outline.d.ts.map +1 -1
- package/build/video/effects/pixelate.d.ts +4 -4
- package/build/video/effects/pixelate.d.ts.map +1 -1
- package/build/video/effects/radialGradient.d.ts +2 -2
- package/build/video/effects/scanline.d.ts +6 -6
- package/build/video/effects/scanline.d.ts.map +1 -1
- package/build/video/effects/sepia.d.ts +5 -5
- package/build/video/effects/sepia.d.ts.map +1 -1
- package/build/video/effects/shadereffect.d.ts +12 -10
- package/build/video/effects/shadereffect.d.ts.map +1 -1
- package/build/video/effects/shine.d.ts +3 -3
- package/build/video/effects/shine.d.ts.map +1 -1
- package/build/video/effects/tintPulse.d.ts +4 -4
- package/build/video/effects/tintPulse.d.ts.map +1 -1
- package/build/video/effects/vignette.d.ts +6 -6
- package/build/video/effects/vignette.d.ts.map +1 -1
- package/build/video/effects/wave.d.ts +4 -4
- package/build/video/effects/wave.d.ts.map +1 -1
- package/build/video/texture/atlas.d.ts +1 -1
- package/build/video/webgl/glshader.d.ts +2 -2
- package/build/video/webgl/webgl_renderer.d.ts +2 -1
- package/build/video/webgl/webgl_renderer.d.ts.map +1 -1
- package/build/video/webgpu/webgpu_renderer.d.ts +2 -1
- package/build/video/webgpu/webgpu_renderer.d.ts.map +1 -1
- package/package.json +5 -9
- package/skills/AGENTS.md +67 -0
- package/skills/melonjs/SKILL.md +156 -0
- package/skills/melonjs-20-migration/SKILL.md +160 -0
- package/skills/melonjs-3d/SKILL.md +226 -0
- package/skills/melonjs-3d-assets/SKILL.md +239 -0
- package/skills/melonjs-audio/SKILL.md +215 -0
- package/skills/melonjs-camera-and-drawing/SKILL.md +171 -0
- package/skills/melonjs-deployment/SKILL.md +113 -0
- package/skills/melonjs-effects-and-shaders/SKILL.md +177 -0
- package/skills/melonjs-events/SKILL.md +159 -0
- package/skills/melonjs-getting-started/SKILL.md +183 -0
- package/skills/melonjs-input/SKILL.md +166 -0
- package/skills/melonjs-lighting/SKILL.md +139 -0
- package/skills/melonjs-loading-assets/SKILL.md +157 -0
- package/skills/melonjs-particles-and-trails/SKILL.md +136 -0
- package/skills/melonjs-performance/SKILL.md +178 -0
- package/skills/melonjs-physics/SKILL.md +321 -0
- package/skills/melonjs-plugins/SKILL.md +191 -0
- package/skills/melonjs-renderables/SKILL.md +214 -0
- package/skills/melonjs-renderer-backends/SKILL.md +173 -0
- package/skills/melonjs-scenes-and-state/SKILL.md +180 -0
- package/skills/melonjs-sprites-and-animation/SKILL.md +173 -0
- package/skills/melonjs-tilemaps/SKILL.md +193 -0
- package/skills/melonjs-ui-and-text/SKILL.md +175 -0
- package/build/audio/backend.d.ts.map +0 -1
- package/build/loader/parsers/fetchdata.d.ts.map +0 -1
- /package/build/{loader/parsers → utils}/fetchdata.d.ts +0 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: melonjs-sprites-and-animation
|
|
3
|
+
description: "Use this skill when creating sprites, playing frame animations, or working with texture atlases in melonJS. Covers Sprite construction from a plain image or an atlas region, addAnimation/setCurrentAnimation, TextureAtlas for TexturePacker/ShoeBox/Aseprite, tint, flip, NineSliceSprite, and the centred anchorPoint that offsets naive positioning. Triggers on: Sprite, new Sprite, addAnimation, setCurrentAnimation, isCurrentAnimation, TextureAtlas, TexturePacker, Aseprite, ShoeBox, atlas, region, framewidth, frameheight, tint, flipX, flipY, NineSliceSprite, animation."
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Sprites, animation and atlases
|
|
8
|
+
|
|
9
|
+
> Creating sprites from images or atlases, driving frame animation, and the
|
|
10
|
+
> positioning trap that catches almost everyone.
|
|
11
|
+
|
|
12
|
+
## `pos` is the centre, not the top-left
|
|
13
|
+
|
|
14
|
+
`Renderable.anchorPoint` defaults to **`(0.5, 0.5)`**, so a sprite's `pos` is its
|
|
15
|
+
**centre**. `Container` forces `(0, 0)` instead, which is why containers and
|
|
16
|
+
sprites appear to disagree.
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
// a 64×64 sprite at (100, 100) covers 68..132, not 100..164
|
|
20
|
+
const s = new Sprite(100, 100, { image: "hero", framewidth: 64, frameheight: 64 });
|
|
21
|
+
|
|
22
|
+
s.anchorPoint.set(0, 0); // switch to top-left positioning if you prefer
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Writing top-left maths against the centred default puts everything half a sprite
|
|
26
|
+
off, with no error. It is the most common source of "close but subtly wrong"
|
|
27
|
+
placement.
|
|
28
|
+
|
|
29
|
+
## Constructing a sprite
|
|
30
|
+
|
|
31
|
+
From a plain image:
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
const hero = new Sprite(x, y, {
|
|
35
|
+
image: "hero", // preloaded asset name
|
|
36
|
+
framewidth: 32,
|
|
37
|
+
frameheight: 32,
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
From a texture atlas region:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
const atlas = new TextureAtlas(loader.getJSON("sprites"), loader.getImage("sprites"));
|
|
45
|
+
const coin = new Sprite(x, y, { image: atlas, region: "coin_01.png" });
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`framewidth` / `frameheight` define the animation grid on a sheet. Omit them and
|
|
49
|
+
the whole image is one frame.
|
|
50
|
+
|
|
51
|
+
## Animation
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
hero.addAnimation("idle", [0, 1, 2, 3], 100); // frames, ms per frame
|
|
55
|
+
hero.addAnimation("walk", [4, 5, 6, 7]);
|
|
56
|
+
hero.addAnimation("die", [8, 9], 150);
|
|
57
|
+
|
|
58
|
+
hero.setCurrentAnimation("walk");
|
|
59
|
+
hero.setCurrentAnimation("die", "idle"); // chain to idle when it ends
|
|
60
|
+
hero.setCurrentAnimation("die", { loop: false }); // play once, hold the last frame
|
|
61
|
+
hero.setCurrentAnimation("die", { loop: false, onComplete: fn }); // …and call fn when it does
|
|
62
|
+
|
|
63
|
+
if (hero.isCurrentAnimation("walk")) { /* … */ }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Frame indices are into the `framewidth`/`frameheight` grid. With an atlas, use
|
|
67
|
+
`atlas.getAnimationSettings([regionNames])` and spread it into the Sprite
|
|
68
|
+
settings, or `atlas.createAnimationFromName([regionNames])`, which returns a
|
|
69
|
+
ready-made Sprite. On a fixed-cell spritesheet only numeric indices are
|
|
70
|
+
accepted — passing region names throws.
|
|
71
|
+
|
|
72
|
+
The second argument to `setCurrentAnimation` is polymorphic. A **string** chains
|
|
73
|
+
to that animation when this one ends. An **options object** takes
|
|
74
|
+
`{ loop, next, speed, onComplete }`, and only an explicit `loop: false` stops the
|
|
75
|
+
looping. A **bare function** is the legacy form: it fires at *every* cycle end,
|
|
76
|
+
and only holds the last frame if it returns `false`. Omit the argument entirely
|
|
77
|
+
and the animation loops forever.
|
|
78
|
+
|
|
79
|
+
Re-selecting the animation that is already current is a no-op, so calling
|
|
80
|
+
`setCurrentAnimation` every frame is safe. An unknown id throws.
|
|
81
|
+
|
|
82
|
+
## Texture atlases
|
|
83
|
+
|
|
84
|
+
`TextureAtlas` reads several formats:
|
|
85
|
+
|
|
86
|
+
| source | how |
|
|
87
|
+
|---|---|
|
|
88
|
+
| TexturePacker / Free Texture Packer JSON | `new TextureAtlas(loader.getJSON(name), loader.getImage(name))` |
|
|
89
|
+
| Aseprite **JSON export** | same as TexturePacker — the format is detected from `meta.app` |
|
|
90
|
+
| Aseprite **`.aseprite` / `.ase` binary** | loader type `"aseprite"`, which stores the composited image *and* a JSON sidecar under the one asset name |
|
|
91
|
+
| ShoeBox | JSON export using the melonJS exporter settings file; same call as TexturePacker |
|
|
92
|
+
| fixed-cell spritesheet | `new TextureAtlas({ framewidth, frameheight, anchorPoint }, loader.getImage(name))` |
|
|
93
|
+
| multi-page | pass an array of atlas JSON objects as the first argument |
|
|
94
|
+
|
|
95
|
+
Helpers: `createSpriteFromName(name)`, `createAnimationFromName([names])`,
|
|
96
|
+
`getAnimationSettings([names])`.
|
|
97
|
+
|
|
98
|
+
Atlases matter for performance: the GPU batchers bind several textures at once
|
|
99
|
+
(up to `renderer.maxTextures`), so a handful of separate images still batch —
|
|
100
|
+
but once a frame crosses that many distinct textures the batch has to flush.
|
|
101
|
+
Packing into one atlas keeps everything on a single binding.
|
|
102
|
+
|
|
103
|
+
## Visual properties
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
sprite.tint.setColor(255, 128, 0); // multiply tint
|
|
107
|
+
sprite.alpha = 0.5;
|
|
108
|
+
sprite.flipX(true); // mirror horizontally
|
|
109
|
+
sprite.flipY(true);
|
|
110
|
+
sprite.blendMode = "additive"; // see the effects skill for the full list
|
|
111
|
+
sprite.scale(2, 2); // ← multiplicative, see below
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**`scale()` is multiplicative, not absolute.** Calling it every frame compounds.
|
|
115
|
+
For absolute scaling reset first:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
sprite.currentTransform.identity();
|
|
119
|
+
sprite.currentTransform.scale(s, s, 1);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This bites hardest with pooled sprites, which arrive carrying their previous
|
|
123
|
+
life's transform.
|
|
124
|
+
|
|
125
|
+
## `NineSliceSprite`
|
|
126
|
+
|
|
127
|
+
For panels and dialogue boxes that stretch without distorting their corners:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
new NineSliceSprite(x, y, {
|
|
131
|
+
image: "panel", width: 300, height: 120,
|
|
132
|
+
insetx: 12, insety: 12, // lowercase x/y — `insetX` is silently ignored
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`width` and `height` are mandatory and the constructor throws without them.
|
|
137
|
+
The insets default to a quarter of the frame.
|
|
138
|
+
|
|
139
|
+
## Pooling
|
|
140
|
+
|
|
141
|
+
Frequently spawned sprites should be pooled:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
pool.register("bullet", Bullet, true); // true = recyclable
|
|
145
|
+
const b = pool.pull("bullet", x, y); // new Bullet(x, y), or onResetEvent(x, y) on a reused one
|
|
146
|
+
world.removeChild(b); // returns it to the pool automatically
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Two consequences:
|
|
150
|
+
|
|
151
|
+
- A recyclable class's `onResetEvent` must reset **everything** its lifetime
|
|
152
|
+
mutates — alpha, tint, scale, animation state. Anything you forget carries
|
|
153
|
+
over to the next use.
|
|
154
|
+
- Pool-recycled objects do **not** fire `onDestroyEvent` on removal. Pair event
|
|
155
|
+
subscriptions with `onActivateEvent` / `onDeactivateEvent` instead.
|
|
156
|
+
|
|
157
|
+
## Symptom → cause
|
|
158
|
+
|
|
159
|
+
| symptom | cause |
|
|
160
|
+
|---|---|
|
|
161
|
+
| everything offset by half a sprite | top-left maths against the centred `anchorPoint` |
|
|
162
|
+
| sprite grows every frame | `scale()` is multiplicative — reset the transform |
|
|
163
|
+
| a recycled sprite keeps its old look | `onResetEvent` does not restore every mutated property |
|
|
164
|
+
| event handlers leak on pooled objects | bound in the constructor rather than `onActivateEvent` |
|
|
165
|
+
| animation never advances | no `framewidth`/`frameheight`, so the sheet is one frame |
|
|
166
|
+
| animation plays once and stops | `{ loop: false }`, or a callback that returned `false` |
|
|
167
|
+
| sprites do not batch | more distinct textures in one frame than `renderer.maxTextures` |
|
|
168
|
+
| nine-slice corners stretch anyway | `insetX`/`insetY` instead of `insetx`/`insety` |
|
|
169
|
+
|
|
170
|
+
## Related skills
|
|
171
|
+
|
|
172
|
+
- `melonjs-renderables` — anchors, draw order, custom draw
|
|
173
|
+
- `melonjs-getting-started` — preloading images and atlases
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: melonjs-tilemaps
|
|
3
|
+
description: "Use this skill for Tiled maps in melonJS — loading TMX/TSX levels, spawning entities from Tiled objects, collision shapes authored in Tiled, isometric and hexagonal maps, and image layers. Covers the pool.register name contract, camera bounds, compressed maps needing the inflate plugin, and the level director API. Triggers on: Tiled, TMX, TSX, tilemap, level.load, tileset, ImageLayer, isometric, hexagonal, staggered, pool.register, Collectable, Trigger, object layer, collision layer, parallax."
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Tiled maps
|
|
8
|
+
|
|
9
|
+
> melonJS's flagship content pipeline. Maps are authored in
|
|
10
|
+
> [Tiled](https://www.mapeditor.org/) and loaded through the `level` director —
|
|
11
|
+
> you rarely touch the `TMX*` classes directly.
|
|
12
|
+
|
|
13
|
+
## Loading a level
|
|
14
|
+
|
|
15
|
+
Preload the map **and** everything it references — the `.tmx`, any external
|
|
16
|
+
`.tsx` tilesets, and the tileset images:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
await loader.preload([
|
|
20
|
+
{ name: "map1", type: "tmx", src: "data/map/map1.tmx" },
|
|
21
|
+
{ name: "tileset", type: "tsx", src: "data/map/tileset.tsx" },
|
|
22
|
+
{ name: "tiles", type: "image", src: "data/img/tiles.png" },
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
level.load("map1"); // inside Stage.onResetEvent
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Asset `type` is `"tmx"` for maps and `"tsx"` for external tilesets — those are
|
|
29
|
+
the only two type strings the loader knows. The *file* can be `.tmx` / `.tsx`
|
|
30
|
+
(XML), or `.json` / `.tmj` / `.tsj`; the parser branches on the extension, not
|
|
31
|
+
on the type. `type: "tmj"` throws `unknown or invalid resource type`. You can
|
|
32
|
+
also skip `src` and pass the map inline via `data` (with `format: "json"` or
|
|
33
|
+
`"xml"`).
|
|
34
|
+
|
|
35
|
+
`level.load(levelId, options)` accepts `container` (default `game.world`),
|
|
36
|
+
`onLoaded` (default `game.onLevelLoaded`), `flatten` (default `game.mergeGroup`)
|
|
37
|
+
and `setViewportBounds` (default **`true`**). It throws `level <id> not found`
|
|
38
|
+
for an unknown id.
|
|
39
|
+
|
|
40
|
+
**`level.load` is deferred while the game loop is running.** It calls
|
|
41
|
+
`state.stop()` and finishes the load in a `setTimeout`, so it returns `true`
|
|
42
|
+
before anything is in the world. Do follow-up work from the `onLoaded` callback
|
|
43
|
+
or an `event.LEVEL_LOADED` listener, not on the next line.
|
|
44
|
+
|
|
45
|
+
`level.reload()`, `level.next()`, `level.previous()`, `level.getCurrentLevelId()`
|
|
46
|
+
and `level.levelCount()` round out the namespace. `flatten: false` wraps each
|
|
47
|
+
Tiled object group in its own `Container` named after the group.
|
|
48
|
+
|
|
49
|
+
## Spawning entities from Tiled objects
|
|
50
|
+
|
|
51
|
+
Objects are matched against a factory registry, in this order: the object's
|
|
52
|
+
Tiled **class**, then its **name**, then the structural fallbacks `"text"`,
|
|
53
|
+
`"tile"` and `"shape"`.
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
pool.register("mainPlayer", PlayerEntity); // ← matches Tiled class OR name
|
|
57
|
+
level.load("map1");
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`pool.register(className, classObj, recycling)` also registers the class as a
|
|
61
|
+
Tiled object factory (and again under an `me.`-prefixed alias), unless you set
|
|
62
|
+
`pool.autoRegisterTiled = false`. The dedicated entry points are:
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
import { registerTiledObjectClass, registerTiledObjectFactory } from "melonjs";
|
|
66
|
+
|
|
67
|
+
registerTiledObjectClass("Enemy", Enemy); // new Enemy(x, y, settings)
|
|
68
|
+
registerTiledObjectFactory("Spine", (settings, map) => { /* → Renderable */ });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Register **before** loading. With no match the object falls through to the
|
|
72
|
+
structural factory and becomes a plain `Renderable` with a static body — it does
|
|
73
|
+
not error, it just has none of your behaviour.
|
|
74
|
+
|
|
75
|
+
`registerTiledObjectClass` **throws** if you register a *different* constructor
|
|
76
|
+
under a name already taken (re-registering the same one is a no-op).
|
|
77
|
+
`registerTiledObjectFactory` overwrites and only `console.warn`s, so it is the
|
|
78
|
+
one to use for overriding a built-in such as `"shape"`.
|
|
79
|
+
|
|
80
|
+
melonJS pre-registers `Renderable`, `Sprite`, `NineSliceSprite`, `Text`,
|
|
81
|
+
`BitmapText`, `ImageLayer`, `ColorLayer`, `Light2d`, `Collectable` and `Trigger`
|
|
82
|
+
as Tiled classes, so `Collectable` and `Trigger` work out of the box.
|
|
83
|
+
|
|
84
|
+
## Camera bounds
|
|
85
|
+
|
|
86
|
+
`level.load` sets the viewport bounds to the map size for you —
|
|
87
|
+
`setViewportBounds` defaults to `true`, and the handler is re-run on every
|
|
88
|
+
viewport resize, centring the map when it is smaller than the screen. You only
|
|
89
|
+
need to do it by hand when you opted out:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
level.load("map1", { setViewportBounds: false });
|
|
93
|
+
app.viewport.setBounds(0, 0, mapWidth, mapHeight); // now it's on you
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The same is true if you drive `TMXTileMap.addTo(container, flatten, setViewportBounds)`
|
|
97
|
+
directly — note that method's `setViewportBounds` defaults to **`false`**, the
|
|
98
|
+
opposite of `level.load`'s.
|
|
99
|
+
|
|
100
|
+
## Collision shapes
|
|
101
|
+
|
|
102
|
+
Draw collision geometry in Tiled on an object layer and melonJS turns it into
|
|
103
|
+
world shapes automatically — rectangles, polygons, polylines, ellipses and
|
|
104
|
+
points all work.
|
|
105
|
+
|
|
106
|
+
The contract is the **layer name**: melonJS lowercases it and checks whether it
|
|
107
|
+
*contains* `"collision"` (so `collision`, `Collision`, `world collision` all
|
|
108
|
+
match). Inside such a layer, objects **with no name** get
|
|
109
|
+
`collisionType = collision.types.WORLD_SHAPE` and `type = "static"`. A *named*
|
|
110
|
+
object in a collision layer keeps whatever its factory gave it — that is how you
|
|
111
|
+
mix triggers into the collision layer.
|
|
112
|
+
|
|
113
|
+
An object that matches no factory and carries no text or tile data falls to the
|
|
114
|
+
`"shape"` factory: a plain `Renderable` with a static `bodyDef` built from its
|
|
115
|
+
geometry, which the world auto-registers with the active adapter on `addChild`.
|
|
116
|
+
|
|
117
|
+
For per-object physics, set the object's custom properties in Tiled; they arrive
|
|
118
|
+
on the settings object passed to your entity's constructor.
|
|
119
|
+
|
|
120
|
+
## Map orientations
|
|
121
|
+
|
|
122
|
+
Orthogonal, **isometric**, hexagonal, staggered and oblique are all supported;
|
|
123
|
+
anything else throws `<orientation> type TMX Tile Map not supported!` at load.
|
|
124
|
+
Isometric maps need y-sorting so objects overlap correctly:
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
app.world.sortOn = "y"; // after level.load
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`sortOn` only accepts `"x"`, `"y"`, `"z"` (the default) or `"depth"` and throws
|
|
131
|
+
on anything else. `Vector2d`, `Vector3d`, their observable variants and `Polygon`
|
|
132
|
+
all carry `.toIso()` / `.to2d()` for converting between screen and map space.
|
|
133
|
+
|
|
134
|
+
## Image layers
|
|
135
|
+
|
|
136
|
+
`ImageLayer` gives parallax backgrounds with a ratio per axis, set in Tiled or in
|
|
137
|
+
code. Repeat behaviour matches Tiled's.
|
|
138
|
+
|
|
139
|
+
## Compressed maps need a plugin
|
|
140
|
+
|
|
141
|
+
A map saved with **gzip, zlib or zstd** compressed layer data throws
|
|
142
|
+
`No inflate function set — GZIP/ZLIB decompression not supported!` at parse time
|
|
143
|
+
without:
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
import { plugin } from "melonjs";
|
|
147
|
+
import { TiledInflatePlugin } from "@melonjs/tiled-inflate-plugin";
|
|
148
|
+
|
|
149
|
+
plugin.register(TiledInflatePlugin); // before preloading any compressed map
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Uncompressed base64 and CSV layer data need no plugin. If a map loads in Tiled
|
|
153
|
+
but not in melonJS, check the layer compression setting first.
|
|
154
|
+
|
|
155
|
+
## Rendering
|
|
156
|
+
|
|
157
|
+
Each tile layer resolves to one of three `renderMode`s — `"shader"`,
|
|
158
|
+
`"prerender"` or `"perTile"` — auto-selected in that order of preference, or
|
|
159
|
+
forced per layer. The `"shader"` path is a single quad per tileset with a
|
|
160
|
+
per-pixel GID lookup in the fragment shader. It requires **all** of:
|
|
161
|
+
|
|
162
|
+
- a renderer that reports GPU tile-layer support (WebGL 2 / WebGPU — not Canvas),
|
|
163
|
+
- `world.gpuTilemap !== false` (the `gpuTilemap` application setting, default `true`),
|
|
164
|
+
- an **orthogonal** layer — isometric, hexagonal, staggered and oblique always
|
|
165
|
+
fall back,
|
|
166
|
+
- no collection-of-image tileset, no non-zero `tileoffset`, and tile overflow
|
|
167
|
+
within the shader's 4-cell limit.
|
|
168
|
+
|
|
169
|
+
When a layer falls back, melonJS `console.warn`s once with the reason. Practical
|
|
170
|
+
consequences: fewer tilesets means fewer batches (one packed tileset beats
|
|
171
|
+
several small ones), and on the Canvas fallback large maps get noticeably
|
|
172
|
+
slower. `world.preRender` (or a `preRender` layer property in Tiled) opts an
|
|
173
|
+
unanimated layer into the offscreen-bake path instead.
|
|
174
|
+
|
|
175
|
+
## Symptom → cause
|
|
176
|
+
|
|
177
|
+
| symptom | cause |
|
|
178
|
+
|---|---|
|
|
179
|
+
| a Tiled object becomes a plain shape with no behaviour | its class/name does not match any registered factory, or it was registered after `level.load` |
|
|
180
|
+
| the world is still empty right after `level.load` | the load is deferred via `setTimeout` while the loop runs — use `onLoaded` / `LEVEL_LOADED` |
|
|
181
|
+
| `level <id> not found` | the map was never preloaded, or the asset `name` differs from the id passed to `load` |
|
|
182
|
+
| `unknown or invalid resource type` | asset `type` set to `"tmj"` / `"tsj"` — use `"tmx"` / `"tsx"` with the `.tmj` / `.tsj` file |
|
|
183
|
+
| camera will not scroll | `setViewportBounds: false`, or the map was added with `addTo()` (which defaults to `false`) |
|
|
184
|
+
| `No inflate function set` at parse time | compressed layers without the tiled-inflate plugin |
|
|
185
|
+
| collision shapes are not solid | the object layer's name does not contain `"collision"`, or the objects are named |
|
|
186
|
+
| isometric objects overlap wrongly | `world.sortOn` not set to `"y"` |
|
|
187
|
+
| missing tiles or a blank map | the `.tsx` or tileset image was not preloaded |
|
|
188
|
+
| tilemap slow on some machines | layer fell back off the shader path — check the console warning for the reason |
|
|
189
|
+
|
|
190
|
+
## Related skills
|
|
191
|
+
|
|
192
|
+
- `melonjs-physics` — what the collision shapes become once loaded
|
|
193
|
+
- `melonjs-getting-started` — preloading and the asset type list
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: melonjs-ui-and-text
|
|
3
|
+
description: "Use this skill for HUDs, buttons, menus, dialogue panels and on-screen text in melonJS. Covers UIBaseElement/UISpriteElement/UITextButton, Draggable and DropTarget, the floating screen-space container pattern, Text and BitmapText, web font loading, and NineSliceSprite panels. Triggers on: UI, HUD, button, UIBaseElement, UISpriteElement, UITextButton, Draggable, DropTarget, menu, dialogue, Text, BitmapText, font, fontface, wordWrapWidth, NineSliceSprite, score display, floating."
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# UI, HUD and text
|
|
8
|
+
|
|
9
|
+
## The HUD pattern
|
|
10
|
+
|
|
11
|
+
A HUD is a `floating` container at a high z, built once and re-added:
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
class HUD extends Container {
|
|
15
|
+
constructor(app) {
|
|
16
|
+
super(0, 0, app.viewport.width, app.viewport.height);
|
|
17
|
+
this.floating = true; // screen space, not world space
|
|
18
|
+
this.isPersistent = true; // survives a world reset
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
app.world.addChild(new HUD(app), 100); // explicit z — not `.z = Infinity`
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
There is no `UIContainer` class. Use `Container`, or `UIBaseElement` when the
|
|
26
|
+
panel itself must react to the pointer — it is a `Container` that already sets
|
|
27
|
+
`floating = true` and `isKinematic = false`.
|
|
28
|
+
|
|
29
|
+
Three things matter here:
|
|
30
|
+
|
|
31
|
+
- **`floating = true`** opts the container out of camera transforms, so it stays
|
|
32
|
+
put while the world scrolls. Without it the HUD scrolls away — a silent
|
|
33
|
+
failure.
|
|
34
|
+
- **Only the parent container needs `floating`.** `addChild` forces
|
|
35
|
+
`child.floating = false` under a floating parent, and the container applies one
|
|
36
|
+
projection swap for the whole subtree.
|
|
37
|
+
- **Pass an explicit z.** `this.z = Number.POSITIVE_INFINITY` appears in several
|
|
38
|
+
shipped examples and does nothing — `renderable.z` is not a real property. The
|
|
39
|
+
real accessor is `depth` (an alias for `pos.z`); `addChild(child, z)` sets it
|
|
40
|
+
for you.
|
|
41
|
+
|
|
42
|
+
Relayout on resize by listening for `event.CANVAS_ONRESIZE`.
|
|
43
|
+
|
|
44
|
+
## Buttons and interactive elements
|
|
45
|
+
|
|
46
|
+
Use the built-ins rather than hand-rolling — they set `isKinematic = false` for
|
|
47
|
+
you, which is the trap that stops hand-rolled buttons receiving clicks at all.
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
class PlayButton extends UISpriteElement {
|
|
51
|
+
constructor(x, y) {
|
|
52
|
+
super(x, y, { image: atlas, region: "play.png" });
|
|
53
|
+
}
|
|
54
|
+
onClick() { state.change(state.PLAY); return false; }
|
|
55
|
+
onOver() { this.setOpacity(1.0); }
|
|
56
|
+
onOut() { this.setOpacity(0.8); }
|
|
57
|
+
onRelease() { return false; }
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
| class | for |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `UIBaseElement` | a `Container` base — clickable, optionally draggable and holdable |
|
|
64
|
+
| `UISpriteElement` | a `Sprite`-backed button with hover/press callbacks |
|
|
65
|
+
| `UITextButton` | a `BitmapText` label on a `RoundRect` background — it needs a **bitmap** font, not a `fontface` |
|
|
66
|
+
| `Draggable` / `DropTarget` | drag-and-drop; `dropTarget.setCheckMethod(dropTarget.CHECKMETHOD_CONTAINS)` to require containment instead of overlap |
|
|
67
|
+
|
|
68
|
+
The callbacks to override are `onClick`, `onOver`, `onOut`, `onRelease` and
|
|
69
|
+
`onHold`; `UIBaseElement` adds `onMove` while dragging. Returning `false` from
|
|
70
|
+
`onClick` / `onRelease` stops the event propagating further.
|
|
71
|
+
|
|
72
|
+
## Text
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
new Text(x, y, {
|
|
76
|
+
font: "Arial",
|
|
77
|
+
size: 32,
|
|
78
|
+
fillStyle: "#FFFFFF",
|
|
79
|
+
textAlign: "center",
|
|
80
|
+
textBaseline: "middle",
|
|
81
|
+
text: "Score: 0",
|
|
82
|
+
wordWrapWidth: 400, // enables wrapping
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`text` accepts a string or an array of lines. Update with `setText()`.
|
|
87
|
+
|
|
88
|
+
**Custom web fonts must be preloaded** with the `"fontface"` asset type:
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
{ name: "PressStart2P", type: "fontface", src: "data/font/PressStart2P.ttf" }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Drawing before the font has loaded silently renders in a fallback font — the
|
|
95
|
+
layout looks subtly wrong rather than failing.
|
|
96
|
+
|
|
97
|
+
## `BitmapText`
|
|
98
|
+
|
|
99
|
+
For pixel-perfect text that scales without antialiasing, and for text drawn in
|
|
100
|
+
volume: every `BitmapText` sharing a font draws glyph quads from that one page
|
|
101
|
+
image, so they batch together. Each `Text` instead owns a private canvas
|
|
102
|
+
texture (re-rasterised whenever it changes), so a screenful of them is a
|
|
103
|
+
screenful of distinct textures.
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
await loader.preload([
|
|
107
|
+
{ name: "PressStart2P", type: "image", src: "data/font/font.png" },
|
|
108
|
+
{ name: "PressStart2P", type: "binary", src: "data/font/font.fnt" },
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
new BitmapText(x, y, {
|
|
112
|
+
font: "PressStart2P",
|
|
113
|
+
size: 2, // a scale ratio, not a pixel size
|
|
114
|
+
text: "GAME OVER",
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
It needs **both** assets, and by default they must share the **same asset
|
|
119
|
+
name** — `settings.font` resolves the image *and*, unless you pass
|
|
120
|
+
`settings.fontData`, the descriptor. Registering the descriptor under a
|
|
121
|
+
different name (`"…-fnt"`) is the usual mistake; either use one name for both,
|
|
122
|
+
or pass `fontData: "PressStart2P-fnt"` explicitly.
|
|
123
|
+
|
|
124
|
+
The descriptor is AngelCode BMFont in either flavour — the text `.fnt` form or
|
|
125
|
+
the XML form — auto-detected, so an `.xml` export loads as-is.
|
|
126
|
+
|
|
127
|
+
## Never draw text through the raw context
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
// ✗ works on Canvas only — getContext() returns the GL/GPU context on the
|
|
131
|
+
// GPU backends, and neither has fillText
|
|
132
|
+
app.renderer.getContext().fillText("hi", 10, 10);
|
|
133
|
+
|
|
134
|
+
// ✓
|
|
135
|
+
world.addChild(new Text(10, 10, { text: "hi", /* … */ }));
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`getContext()` hands back the *backend's* context — a
|
|
139
|
+
`CanvasRenderingContext2D` only under the Canvas renderer. On WebGL or WebGPU
|
|
140
|
+
the call throws `TypeError: … .fillText is not a function`, so this fails loudly
|
|
141
|
+
— but only on the machines that picked a GPU backend, which under `video.AUTO`
|
|
142
|
+
is most of them and probably not yours.
|
|
143
|
+
|
|
144
|
+
## Panels
|
|
145
|
+
|
|
146
|
+
`NineSliceSprite` stretches a panel without distorting its corners — the right
|
|
147
|
+
tool for dialogue boxes and windows:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
new NineSliceSprite(x, y, {
|
|
151
|
+
image: "panel", width: 300, height: 120, insetx: 12, insety: 12,
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The inset keys are lowercase `insetx` / `insety`; `insetX` is silently ignored
|
|
156
|
+
and the corners fall back to a quarter of the frame. `width` and `height` are
|
|
157
|
+
mandatory — the constructor throws without them.
|
|
158
|
+
|
|
159
|
+
## Symptom → cause
|
|
160
|
+
|
|
161
|
+
| symptom | cause |
|
|
162
|
+
|---|---|
|
|
163
|
+
| HUD scrolls away with the camera | missing `floating = true` on the container |
|
|
164
|
+
| HUD drawn under the game | `this.z = …` instead of `addChild(hud, z)` |
|
|
165
|
+
| hand-rolled button never responds | `isKinematic` left `true` — use `UISpriteElement` |
|
|
166
|
+
| text renders in the wrong font | web font not preloaded as `"fontface"` |
|
|
167
|
+
| `BitmapText` renders nothing | the `.fnt` / image pair was loaded under two different asset names |
|
|
168
|
+
| `TypeError: … .fillText is not a function` | drawn via `getContext()` under a GPU backend |
|
|
169
|
+
| `UIContainer is not defined` | no such class — use `Container` or `UIBaseElement` |
|
|
170
|
+
| UI misplaced after a window resize | no `CANVAS_ONRESIZE` relayout |
|
|
171
|
+
|
|
172
|
+
## Related skills
|
|
173
|
+
|
|
174
|
+
- `melonjs-input` — the `isKinematic` requirement in full
|
|
175
|
+
- `melonjs-renderables` — `floating`, draw order, containers
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/audio/backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,IAAI,EAAU,MAAM,QAAQ,CAAC;AAEtC;;;;;;;;;;GAUG;AACH,eAAO,IAAI,gBAAgB,EAAE,OAAc,CAAC;AAE5C;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAExD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,KAAK;YACH,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,SAAS,CAAC;oBACtB,MAAM,GAAG,IAAI;mBAChB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;eAC1B,MAAM,EAAE;CACzB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAMxD;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAC1B,YAAY,MAAM,EAClB,aAAa,MAAM,IAAI,EACvB,cAAa,OAAc,KACzB,IAqBF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,IAAI,YAAY,GAAG,IAAI,CAmBrD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,IAAI,QAAQ,GAAG,IAAI,CAS/C;AAUD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAKvC;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fetchdata.d.ts","sourceRoot":"","sources":["../../../src/loader/parsers/fetchdata.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,oCALW,MAAM,aACN,MAAM,GACJ,OAAO,CAKnB;AA2DD;;;;;;;;;;;;;;;GAeG;AACH,+BAdW,MAAM,gBACN,MAAM,aACN,MAAM,gBAkDhB"}
|
|
File without changes
|