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-deployment
|
|
3
|
-
description: "Use this skill when packaging, shipping or scaffolding a melonJS game — starting a project with create-melonjs, building for the web, wrapping as a native app with Capacitor or Cordova, publishing to WeChat Mini Games or Facebook Instant Games, browser support and transpiling. Triggers on: create-melonjs, scaffold, new project, build, deploy, publish, Capacitor, Cordova, native app, iOS, Android, WeChat, Instant Games, browser support, transpile, ES2022, bundle size, tree-shaking."
|
|
3
|
+
description: "Use this skill when packaging, shipping or scaffolding a melonJS game — starting a project with create-melonjs, building for the web, wrapping as a native app with Capacitor or Cordova, publishing to WeChat Mini Games or Facebook Instant Games, running with no DOM (nx.js on the Nintendo Switch, a Web Worker), browser support and transpiling. Triggers on: create-melonjs, scaffold, new project, build, deploy, publish, Capacitor, Cordova, native app, iOS, Android, WeChat, Instant Games, nx.js, Nintendo Switch, homebrew, no DOM, Web Worker, OffscreenCanvas, browser support, transpile, ES2022, bundle size, tree-shaking."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -15,9 +15,11 @@ npm create melonjs@latest my-game
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
The project name is required — without it the command prints its usage and
|
|
18
|
-
exits
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
exits — and it names a new directory: an existing one is refused. It downloads
|
|
19
|
+
the melonJS TypeScript boilerplate (Vite, TypeScript and the debug plugin
|
|
20
|
+
already wired in), which saves you the top-level-await and asset-path issues
|
|
21
|
+
that a hand-rolled config usually hits first. Then `npm install` and
|
|
22
|
+
`npm run dev` in that directory.
|
|
21
23
|
|
|
22
24
|
## Adding melonJS to an existing project
|
|
23
25
|
|
|
@@ -69,14 +71,22 @@ Two caveats worth knowing:
|
|
|
69
71
|
|
|
70
72
|
## Native apps
|
|
71
73
|
|
|
72
|
-
**Capacitor** is the current recommendation for iOS and Android
|
|
73
|
-
`@melonjs/capacitor-plugin
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
**Capacitor** is the current recommendation for iOS and Android. A melonJS game
|
|
75
|
+
runs in its WebView unchanged; `@melonjs/capacitor-plugin` adds the native
|
|
76
|
+
lifecycle: the game pauses when the app goes to the background and resumes when
|
|
77
|
+
it comes back (but stays paused if the player had paused it, from 1.0.1), the
|
|
78
|
+
hardware back button reaches the current stage (`bindStageBack`), and
|
|
79
|
+
orientation lock and the splash screen are one call each. The boilerplate's
|
|
80
|
+
Vite build writes to `dist/`, Capacitor's default `webDir`. See
|
|
81
|
+
`melonjs-plugins` for its options. **Cordova** also works and is documented,
|
|
82
|
+
but is the older route.
|
|
83
|
+
|
|
84
|
+
The thing that breaks first under Cordova is asset loading: the app is served
|
|
85
|
+
from `file://`, where `fetch()` cannot read local files. melonJS's loader falls
|
|
86
|
+
back to XHR for that scheme, so engine-loaded assets work — but any `fetch()` in
|
|
87
|
+
*your* game code will fail there. Capacitor serves the app from a local web
|
|
88
|
+
server instead (`https://localhost` on Android, `capacitor://localhost` on
|
|
89
|
+
iOS), so `fetch()` works there.
|
|
80
90
|
|
|
81
91
|
## Mini-game platforms
|
|
82
92
|
|
|
@@ -85,7 +95,67 @@ wiki. Note that `device.platform.isWeixin` detects WeChat's **in-app browser**,
|
|
|
85
95
|
which is a normal WebView. WeChat *Mini Games* are a different runtime with no
|
|
86
96
|
DOM, and need platform-specific adaptation rather than just detection — the one
|
|
87
97
|
hook the engine offers is that the renderer adopts a `globalThis.canvas` when
|
|
88
|
-
the runtime's adapter provides one, instead of creating its own.
|
|
98
|
+
the runtime's adapter provides one, instead of creating its own. What the engine
|
|
99
|
+
does without a DOM is described in the next section.
|
|
100
|
+
|
|
101
|
+
## Runtimes with no DOM: nx.js, Web Workers
|
|
102
|
+
|
|
103
|
+
melonJS (from 20.14) runs where there is no `document`: in a Web Worker, and in
|
|
104
|
+
runtimes that implement the web APIs a game uses but no page around it, such as
|
|
105
|
+
[nx.js](https://github.com/TooTallNate/nx.js), JavaScript homebrew for the
|
|
106
|
+
Nintendo Switch. There is no `<canvas>` to create there, so hand the
|
|
107
|
+
Application the canvas to draw to. On nx.js that is the global `screen`, the
|
|
108
|
+
1280x720 display:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
import { Application, video } from "melonjs";
|
|
112
|
+
|
|
113
|
+
const app = new Application(1280, 720, {
|
|
114
|
+
canvas: screen, // nx.js's display; an OffscreenCanvas in a worker
|
|
115
|
+
renderer: video.AUTO, // WebGL 2 where the runtime has it, else Canvas
|
|
116
|
+
scale: 1,
|
|
117
|
+
});
|
|
118
|
+
await app.init();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
What changes without a DOM:
|
|
122
|
+
|
|
123
|
+
- **Design at the canvas's size.** There is no page to fit, so
|
|
124
|
+
`scale: "auto"` measures nothing and the game is drawn at the scale it was
|
|
125
|
+
given. On the Switch that is 1280x720 (the console scales it when docked).
|
|
126
|
+
- `app.parentElement` is `undefined`: the canvas is the display and is not
|
|
127
|
+
attached to anything. Fullscreen and pointer lock do nothing.
|
|
128
|
+
- **Assets** load through `fetch` and `createImageBitmap` as in a browser; on
|
|
129
|
+
nx.js a relative path resolves inside the app's RomFS. Tiled maps must be
|
|
130
|
+
**JSON** (`.tmj` / `.tsj`): XML needs `DOMParser` (see `melonjs-tilemaps`).
|
|
131
|
+
The `video` and `script` asset types need a DOM and fail.
|
|
132
|
+
- **Input**: touch arrives as `TouchEvent`s on the canvas, and melonJS reports
|
|
133
|
+
them as pointer events as usual. There is no mouse. On nx.js the Joy-Con are
|
|
134
|
+
standard gamepads (`melonjs-input`), and keyboard events only come from a
|
|
135
|
+
USB keyboard.
|
|
136
|
+
- **Audio** needs no gesture to unlock. nx.js cannot say which formats it
|
|
137
|
+
decodes (its `Audio` has no `canPlayType`), so melonJS tries the first
|
|
138
|
+
extension given to `audio.init()`: list one FFmpeg decodes, such as
|
|
139
|
+
`audio.init("ogg,mp3")`. nx.js's Web Audio has no `PannerNode`,
|
|
140
|
+
`OscillatorNode`, `BiquadFilterNode` or effect nodes, so plain playback,
|
|
141
|
+
volume and `stereo()` work, but 3D `position()` / `panner()`, `tone()` /
|
|
142
|
+
`noise()` and the reverb / echo / compressor effects throw there (see
|
|
143
|
+
`melonjs-audio`).
|
|
144
|
+
- **Fonts**: nx.js's `FontFace` only takes the font's bytes, so a `fontface`
|
|
145
|
+
asset (a URL) fails there. Load the bytes yourself and add the face before
|
|
146
|
+
using it in a `Text`. The face is ready once made, and its `load()` throws
|
|
147
|
+
on nx.js, so do not call it:
|
|
148
|
+
`fonts.add(new FontFace("Pixel", await (await fetch("font.ttf")).arrayBuffer()))`.
|
|
149
|
+
- **Save data**: melonJS checks `localStorage` when it loads. On nx.js, if the
|
|
150
|
+
app declares user save data (`userAccountSaveDataSize` in its NACP), that
|
|
151
|
+
first read is when the Switch asks the player to pick a user profile. An app
|
|
152
|
+
that saves nothing should leave that size at 0, so no picker appears.
|
|
153
|
+
- A Web Worker has no `Image`, `Audio` or `AudioContext` of its own: draw
|
|
154
|
+
there, and play sound from the page.
|
|
155
|
+
|
|
156
|
+
The engine's own test, `tests/nxjs-compatibility.spec.js`, runs a game in a
|
|
157
|
+
Web Worker made to look like nx.js. It is the reference for what is expected
|
|
158
|
+
to work, not a test on Switch hardware.
|
|
89
159
|
|
|
90
160
|
## Size
|
|
91
161
|
|
|
@@ -103,11 +173,19 @@ are not decoded into memory.
|
|
|
103
173
|
|---|---|
|
|
104
174
|
| blank page, syntax error in the console | browser older than the ES2022 target |
|
|
105
175
|
| transpiling changes nothing | build config excludes `node_modules` |
|
|
106
|
-
| assets 404 in a packaged
|
|
176
|
+
| assets 404 in a packaged Cordova app | `fetch()` in game code — `file://` needs XHR |
|
|
107
177
|
| a built-in method is missing after transpiling | transpiling is syntax-only; polyfills are separate |
|
|
108
178
|
| bundle much larger than expected | tree-shaking recovers little — subsystems are interconnected |
|
|
179
|
+
| a native app paused from its menu is running again after coming back from the background | capacitor-plugin < 1.0.1 resumed on every return; upgrade |
|
|
180
|
+
| `Cannot read properties of undefined (reading 'body')` from `new Application` in nx.js or a Web Worker | melonJS older than 20.14, which needed a DOM; update |
|
|
181
|
+
| WebGL 2 "could not be created" on nx.js | another context was taken on `screen` first: it holds one context kind, so let the Application be the first to call `getContext` on it |
|
|
182
|
+
| the game is drawn small, or cropped, on nx.js | its resolution is not the screen's 1280x720; with no page, `scale: "auto"` does not fit it |
|
|
183
|
+
| an XML Tiled map fails with "needs DOMParser" | no DOM to parse XML with: export the map and its tilesets as JSON (`.tmj` / `.tsj`) |
|
|
184
|
+
| a `fontface` asset fails on nx.js | nx.js's `FontFace` takes bytes, not a URL: fetch the file and add the face to `fonts` yourself |
|
|
185
|
+
| spatial sound, `tone()` or `noise()` throws "Method not implemented" on nx.js | the node it needs is missing from nx.js's Web Audio; use `stereo()` and recorded sounds |
|
|
109
186
|
|
|
110
187
|
## Related skills
|
|
111
188
|
|
|
112
189
|
- `melonjs` — the companion packages, including the Capacitor plugin
|
|
190
|
+
- `melonjs-plugins` — registering the Capacitor plugin, and its helpers
|
|
113
191
|
- `melonjs-getting-started` — the Application bootstrap the template produces
|
|
@@ -39,8 +39,11 @@ the usual arrangement: author once, scale to whatever you are given.
|
|
|
39
39
|
|
|
40
40
|
## `scale` and `scaleMethod`
|
|
41
41
|
|
|
42
|
-
`scale: "auto"` turns on automatic scaling
|
|
43
|
-
`scaleMethod`
|
|
42
|
+
`scale: "auto"` turns on automatic scaling, and so does naming any
|
|
43
|
+
`scaleMethod` other than `"manual"`, even with no `scale` given:
|
|
44
|
+
`{ scaleMethod: "flex" }` alone scales to its container. A number is a fixed
|
|
45
|
+
multiplier, used only while `scaleMethod` is `"manual"` (the default); with
|
|
46
|
+
automatic scaling on, the number is ignored.
|
|
44
47
|
|
|
45
48
|
`scaleMethod` decides how the available space is used:
|
|
46
49
|
|
|
@@ -54,7 +57,8 @@ the usual arrangement: author once, scale to whatever you are given.
|
|
|
54
57
|
both axes.
|
|
55
58
|
- **`"stretch"`** scales each axis independently, so the aspect ratio is not
|
|
56
59
|
preserved and circles become ovals.
|
|
57
|
-
- **`"manual"
|
|
60
|
+
- **`"manual"`**, the default, does nothing automatic with a numeric `scale`.
|
|
61
|
+
With `scale: "auto"` it scales as `"fit"` does.
|
|
58
62
|
|
|
59
63
|
The distinction that matters: **`fit` scales a fixed frame; everything else
|
|
60
64
|
changes how much of the world is visible.** A game whose layout assumes a
|
|
@@ -381,3 +385,4 @@ const app = new Application(1024, 576, {
|
|
|
381
385
|
| `scaleRounding: "integer"` changes nothing | it applies to `scaleMethod: "fit"` only; the others resize the renderer |
|
|
382
386
|
| text is soft but sprites are crisp | the glyphs were anti-aliased when `Text` baked them; use `BitmapText` (see below) |
|
|
383
387
|
| the game shows more world on a bigger window | that is `fill-*` or `flex-*` doing their job; use `"fit"` to keep a fixed frame |
|
|
388
|
+
| a numeric `scale` (say `2`) is ignored | a `scaleMethod` other than `"manual"` turns automatic scaling on, which ignores the number |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-effects-and-shaders
|
|
3
|
-
description: "Use this skill for post-processing effects, custom shaders, blend modes and colour grading in melonJS. Covers the built-in ShaderEffect presets, addPostEffect on renderables and cameras, writing a custom dual-language GLSL/WGSL effect, the screen_texture builtins, and why effects silently do nothing on the Canvas fallback. Triggers on: Tween, easing, animate an effect, ShaderEffect, addPostEffect, removePostEffect, getPostEffect, postEffectNeedsCapture, VignetteEffect, GlowEffect, BlurEffect, PixelateEffect, ScanlineEffect, shader, GLSL, WGSL, uniform, setUniform, setTexture, setTime, blendMode, colorMatrix, screen_texture, toFrameTexture, post effect, filter."
|
|
3
|
+
description: "Use this skill for post-processing effects, custom shaders, blend modes and colour grading in melonJS. Covers the built-in ShaderEffect presets, addPostEffect on renderables and cameras, writing a custom dual-language GLSL/WGSL effect, the screen_texture builtins, and why effects silently do nothing on the Canvas fallback. Triggers on: Tween, easing, animate an effect, ShaderEffect, addPostEffect, removePostEffect, getPostEffect, postEffectNeedsCapture, VignetteEffect, GlowEffect, BlurEffect, PixelateEffect, ScanlineEffect, OutlineEffect, BloomEffect, ToneMappingEffect, SwayEffect, SquishEffect, hdr, tone mapping, shader, GLSL, WGSL, uniform, setUniform, setTexture, setTime, getShader, prewarm, blendMode, colorMatrix, screen_texture, screen_uv, noise_uv, toFrameTexture, pixelate transition, post effect, filter."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -88,8 +88,9 @@ class Bar extends Renderable {
|
|
|
88
88
|
}
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
`ProgressBar` and `Trail` set it for you, and a sprite needs
|
|
92
|
-
defaults to `false` and only ever changes the single-effect
|
|
91
|
+
`ProgressBar`, `UIPanel` and `Trail` set it for you, and a sprite needs
|
|
92
|
+
nothing: the flag defaults to `false` and only ever changes the single-effect
|
|
93
|
+
case.
|
|
93
94
|
|
|
94
95
|
## Toggle with `enabled`, do not remove
|
|
95
96
|
|
|
@@ -229,7 +230,8 @@ squish.setBulge(1 - scaleY);
|
|
|
229
230
|
```
|
|
230
231
|
|
|
231
232
|
Apply that from the sprite's own `update()`, not from a tween's `onUpdate`. A
|
|
232
|
-
physics adapter that mirrors a body's angle (
|
|
233
|
+
physics adapter that mirrors a body's angle (matter, planck and Rapier do, and so does
|
|
234
|
+
the built-in once a body has an angle or angular velocity) rewrites
|
|
233
235
|
`currentTransform` on every step, before the renderables update, so a scale set
|
|
234
236
|
anywhere else is gone by the time the sprite is drawn. Let the tween animate a
|
|
235
237
|
plain `{ scaleY }` object and read it in `update()`.
|
|
@@ -457,8 +459,10 @@ renderer.setToneMapping("aces", { exposure: 0.4, white: 6 });
|
|
|
457
459
|
renderer.setToneMapping("none"); // and off again
|
|
458
460
|
```
|
|
459
461
|
|
|
460
|
-
`"none"` by default
|
|
461
|
-
|
|
462
|
+
`"none"` by default, and set at boot through the `toneMapping`,
|
|
463
|
+
`toneMappingExposure` and `toneMappingWhite` application settings. The
|
|
464
|
+
renderer keeps the curve LAST on the camera, so whatever you put in front of
|
|
465
|
+
it is mapped along with everything else. Changing
|
|
462
466
|
the mode rebuilds one shader (the curve is baked); `exposure` and `white` are
|
|
463
467
|
uniform writes, so a settings screen can drive them live. `white` is the value
|
|
464
468
|
that comes out as display white — the knob a "brightness" or "peak" slider
|
|
@@ -483,6 +487,13 @@ It is still *implemented* as one: a non-identity matrix makes the camera append
|
|
|
483
487
|
an internal `ColorMatrixEffect` to its post-effect chain for that frame, so it
|
|
484
488
|
needs a GPU backend like everything else here.
|
|
485
489
|
|
|
490
|
+
The `"pixelate"` state transition (`state.transition("pixelate", …)`, or a
|
|
491
|
+
`PixelateTransitionEffect` added with `addCameraEffect`) is another such guest:
|
|
492
|
+
while it runs it puts a `PixelateEffect` FIRST in the camera's chain, so a
|
|
493
|
+
vignette or scanlines you added stay sharp over the blocks, and it takes the
|
|
494
|
+
effect off again when it is done. Being a shader, it does nothing on Canvas,
|
|
495
|
+
where `state.transition` changes the state directly instead.
|
|
496
|
+
|
|
486
497
|
## Custom effects are dual-language
|
|
487
498
|
|
|
488
499
|
A custom `ShaderEffect` supplies a fragment **body**, not a whole program. To run
|
|
@@ -582,8 +593,8 @@ string in your own module:
|
|
|
582
593
|
|
|
583
594
|
```js
|
|
584
595
|
// with the level's other assets, not in the stage that uses them.
|
|
585
|
-
// `preload()`
|
|
586
|
-
//
|
|
596
|
+
// `preload()` or a bundle's `load(id)`, not `load(asset)`: those two warm
|
|
597
|
+
// the shaders they bring in, and run again per level, not just at boot
|
|
587
598
|
await loader.preload([
|
|
588
599
|
{ name: "ripples", type: "shader", src: {
|
|
589
600
|
glsl: "assets/level2/ripples.glsl",
|
|
@@ -596,6 +607,14 @@ await loader.preload([
|
|
|
596
607
|
`src` takes the dual-language pair, so one asset carries both realizations and
|
|
597
608
|
the two backends cannot drift apart. `data` takes inline source the same way.
|
|
598
609
|
|
|
610
|
+
A level's bundle (20.14: `loader.load("level2", [...])` declares and loads
|
|
611
|
+
one, warming like `preload` but with no loading screen) is also the shader's
|
|
612
|
+
lifetime. Unloading the last bundle that lists it, or `loader.unload()` of the
|
|
613
|
+
shader asset itself, destroys the shared instance whatever still draws with
|
|
614
|
+
it, and those renderables then draw without the effect. An effect that
|
|
615
|
+
outlives a level (a HUD's, a camera's) belongs in a bundle that outlives it
|
|
616
|
+
too; see `melonjs-loading-assets`.
|
|
617
|
+
|
|
599
618
|
With `prewarm` on (the default since 20.6), `loader.preload()` warms on its way
|
|
600
619
|
through — automatically, on every call — and compiles the GPU-side objects for
|
|
601
620
|
every shader it holds — on WebGPU that is the
|
|
@@ -632,7 +651,9 @@ variants are covered by the loading-screen warm-up and by the per-renderable one
|
|
|
632
651
|
For refraction, distortion and anything that samples what is already drawn:
|
|
633
652
|
|
|
634
653
|
- **`screen_texture`** — a sampler kept filled with everything drawn so far
|
|
635
|
-
(GLSL: annotate the sampler `uniform sampler2D tex : screen_texture
|
|
654
|
+
(GLSL: annotate the sampler `uniform sampler2D tex : screen_texture;`, or
|
|
655
|
+
`: screen_texture(repeat)` to sample it wrapping; `repeat-x` and
|
|
656
|
+
`repeat-y` work too)
|
|
636
657
|
- **`screen_uv`** — this fragment's 0..1 position in that capture
|
|
637
658
|
- **`noise_uv`** — 0..1 across the sprite regardless of its atlas frame, and
|
|
638
659
|
across the renderable when it has several effects (it does not slide with
|
|
@@ -650,7 +671,11 @@ Two places where a straight port really does differ:
|
|
|
650
671
|
into a `Texture2d` you hand to `setTexture` — the supported replacement for
|
|
651
672
|
`readPixels` tricks. The GL capture is bottom-up (GLSL bodies sample it with
|
|
652
673
|
`1.0 - uv.y`) and opaque RGB; the WebGPU capture is top-down (the WGSL twin
|
|
653
|
-
must *not* flip) and preserves alpha.
|
|
674
|
+
must *not* flip) and preserves alpha. It holds what was drawn when it was
|
|
675
|
+
called, so call it right before the draw that samples it. By default it
|
|
676
|
+
refills one shared, renderer-owned texture; `toFrameTexture({ target: null })`
|
|
677
|
+
hands back a capture of your own, for two alive at once, which you
|
|
678
|
+
`destroy()` when done.
|
|
654
679
|
- **Vertical UV offsets** — a drop shadow, a directional smear. The WebGL pooled
|
|
655
680
|
multi-effect path composites through bottom-up FBOs, so declare a `uUVYDir`
|
|
656
681
|
uniform (initialise it to `1.0`) and multiply vertical offsets by it; the
|
|
@@ -724,10 +749,14 @@ capture plus one composite **per draw**: `difference`, `overlay`, `hard-light`,
|
|
|
724
749
|
accents; expensive for hundreds of objects. The Canvas renderer reaches all
|
|
725
750
|
eight through `globalCompositeOperation` instead, at no extra cost.
|
|
726
751
|
|
|
727
|
-
Anything else collapses to `"normal"
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
752
|
+
Anything else collapses to `"normal"`. `renderer.setBlendMode(mode)` returns
|
|
753
|
+
the mode it actually applied, so comparing that against your request detects
|
|
754
|
+
an unknown name, and `none` on Canvas.
|
|
755
|
+
|
|
756
|
+
Two kinds of draw cannot take the eight composited modes on the GPU backends:
|
|
757
|
+
a 3D mesh and a `Gradient` fill draw them as `"normal"`, with a one-time
|
|
758
|
+
console warning naming which. `setBlendMode` cannot see that one, since it
|
|
759
|
+
happens at the draw, and still reports the mode you asked for.
|
|
731
760
|
|
|
732
761
|
## The Canvas fallback
|
|
733
762
|
|
|
@@ -758,6 +787,7 @@ same question after construction.
|
|
|
758
787
|
|
|
759
788
|
| symptom | cause |
|
|
760
789
|
|---|---|
|
|
790
|
+
| a camera's post effect hides another camera drawn before it | before 20.13 a later camera's pass replaced the whole frame; update. Since then each camera's effects apply to what it draws, over the cameras before it |
|
|
761
791
|
| effect does nothing, warning in console | Canvas fallback — no programmable pipeline |
|
|
762
792
|
| effect does nothing on some machines only | GLSL-only shader, `video.AUTO` chose WebGPU |
|
|
763
793
|
| a white or solid box where the effect should be | carrier renderable drawn while the effect is disabled |
|
|
@@ -766,9 +796,11 @@ same question after construction.
|
|
|
766
796
|
| ported shader renders upside down | a hand-bound `toFrameTexture()` capture — GL is bottom-up, WebGPU top-down |
|
|
767
797
|
| shadow/smear offset flips on some draws | vertical UV offset not multiplied by `uUVYDir` |
|
|
768
798
|
| frame rate collapses with many blended sprites | an *advanced* mode (overlay, darken, …) — each draw is a capture plus a composite |
|
|
799
|
+
| `overlay` (or another composited mode) does nothing on a mesh or a gradient fill, with a console warning | those two draws take the eight composited modes as `"normal"` on the GPU backends |
|
|
800
|
+
| an effect from `loader.getShader()` stops drawing after a level change | its asset, or the last bundle listing it, was unloaded, which destroys the shared instance |
|
|
769
801
|
| many sprites with the same effect still cost a draw each | an effect instance per sprite, or a uniform set to a different value per sprite — share one instance and group sprites by value |
|
|
770
802
|
| animated shader never moves | `setTime` fed milliseconds, or the shader declares no `uTime` |
|
|
771
|
-
| squash and stretch only bulges, the height never changes, under
|
|
803
|
+
| squash and stretch only bulges, the height never changes, under matter, planck or Rapier (or a rotating built-in body) | the adapter rewrites `currentTransform` every step; set the scale from the sprite's `update()` |
|
|
772
804
|
| `SquishEffect` squishes the whole screen instead of the sprite | it is chained with another effect, or on the camera; give the sprite it alone |
|
|
773
805
|
| syntax error in a file whose shader you just edited, pointing at a line of GLSL | a backtick in the shader body ended the template literal — most often one wrapped around an identifier in a comment |
|
|
774
806
|
|
|
@@ -55,7 +55,7 @@ game events in a `.ts` project.
|
|
|
55
55
|
| event | when |
|
|
56
56
|
|---|---|
|
|
57
57
|
| `GAME_BEFORE_UPDATE` / `GAME_UPDATE` / `GAME_AFTER_UPDATE` | around the world update — `GAME_UPDATE` is **not** emitted while paused |
|
|
58
|
-
| `GAME_BEFORE_DRAW` / `GAME_AFTER_DRAW` | around the draw pass |
|
|
58
|
+
| `GAME_BEFORE_DRAW` / `GAME_AFTER_DRAW` | around the draw pass, on frames that redraw: one where no update reported a change is not drawn |
|
|
59
59
|
| `WORLD_STEP` | after the physics step |
|
|
60
60
|
| `TICK` | the raw engine tick |
|
|
61
61
|
|
|
@@ -67,7 +67,7 @@ game events in a `.ts` project.
|
|
|
67
67
|
| `STATE_CHANGE`, `STATE_PAUSE`, `STATE_RESUME`, `STATE_STOP` | state machine |
|
|
68
68
|
| `STAGE_RESET` | a stage was reset |
|
|
69
69
|
| `LEVEL_LOADED` | a Tiled level finished loading |
|
|
70
|
-
| `LOADER_PROGRESS`, `LOADER_COMPLETE`, `LOADER_ERROR` | asset loading |
|
|
70
|
+
| `LOADER_PROGRESS`, `LOADER_COMPLETE`, `LOADER_ERROR` | asset loading: every `loader.preload()`, and a bundle loaded with `loader.load(id)` (20.14); not one asset loaded with `loader.load(descriptor)` |
|
|
71
71
|
| `BLUR` / `FOCUS` | the page lost or regained focus |
|
|
72
72
|
| `CANVAS_ONRESIZE`, `VIEWPORT_ONRESIZE` | relayout hooks |
|
|
73
73
|
| `POINTERMOVE` | global pointer movement |
|
|
@@ -87,7 +87,7 @@ right times and there is nothing to unsubscribe:
|
|
|
87
87
|
| callback | when |
|
|
88
88
|
|---|---|
|
|
89
89
|
| `onActivateEvent()` | `addChild`-ed into a container that is already attached to the world root (a `Container` forwards it to its children) |
|
|
90
|
-
| `onDeactivateEvent()` | removed from its container — including the automatic world clear on a state change |
|
|
90
|
+
| `onDeactivateEvent()` | removed from its container — including the automatic world clear on a state change. Once per `onActivateEvent`: the children of a removed container, at any depth, are told once |
|
|
91
91
|
| `onDestroyEvent()` | `destroy()` ran; called with no arguments from the container-removal path |
|
|
92
92
|
| `onCollisionStart` / `onCollisionActive` / `onCollisionEnd` | contacts |
|
|
93
93
|
|
|
@@ -153,7 +153,10 @@ Use `removeChild()`, or flag the work and drain it from `Stage.update`.
|
|
|
153
153
|
| crash during a stage switch | callback ran mid-teardown — guard with `state.isCurrent` |
|
|
154
154
|
| crash after a collision | `removeChildNow()` inside a contact callback — use `removeChild()` |
|
|
155
155
|
| progress callbacks never fire | assigned `loader.onProgress` (removed in 20.3) instead of subscribing to `LOADER_PROGRESS` |
|
|
156
|
+
| a `LOADER_COMPLETE` handler (a `state.change` to the first screen) runs again mid-game | a later `preload()`, or a level's bundle loaded with `loader.load(id)`, emits it too; subscribe with `event.once`, or use `preload()`'s promise or callback |
|
|
156
157
|
| `onActivateEvent` never runs | the child was added to a container not yet attached to the world |
|
|
158
|
+
| `onDeactivateEvent` runs twice when a group is removed (a count goes negative, a light or listener is released twice) | the group's `onDeactivateEvent` and its `destroy()` both reached the children, before 20.13.0 — now once per `onActivateEvent` |
|
|
159
|
+
| `onDeactivateEvent` never runs on a child of a group built off-world and destroyed | it never had `onActivateEvent` either: the pair is only sent to children that entered the world |
|
|
157
160
|
|
|
158
161
|
## Related skills
|
|
159
162
|
|
|
@@ -137,7 +137,6 @@ with the formats you ship before any audio asset is preloaded.
|
|
|
137
137
|
```js
|
|
138
138
|
import { audio, loader } from "melonjs";
|
|
139
139
|
|
|
140
|
-
loader.setOptions({ crossOrigin: "anonymous" }); // needed for remote assets
|
|
141
140
|
audio.init("mp3,ogg"); // BEFORE preloading audio
|
|
142
141
|
await loader.preload([
|
|
143
142
|
{ name: "tileset", type: "image", src: "data/img/tileset.png" },
|
|
@@ -145,6 +144,11 @@ await loader.preload([
|
|
|
145
144
|
]);
|
|
146
145
|
```
|
|
147
146
|
|
|
147
|
+
A flat list suits a single-screen game. A game with several levels exports a
|
|
148
|
+
record of bundles instead — `{ common: [...], level1: [...], level2: [...] }`,
|
|
149
|
+
passed to the same `loader.preload()` — so each level's assets can later be
|
|
150
|
+
freed and loaded again by id; see `melonjs-loading-assets`.
|
|
151
|
+
|
|
148
152
|
Two ordering rules that produce confusing failures when missed:
|
|
149
153
|
|
|
150
154
|
- **`audio.init()` must run before any audio asset is preloaded**, and names the
|
|
@@ -173,7 +177,7 @@ placement.
|
|
|
173
177
|
| scene content appears once and never resets | scene built in the constructor instead of `onResetEvent` |
|
|
174
178
|
| everything offset by half a sprite | top-left math against the centred `anchorPoint` default |
|
|
175
179
|
| a `Tween` or `ParticleEmitter` does nothing | `.start()` / `streamParticles()` never called |
|
|
176
|
-
|
|
|
180
|
+
| an asset on another origin fails to load, the error naming it | that server sends no `Access-Control-Allow-Origin` header (see melonjs-loading-assets) |
|
|
177
181
|
|
|
178
182
|
## Related skills
|
|
179
183
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-input
|
|
3
|
-
description: "Use this skill for keyboard, pointer, mouse, touch and gamepad input in melonJS. Covers the bindKey action indirection, registerPointerEvent on regions, the isKinematic requirement that silently blocks pointer events, which of several overlapping objects receives an event and how to consume it, world vs screen pointer coordinates, and gamepad mapping onto the same actions. Triggers on: input, bindKey, isKeyPressed, KEY, registerPointerEvent, releasePointerEvent, pointerdown, pointerup, pointermove, POINTERMOVE, gamepad, bindGamepad, triggerKeyEvent, virtual gamepad, touch controls, joystick, gamepad-plugin, KEYDOWN, KEYUP, press any key, splash screen, click, touch, drag, hit test, event propagation, stop propagating, overlapping, which object gets the click."
|
|
3
|
+
description: "Use this skill for keyboard, pointer, mouse, touch and gamepad input in melonJS. Covers the bindKey action indirection, registerPointerEvent on regions, the isKinematic requirement that silently blocks pointer events, which of several overlapping objects receives an event and how to consume it, world vs screen pointer coordinates, and gamepad mapping onto the same actions. Triggers on: input, bindKey, isKeyPressed, KEY, bindPointer, mouse button, vibrateGamepad, rumble, activeGamepad, registerPointerEvent, releasePointerEvent, pointerdown, pointerup, pointermove, POINTERMOVE, gamepad, bindGamepad, triggerKeyEvent, virtual gamepad, touch controls, joystick, gamepad-plugin, floating stick, floating button, floating joystick, two thumbs, tap to fire, hideAtRest, mobile controls, KEYDOWN, KEYUP, press any key, splash screen, click, touch, drag, hit test, event propagation, stop propagating, overlapping, which object gets the click."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -54,12 +54,37 @@ narrows it; it also repeats while the key is held, so guard anything that must
|
|
|
54
54
|
happen once. Keep polling for movement and for a menu cursor, where one step
|
|
55
55
|
per press and a key held across a state change are the things that matter.
|
|
56
56
|
|
|
57
|
+
When the window loses the focus or the page is hidden, every key and
|
|
58
|
+
`bindPointer` button still held is released, since its real release goes to
|
|
59
|
+
another window: polling sees it up when the player comes back, and a key still
|
|
60
|
+
held then is pressed again by its next `keydown`. No `KEYUP` is sent for it,
|
|
61
|
+
so code moved by `KEYDOWN` / `KEYUP` rather than by polling should also listen
|
|
62
|
+
to `event.BLUR`. A gamepad's presses are left to the gamepad, which is read
|
|
63
|
+
every frame.
|
|
64
|
+
|
|
57
65
|
The full signature is `bindKey(keyCode, action, lock?, preventDefault?)`;
|
|
58
66
|
`preventDefault` defaults to `input.preventDefault` (itself `true`). Pass the
|
|
59
67
|
fourth argument per binding, or change the global default with
|
|
60
68
|
`input.setPreventDefault(false)` — **assigning `input.preventDefault = false`
|
|
61
69
|
does not work**, it is a read-only module binding.
|
|
62
70
|
|
|
71
|
+
### A mouse button as a key
|
|
72
|
+
|
|
73
|
+
`bindPointer` makes a mouse button press a bound key, so a click fires the same
|
|
74
|
+
action and `isKeyPressed` sees it:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
input.bindKey(input.KEY.X, "fire");
|
|
78
|
+
input.bindPointer(input.KEY.X); // the left button
|
|
79
|
+
input.bindPointer(input.pointer.RIGHT, input.KEY.X); // or another button
|
|
80
|
+
input.unbindPointer(input.pointer.RIGHT); // LEFT when omitted
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
As with `bindGamepad`, the key needs its `bindKey` first, or `bindPointer`
|
|
84
|
+
throws `no action defined for keycode N`. Turning the wheel neither presses
|
|
85
|
+
nor releases a bound button's key (before 20.14 it released the left
|
|
86
|
+
button's).
|
|
87
|
+
|
|
63
88
|
## Pointer events need `isKinematic = false`
|
|
64
89
|
|
|
65
90
|
**This is the trap.** `Renderable.isKinematic` defaults to `true`, and both the
|
|
@@ -82,9 +107,11 @@ class Button extends Renderable {
|
|
|
82
107
|
```
|
|
83
108
|
|
|
84
109
|
`Container`, `Camera2d`, `Draggable`, `DropTarget` and the `UI*` elements set it
|
|
85
|
-
to `false` for you, and so does
|
|
86
|
-
|
|
87
|
-
|
|
110
|
+
to `false` for you, and so does giving it a physics body (a `bodyDef` on any
|
|
111
|
+
adapter, or a `Body`) — so plain `Renderable` and `Sprite` subclasses with no
|
|
112
|
+
body are the ones that catch people. Before melonJS 20.14, matter-adapter
|
|
113
|
+
1.6.0 and planck-adapter 1.7.0, a `bodyDef` under matter or planck left
|
|
114
|
+
`isKinematic` at `true`, so set it yourself there.
|
|
88
115
|
|
|
89
116
|
Register in `onActivateEvent` and release in `onDeactivateEvent`, not in the
|
|
90
117
|
constructor — pooled objects are reused and would otherwise accumulate handlers.
|
|
@@ -103,6 +130,11 @@ throw `invalid event type`. melonJS maps the canonical name onto whichever
|
|
|
103
130
|
mouse/touch events the device actually supports, so you always register the
|
|
104
131
|
pointer name.
|
|
105
132
|
|
|
133
|
+
A region can listen to `"pointerup"` alone, to act on release: it gets the
|
|
134
|
+
release of a press made on it. With several fingers on one region, each finger
|
|
135
|
+
that pressed it gets its own `pointerup` / `pointercancel`; `pointerenter` and
|
|
136
|
+
`pointerleave` still follow one pointer per region.
|
|
137
|
+
|
|
106
138
|
## Which object gets the event
|
|
107
139
|
|
|
108
140
|
The dispatcher collects the regions near the pointer from the broadphase,
|
|
@@ -182,9 +214,10 @@ globalThis.addEventListener("pointerup", onRelease);
|
|
|
182
214
|
|
|
183
215
|
For plain drag-and-drop, `Draggable` / `DropTarget` already wire all of this up
|
|
184
216
|
(`pointerdown` / `pointerup` / `pointercancel` regions plus a global
|
|
185
|
-
`POINTERMOVE` listener) and emit `event.DRAGSTART` / `event.DRAGEND
|
|
186
|
-
|
|
187
|
-
|
|
217
|
+
`POINTERMOVE` listener) and emit `event.DRAGSTART` / `event.DRAGEND`, and
|
|
218
|
+
follow the pointer under any camera, scrolled or zoomed. `DRAGEND` still
|
|
219
|
+
originates from a region handler, so it only fires when the release lands on
|
|
220
|
+
the draggable itself.
|
|
188
221
|
|
|
189
222
|
## Gamepad
|
|
190
223
|
|
|
@@ -252,10 +285,10 @@ input.hasGamepadVibration(index); // for an options-screen toggle
|
|
|
252
285
|
|
|
253
286
|
Safe to call blind: no pad, no motors, or no Gamepad API at all resolves
|
|
254
287
|
`false` and does nothing, so game code never branches on support. Magnitudes
|
|
255
|
-
default to **full** — the underlying web API defaults
|
|
256
|
-
which makes a bare `playEffect` a silent no-op. A
|
|
257
|
-
first on the same pad, so a rapid series of hits
|
|
258
|
-
hits rather than as a queue. For a SUSTAINED rumble issue one effect with a
|
|
288
|
+
default to **full** and `duration` to 200 ms — the underlying web API defaults
|
|
289
|
+
every parameter to zero, which makes a bare `playEffect` a silent no-op. A
|
|
290
|
+
second call preempts the first on the same pad, so a rapid series of hits
|
|
291
|
+
reads as a rapid series of hits rather than as a queue. For a SUSTAINED rumble issue one effect with a
|
|
259
292
|
long `duration` instead of calling every frame.
|
|
260
293
|
|
|
261
294
|
Hot-plugging needs no polling of your own — the engine listens for the browser's
|
|
@@ -270,12 +303,15 @@ event.on(event.GAMEPAD_DISCONNECTED, (gamepad) => { /* pause, show a prompt */ }
|
|
|
270
303
|
|
|
271
304
|
Bindings survive a disconnect: they are attached to the index, not to the
|
|
272
305
|
device, so a pad unplugged and plugged back into the same slot keeps working.
|
|
306
|
+
A pad unplugged with a button or the stick held lets go of the keys bound to
|
|
307
|
+
them, as releasing them would.
|
|
273
308
|
|
|
274
309
|
## Virtual controls
|
|
275
310
|
|
|
276
|
-
For on-screen touch controls, use `@melonjs/gamepad-plugin`
|
|
277
|
-
writing them: sticks and buttons drawn over the
|
|
278
|
-
a gamepad slot, so the `bindGamepad` bindings
|
|
311
|
+
For on-screen touch controls, use `@melonjs/gamepad-plugin` (it needs melonJS
|
|
312
|
+
20.12 or later) rather than writing them: sticks and buttons drawn over the
|
|
313
|
+
game and reported as a pad in a gamepad slot, so the `bindGamepad` bindings
|
|
314
|
+
above answer to them unchanged.
|
|
279
315
|
|
|
280
316
|
```js
|
|
281
317
|
import { GamepadPlugin } from "@melonjs/gamepad-plugin";
|
|
@@ -293,6 +329,45 @@ its own, a touch starting on the controls does not reach the game, and they
|
|
|
293
329
|
hide while a real pad is in use. `plugin.cache.gamepad.visible` says whether
|
|
294
330
|
they show, to word a prompt ("tap" or a key name).
|
|
295
331
|
|
|
332
|
+
Sticks and buttons are fixed in a corner by default. **`floating: true`**
|
|
333
|
+
puts one under the finger wherever it lands inside its `area` (`"half"`, its
|
|
334
|
+
own half of the screen, by default; `"screen"`; or `{ x, y, width, height }`),
|
|
335
|
+
and **`hideAtRest: true`** draws it only while held. The usual mobile
|
|
336
|
+
two-thumb layout, nothing on screen until touched (1.1):
|
|
337
|
+
|
|
338
|
+
```js
|
|
339
|
+
plugin.register(GamepadPlugin, "gamepad", {
|
|
340
|
+
sticks: [{ floating: true, hideAtRest: true }], // left half
|
|
341
|
+
buttons: [{ floating: true, hideAtRest: true, label: "A" }], // right half
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Or one thumb for both: a stick over the whole screen whose quick tap (lifted
|
|
346
|
+
within `tap.time`, 250 ms, without leaning to `dpadThreshold`) presses a key
|
|
347
|
+
or button for one frame, and no button at all:
|
|
348
|
+
|
|
349
|
+
```js
|
|
350
|
+
plugin.register(GamepadPlugin, "gamepad", {
|
|
351
|
+
sticks: [{ floating: true, area: "screen", hideAtRest: true,
|
|
352
|
+
tap: { key: input.KEY.SPACE } }],
|
|
353
|
+
buttons: 0,
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Where areas overlap, a finger takes the first free control there: fixed
|
|
358
|
+
buttons, fixed sticks, floating buttons, floating sticks. So a floating button
|
|
359
|
+
on its `"half"` carves the right half out of a `"screen"` stick, and several
|
|
360
|
+
floating buttons sharing an area split it by distance to where each rests.
|
|
361
|
+
Every press lasts at least one frame, so a 15 ms thumb tap still reaches
|
|
362
|
+
`isKeyPressed`, and Safari's pinch gesture is cancelled while the controls
|
|
363
|
+
show, so a second thumb works on iOS.
|
|
364
|
+
|
|
365
|
+
To match the game's artwork (1.2), pass an already-loaded `TextureAtlas` as
|
|
366
|
+
`skin` to `GamepadPlugin`, with `base` / `thumb` region names on sticks
|
|
367
|
+
and `region` / optional `pressed` names on buttons. Omitted parts keep
|
|
368
|
+
their shapes. Regions scale to the controls' sizes without changing hit areas;
|
|
369
|
+
labels, floating controls, opacity, hiding and dimming keep working.
|
|
370
|
+
|
|
296
371
|
A single key can also be pressed directly, reaching the same actions:
|
|
297
372
|
|
|
298
373
|
```js
|
|
@@ -305,17 +380,28 @@ input.triggerKeyEvent(input.KEY.LEFT, false); // release
|
|
|
305
380
|
| symptom | cause |
|
|
306
381
|
|---|---|
|
|
307
382
|
| pointer handler never fires | `isKinematic` left at its `true` default |
|
|
383
|
+
| touches never arrive where there is no DOM (nx.js, a Web Worker) | melonJS older than 20.14 listened for mouse events there; update. Touch is then read from `TouchEvent`s dispatched on the canvas given as `settings.canvas` |
|
|
308
384
|
| handlers fire twice, or on dead objects | registered in the constructor instead of `onActivateEvent` |
|
|
309
385
|
| camera drag jumps around | using world coordinates instead of `gameScreenX`/`gameScreenY` |
|
|
310
386
|
| `gameX` holds screen coords where world coords were expected | read on `event.POINTERMOVE`, or on a floating region — use `gameWorldX`/`gameWorldY` |
|
|
311
387
|
| drag stops when leaving the object | region events end at the region — use `event.POINTERMOVE` |
|
|
312
388
|
| release never detected outside the region | no global pointer-up event — add a raw listener |
|
|
313
389
|
| jump fires every frame while held | `bindKey` without its third `lock` argument |
|
|
390
|
+
| the hero keeps walking after an Alt-Tab, until the key is pressed again (before 20.14) | the key's release went to another window: update, held keys are now released on blur |
|
|
391
|
+
| a region with only a `"pointerup"` callback never gets it, or the first finger's release is lost with two on one region (before 20.14) | the release was only delivered to the last pointer to press the region: update |
|
|
392
|
+
| a key bound with `bindPointer` never fires once the mouse has entered the canvas (before 20.12) | the pointer's button bindings were replaced on each event: update |
|
|
393
|
+
| firing (a key bound with `bindPointer`) stops while the button is held and the wheel turns (before 20.14) | a wheel event released the main button's key: update |
|
|
394
|
+
| clicks land off target after browser zoom or a move to another screen (before 20.14) | the pointer was scaled by the live pixel ratio against a canvas sized with the old one: update |
|
|
395
|
+
| a `Draggable` jumps away when grabbed, and stays stuck dragging, once the camera has moved (before 20.14) | it mixed screen and world coordinates: update |
|
|
314
396
|
| `invalid event type` thrown at registration | non-pointer name (`"click"`, `"mousedown"`, …) passed to `registerPointerEvent` |
|
|
315
|
-
| `no action defined for keycode N` | `bindGamepad` called before `bindKey` for that keycode |
|
|
397
|
+
| `no action defined for keycode N` | `bindGamepad` or `bindPointer` called before `bindKey` for that keycode |
|
|
398
|
+
| floating stick ignores the right half of the screen | its `area` defaults to `"half"`, its own side — use `area: "screen"` |
|
|
399
|
+
| `area: "screen"` stick still ignores part of the screen | a floating button's area comes first there — the overlap order is fixed buttons, fixed sticks, floating buttons, floating sticks |
|
|
400
|
+
| a quick tap on a stick moves instead of acting | it leaned past `dpadThreshold` or outlasted `tap.time` before lifting |
|
|
316
401
|
| stick has to be pushed well past its `threshold` before it registers | `threshold` is ADDED to the deadzone, not used instead of it |
|
|
317
402
|
| one half of a stick does nothing | each half is a separate `bindGamepad`, chosen by the sign of `threshold` |
|
|
318
|
-
| a
|
|
403
|
+
| a key stays pressed after its pad is unplugged, or a stick bound on one side holds its key after coming back to the centre (before 20.12) | update: an unplugged pad lets go of what it held, and a stick lets go when it returns to rest, even just past the centre |
|
|
404
|
+
| a widget under an overlapping panel still lights up | the panel consumes no hover: return `false` from its `onOver` (presses already stop at UI elements; `UIPanel` with `blocking` does both) |
|
|
319
405
|
| a widget stays highlighted after the pointer moves onto a panel over it | the panel consumes no `pointermove`, so nothing takes the pointer off the widget |
|
|
320
406
|
| a child's big `z` does not put it in front of another container | `pos.z` is container-local — use `ancestor.moveToTop()` on the container |
|
|
321
407
|
| every click in the game stops working | a handler on `app.viewport` returning `false`, which is asked first |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: melonjs-lighting
|
|
3
|
-
description: "Use this skill for 2D dynamic lighting in melonJS — Light2d, ambient light on a Stage, multiple lights, and per-pixel normal-mapped sprites authored in SpriteIlluminator. Covers specular highlights via shininess, the ambient-cutout model, which parts need a GPU backend, and why Light2d does not survive a Camera3d. Triggers on: Light2d, specular, shininess, highlight, glint, lighting, ambientLight, ambientLightingColor, normalMap, SpriteIlluminator, per-pixel lighting, drawLight, torch, glow, darkness."
|
|
3
|
+
description: "Use this skill for 2D dynamic lighting in melonJS — Light2d, ambient light on a Stage, multiple lights, and per-pixel normal-mapped sprites authored in SpriteIlluminator. Covers specular highlights via shininess, the ambient-cutout model, which parts need a GPU backend, and why Light2d does not survive a Camera3d. Triggers on: Light2d, softness, soft light, lightHeight, setRadii, specular, shininess, highlight, glint, lighting, ambientLight, ambientLightingColor, normalMap, SpriteIlluminator, per-pixel lighting, drawLight, torch, glow, darkness."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -31,6 +31,11 @@ intensity = 0.7)`, and `pos` is the light's **centre** — its `anchorPoint` is
|
|
|
31
31
|
`(0.5, 0.5)`, and `centerX` / `centerY` are overridden to return `pos` directly.
|
|
32
32
|
Give `radiusY` a different value for a stretched, elliptical light.
|
|
33
33
|
|
|
34
|
+
Resize a light with `light.setRadii(radiusX, radiusY)`, which resizes its
|
|
35
|
+
bounds too. Writing `radiusX` alone changes nothing you can see: the glow, the
|
|
36
|
+
hole in the darkness and the normal-map shading are all sized from the bounds,
|
|
37
|
+
on every backend.
|
|
38
|
+
|
|
34
39
|
A `Light2d` added to the world **registers itself with the stage's ambient
|
|
35
40
|
overlay on activate**, so you add it like any other renderable and it takes part
|
|
36
41
|
in the lighting pass. It draws additively (`blendMode = "lighter"`); set
|
|
@@ -105,6 +110,11 @@ const hero = new Sprite(x, y, {
|
|
|
105
110
|
canvas, `ImageBitmap`, a `Texture2d`). Assigning anything else — including an
|
|
106
111
|
`HTMLVideoElement`, which would freeze on frame 0 — throws a `TypeError`.
|
|
107
112
|
|
|
113
|
+
How grazing the light is comes from `light.lightHeight`, its height above the
|
|
114
|
+
sprite plane in the same units as its radius (default `max(radiusX, radiusY) *
|
|
115
|
+
0.075`). Lower it for light raking across the surface, which shows the relief
|
|
116
|
+
off; raise it for flatter, more even shading.
|
|
117
|
+
|
|
108
118
|
Normal maps are typically authored in **SpriteIlluminator**. The sidecar pattern
|
|
109
119
|
above — one `_n` image beside each diffuse image — is the simplest setup, but an
|
|
110
120
|
atlas can carry one too: `new TextureAtlas(json, image, { normalMap })` pairs a
|
|
@@ -209,6 +219,8 @@ See the `melonjs-3d` skill.
|
|
|
209
219
|
| sprite not shaded by a light, only glowed over | no `normalMap` on the sprite, or the Canvas backend is active |
|
|
210
220
|
| normal-mapped sprite is pure black where unlit | `Stage.ambientLightingColor` left at black |
|
|
211
221
|
| the 33rd light shades nothing | `MAX_LIGHTS = 32` for the normal-map pipeline |
|
|
222
|
+
| changing `radiusX` / `radiusY` does nothing | everything is sized from the bounds, which `setRadii()` updates and a bare field write does not |
|
|
223
|
+
| normal-mapped relief looks flat under a light | `lightHeight` is high relative to the radius; lower it for a grazing light |
|
|
212
224
|
| `TypeError` assigning `normalMap` | not an image-like value (a video element is rejected outright) |
|
|
213
225
|
| lighting misaligned under a 3D camera | the 2D light pass is not projection-aware — use `Light3d` |
|
|
214
226
|
|