like2d 2.7.4 → 2.9.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 +34 -35
- package/dist/core/audio.d.ts +12 -9
- package/dist/core/audio.d.ts.map +1 -1
- package/dist/core/audio.js +7 -4
- package/dist/core/canvas.d.ts +58 -0
- package/dist/core/canvas.d.ts.map +1 -0
- package/dist/core/canvas.js +209 -0
- package/dist/core/events.d.ts +92 -7
- package/dist/core/events.d.ts.map +1 -1
- package/dist/core/events.js +20 -0
- package/dist/core/gamepad-mapping.d.ts +57 -18
- package/dist/core/gamepad-mapping.d.ts.map +1 -1
- package/dist/core/gamepad-mapping.js +23 -223
- package/dist/core/gamepad.d.ts +34 -58
- package/dist/core/gamepad.d.ts.map +1 -1
- package/dist/core/gamepad.js +79 -213
- package/dist/core/graphics.d.ts +175 -64
- package/dist/core/graphics.d.ts.map +1 -1
- package/dist/core/graphics.js +294 -198
- package/dist/core/input-state.d.ts +2 -2
- package/dist/core/input-state.d.ts.map +1 -1
- package/dist/core/input.d.ts +22 -15
- package/dist/core/input.d.ts.map +1 -1
- package/dist/core/input.js +25 -37
- package/dist/core/keyboard.d.ts +7 -7
- package/dist/core/keyboard.d.ts.map +1 -1
- package/dist/core/keyboard.js +24 -31
- package/dist/core/like.d.ts +98 -44
- package/dist/core/like.d.ts.map +1 -1
- package/dist/core/like.js +8 -0
- package/dist/core/mouse.d.ts +45 -22
- package/dist/core/mouse.d.ts.map +1 -1
- package/dist/core/mouse.js +90 -78
- package/dist/core/timer.d.ts +2 -5
- package/dist/core/timer.d.ts.map +1 -1
- package/dist/core/timer.js +2 -14
- package/dist/engine.d.ts +61 -11
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +119 -102
- package/dist/index.d.ts +42 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -14
- package/dist/math/index.d.ts +2 -0
- package/dist/math/index.d.ts.map +1 -0
- package/dist/math/index.js +1 -0
- package/dist/math/rect.d.ts +71 -0
- package/dist/math/rect.d.ts.map +1 -0
- package/dist/{core → math}/rect.js +8 -0
- package/dist/math/vector2.d.ts +78 -0
- package/dist/math/vector2.d.ts.map +1 -0
- package/dist/{core → math}/vector2.js +24 -0
- package/dist/prefab-scenes/index.d.ts +7 -0
- package/dist/prefab-scenes/index.d.ts.map +1 -0
- package/dist/prefab-scenes/index.js +6 -0
- package/dist/prefab-scenes/startScreen.d.ts +59 -0
- package/dist/prefab-scenes/startScreen.d.ts.map +1 -0
- package/dist/{scenes/startup.js → prefab-scenes/startScreen.js} +47 -7
- package/dist/scene.d.ts +103 -5
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +28 -1
- package/package.json +18 -2
- package/dist/core/canvas-config.d.ts +0 -22
- package/dist/core/canvas-config.d.ts.map +0 -1
- package/dist/core/canvas-config.js +0 -14
- package/dist/core/canvas-manager.d.ts +0 -25
- package/dist/core/canvas-manager.d.ts.map +0 -1
- package/dist/core/canvas-manager.js +0 -178
- package/dist/core/gamepad-buttons.d.ts +0 -23
- package/dist/core/gamepad-buttons.d.ts.map +0 -1
- package/dist/core/gamepad-buttons.js +0 -36
- package/dist/core/rect.d.ts +0 -26
- package/dist/core/rect.d.ts.map +0 -1
- package/dist/core/vector2.d.ts +0 -26
- package/dist/core/vector2.d.ts.map +0 -1
- package/dist/gamecontrollerdb.txt +0 -2221
- package/dist/scenes/startup.d.ts +0 -18
- package/dist/scenes/startup.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# LÏKE2D
|
|
2
2
|
|
|
3
|
-
<svg
|
|
3
|
+
<svg version="1.1" viewBox="0 0 300 105" xmlns="http://www.w3.org/2000/svg">
|
|
4
4
|
<rect x="10" y="11.23" width="280" height="83.544" fill="#e48080" stroke="#000" stroke-linejoin="round" stroke-width="2"/>
|
|
5
5
|
<g fill="none" stroke="#000" stroke-linejoin="round">
|
|
6
6
|
<rect x="97.484" y="11.23" width="52.516" height="46.237"/>
|
|
@@ -31,37 +31,44 @@
|
|
|
31
31
|
</g>
|
|
32
32
|
</svg>
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Lightweight Web framework inspired by [LÖVE](https://love2d.org/).
|
|
35
35
|
|
|
36
|
+
## <div style="color:red">During v2.x.x, LIKE's API will change.</div>
|
|
36
37
|
## What it is
|
|
37
38
|
|
|
38
|
-
LIKE is a
|
|
39
|
+
LIKE is a cozy way to make 2d games for browser.
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
## What LIKE does
|
|
41
42
|
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
-
|
|
43
|
+
- **🔥 Fire-and-forget Assets:** Graphics and audio that pretend to be synchronous.
|
|
44
|
+
- **🎯 DWIM graphics:** Turns repetitive draw calls into one while removing state bleed for properties like `lineCap`.
|
|
45
|
+
- **↔️ Two Canvas Modes:**
|
|
46
|
+
- 🖊️ Audio-resize the canvas; sharp at any resolution.
|
|
47
|
+
- 👾 For retro-style developers, pixels stay crisp but smooth via prescaling.
|
|
48
|
+
- **⭕ Easier Geometry:** `Vector2` and `Rect` are just number tuples (arrays), but a pure-functional library makes them easy to work with and plays nice with `map` and `reduce`.
|
|
49
|
+
- **🚲 Easy Input:** Keyboard, Mouse, and Gamepad all are given both event-based and tracking-based options. Choose what fits your architecture.
|
|
50
|
+
- **👟 Consistent APIs:** Colors 0-1, not 0-255. Seconds, not milliseconds.
|
|
51
|
+
- **👉 Actions System:** An input layer maps inputs to actions, which fire usable events.
|
|
52
|
+
- **🌎 Global control:** Choose how to handle LIKE events, and manage resources with centralized trackers. LIKE is a great foundation for your own engine.
|
|
53
|
+
- **🐦 Light and Elegant:** Zero dependencies and less than 5000 lines of code -- focused entirely on what matters.
|
|
53
54
|
|
|
54
55
|
## Installation
|
|
55
56
|
|
|
57
|
+
Most package managers will work.
|
|
56
58
|
```bash
|
|
57
59
|
npm install like2d
|
|
58
|
-
# or
|
|
59
|
-
|
|
60
|
+
# or ...
|
|
61
|
+
deno add jsr:@like2d/like
|
|
62
|
+
# or...
|
|
60
63
|
```
|
|
61
64
|
|
|
62
65
|
## Quick Start
|
|
63
66
|
|
|
64
|
-
|
|
67
|
+
To try Like2D quickly, use this starter with
|
|
68
|
+
hot reloading and a basic webpage.
|
|
69
|
+
```bash
|
|
70
|
+
npx degit 44100hertz/Like2D/examples/starter my-game
|
|
71
|
+
```
|
|
65
72
|
|
|
66
73
|
## Usage Example
|
|
67
74
|
|
|
@@ -71,8 +78,8 @@ import { createLike } from 'like2d';
|
|
|
71
78
|
const like = createLike(document.body);
|
|
72
79
|
|
|
73
80
|
like.load = () => {
|
|
74
|
-
like.setMode(
|
|
75
|
-
like.input.
|
|
81
|
+
like.setMode([800, 600]);
|
|
82
|
+
like.input.setAction('jump', ['Space', 'ButtonBottom']);
|
|
76
83
|
};
|
|
77
84
|
|
|
78
85
|
like.update = (dt) => {
|
|
@@ -93,27 +100,19 @@ await like.start();
|
|
|
93
100
|
## For Love2D Developers
|
|
94
101
|
|
|
95
102
|
LIKE's API is not the same as LOVE, but similar in spirit. Notable differences:
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
- Theres an actions system -- `input.map` / `actionpressed` and `actionreleased` callbacks.
|
|
103
|
+
- Draw your graphics in one call, that's all. No setup or state bleed.
|
|
104
|
+
- You manage your own instance of like in a big friendly object. This allows us to have multiple games on one page.
|
|
105
|
+
- We use Vector2 and Rect tuples (like `[x, y]`) instead of loose coordinates.
|
|
106
|
+
- Theres an actions system -- `input.setAction` / `actionpressed` and `actionreleased` callbacks.
|
|
101
107
|
- Some things are missing either due to browser limitations or smaller scope.
|
|
102
108
|
|
|
103
109
|
## Feedback welcome
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
1. Make sure you're on the latest release.
|
|
107
|
-
2. If the issue exists already, just comment on that one.
|
|
108
|
-
3. See if it happens in other web browsers.
|
|
111
|
+
[LIKE is on GitHub.](https://github.com/44100hertz/Like2D)
|
|
109
112
|
|
|
110
|
-
|
|
111
|
-
1. ask: Would it make sense as a core feature, or should it be an external library?
|
|
112
|
-
2. See if the feature exists already.
|
|
113
|
-
3. Think about the demand for it overall. Make your case why game developers want it.
|
|
114
|
-
4. Consider just making a PR yourself, or sending a prompt/spec that an AI can hack on.
|
|
113
|
+
[Check out the docs for our long-term vision.](https://github.com/44100hertz/Like2D/tree/master/docs)
|
|
115
114
|
|
|
116
|
-
[
|
|
115
|
+
[Feature requests welcome. PRs discouraged for now.](https://github.com/44100hertz/Like2D/issues/)
|
|
117
116
|
|
|
118
117
|
## License
|
|
119
118
|
|
package/dist/core/audio.d.ts
CHANGED
|
@@ -7,26 +7,26 @@
|
|
|
7
7
|
* Start, stop, or set global volume for every currently playing sound.
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
export type
|
|
10
|
+
export type AudioSourceOptions = {
|
|
11
11
|
volume?: number;
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
|
-
* Handle to a loaded audio resource.
|
|
14
|
+
* Handle to a loaded audio resource, which pretends to be synchronous.
|
|
15
15
|
* Use `play()`, `stop()`, `pause()`, `resume()` for playback control.
|
|
16
16
|
* Access the underlying HTMLAudioElement via `source.audio` for looping,
|
|
17
17
|
* pitch, etc. Note: Use `source.setVolume()` instead of setting
|
|
18
18
|
* `source.audio.volume` directly to ensure global volume scaling works correctly.
|
|
19
19
|
*/
|
|
20
|
-
export declare class
|
|
20
|
+
export declare class AudioSource {
|
|
21
21
|
readonly path: string;
|
|
22
22
|
/** Underlying HTMLAudioElement. Modify directly for looping, pitch, etc. Use methods for playback control. Avoid setting volume directly. */
|
|
23
23
|
readonly audio: HTMLAudioElement;
|
|
24
|
-
readonly options: Required<
|
|
24
|
+
readonly options: Required<AudioSourceOptions>;
|
|
25
25
|
/** Resolves when the audio is loaded and ready to play. */
|
|
26
26
|
readonly ready: Promise<void>;
|
|
27
27
|
private loadState;
|
|
28
28
|
private audioRef;
|
|
29
|
-
constructor(path: string, audioRef:
|
|
29
|
+
constructor(path: string, audioRef: AudioInternal, options?: AudioSourceOptions);
|
|
30
30
|
isReady(): boolean;
|
|
31
31
|
play(): void;
|
|
32
32
|
stop(): void;
|
|
@@ -42,20 +42,23 @@ export declare class Source {
|
|
|
42
42
|
getVolume(): number;
|
|
43
43
|
getDuration(): number;
|
|
44
44
|
}
|
|
45
|
-
export declare class
|
|
45
|
+
export declare class AudioInternal {
|
|
46
46
|
private sources;
|
|
47
47
|
private globalVolume;
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Get a {@link AudioSource}
|
|
50
|
+
*/
|
|
51
|
+
newSource(path: string, options?: AudioSourceOptions): AudioSource;
|
|
49
52
|
/** Get all audio sources -- note that sources are tracked
|
|
50
53
|
* using weak references, and storing this list can cause
|
|
51
54
|
* a memory leak.
|
|
52
55
|
*/
|
|
53
|
-
|
|
56
|
+
getAllSources(): AudioSource[];
|
|
54
57
|
stopAll(): void;
|
|
55
58
|
pauseAll(): void;
|
|
56
59
|
resumeAll(): void;
|
|
57
60
|
setVolume(volume: number): void;
|
|
58
61
|
getVolume(): number;
|
|
59
|
-
clone(source:
|
|
62
|
+
clone(source: AudioSource): AudioSource;
|
|
60
63
|
}
|
|
61
64
|
//# sourceMappingURL=audio.d.ts.map
|
package/dist/core/audio.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../src/core/audio.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../src/core/audio.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAA;AAMD;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6IAA6I;IAC7I,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAC/C,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,CAAC,SAAS,CAAoE;IACrF,OAAO,CAAC,QAAQ,CAAgB;gBAEpB,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,GAAE,kBAAuB;IAmCnF,OAAO,IAAI,OAAO;IAIlB,IAAI,IAAI,IAAI;IAUZ,IAAI,IAAI,IAAI;IAUZ,KAAK,IAAI,IAAI;IAQb,MAAM,IAAI,IAAI;IAYd,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ5B,IAAI,IAAI,MAAM;IAKd,SAAS,IAAI,OAAO;IAKpB,QAAQ,IAAI,OAAO;IAKnB,SAAS,IAAI,OAAO;IAKpB,+FAA+F;IAC/F,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK/B,SAAS,IAAI,MAAM;IAInB,WAAW,IAAI,MAAM;CAItB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,YAAY,CAAK;IAEzB;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW;IAMlE;;;QAGI;IACJ,aAAa,IAAI,WAAW,EAAE;IAS9B,OAAO,IAAI,IAAI;IAIf,QAAQ,IAAI,IAAI;IAIhB,SAAS,IAAI,IAAI;IAIjB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAO/B,SAAS,IAAI,MAAM;IAInB,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW;CAGxC"}
|
package/dist/core/audio.js
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
|
-
* Handle to a loaded audio resource.
|
|
11
|
+
* Handle to a loaded audio resource, which pretends to be synchronous.
|
|
12
12
|
* Use `play()`, `stop()`, `pause()`, `resume()` for playback control.
|
|
13
13
|
* Access the underlying HTMLAudioElement via `source.audio` for looping,
|
|
14
14
|
* pitch, etc. Note: Use `source.setVolume()` instead of setting
|
|
15
15
|
* `source.audio.volume` directly to ensure global volume scaling works correctly.
|
|
16
16
|
*/
|
|
17
|
-
export class
|
|
17
|
+
export class AudioSource {
|
|
18
18
|
constructor(path, audioRef, options = {}) {
|
|
19
19
|
Object.defineProperty(this, "path", {
|
|
20
20
|
enumerable: true,
|
|
@@ -169,7 +169,7 @@ export class Source {
|
|
|
169
169
|
return 0;
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
-
export class
|
|
172
|
+
export class AudioInternal {
|
|
173
173
|
constructor() {
|
|
174
174
|
Object.defineProperty(this, "sources", {
|
|
175
175
|
enumerable: true,
|
|
@@ -184,8 +184,11 @@ export class Audio {
|
|
|
184
184
|
value: 1
|
|
185
185
|
});
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Get a {@link AudioSource}
|
|
189
|
+
*/
|
|
187
190
|
newSource(path, options) {
|
|
188
|
-
const source = new
|
|
191
|
+
const source = new AudioSource(path, this, options);
|
|
189
192
|
this.sources.push(new WeakRef(source));
|
|
190
193
|
return source;
|
|
191
194
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { EngineDispatch } from "../engine";
|
|
2
|
+
import { Rectangle } from "../math/rect";
|
|
3
|
+
import { type Vector2 } from "../math/vector2";
|
|
4
|
+
export type CanvasModeOptions = {
|
|
5
|
+
fullscreen: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type CanvasSize = Vector2 | 'native';
|
|
8
|
+
export declare class CanvasInternal {
|
|
9
|
+
dispatch: EngineDispatch;
|
|
10
|
+
/** The ultimately visible canvas in the browser */
|
|
11
|
+
_displayCanvas: HTMLCanvasElement;
|
|
12
|
+
/** The canvas that we're drawing to with `like.gfx` functions.
|
|
13
|
+
* If it's the same as _displayCanvas, we're in native mode.
|
|
14
|
+
* Otherwise, we're in pixelart mode, consisting of nearest -> linear scaling.
|
|
15
|
+
*/
|
|
16
|
+
_renderCanvas: HTMLCanvasElement;
|
|
17
|
+
private resizeTimeoutId;
|
|
18
|
+
private abort;
|
|
19
|
+
constructor(dispatch: EngineDispatch);
|
|
20
|
+
/** Get a unified canvas info object. */
|
|
21
|
+
getMode(): {
|
|
22
|
+
size: Vector2;
|
|
23
|
+
flags: CanvasModeOptions;
|
|
24
|
+
};
|
|
25
|
+
/** Set the game's apparent resolution, fullscreen, etc.
|
|
26
|
+
*
|
|
27
|
+
* ### `'native'` mode
|
|
28
|
+
* Keeps the canvas pixel resolution
|
|
29
|
+
* the same as the physical pixel resolution of the
|
|
30
|
+
* device.
|
|
31
|
+
*
|
|
32
|
+
* ### Pixel art mode `[width, height]`
|
|
33
|
+
* The canvas will use prescaling to keep your pixel
|
|
34
|
+
* games looking sharp, but without the uneven pixels
|
|
35
|
+
* caused by the naive approach.
|
|
36
|
+
*
|
|
37
|
+
* @param size 'native' for native mode, otherwise [width, height]
|
|
38
|
+
* @param flags optional options.
|
|
39
|
+
*/
|
|
40
|
+
setMode(size: CanvasSize, flags?: Partial<CanvasModeOptions>): void;
|
|
41
|
+
/** Get the apparent (in-game) canvas size. */
|
|
42
|
+
getSize(): Vector2;
|
|
43
|
+
/** Sometimes you want a screen rect! */
|
|
44
|
+
getRect(): Rectangle;
|
|
45
|
+
/** Get the actual (physical) canvas size on screen. */
|
|
46
|
+
_getDisplayPixelSize(): Vector2;
|
|
47
|
+
/** Are we fullscreen? */
|
|
48
|
+
getFullscreen(): boolean;
|
|
49
|
+
/** Set fullscreen. */
|
|
50
|
+
setFullscreen(fullscreen: boolean): void;
|
|
51
|
+
/** Called every frame by the engine after drawing */
|
|
52
|
+
_present(): void;
|
|
53
|
+
/** @returns if size was changed. */
|
|
54
|
+
static setCanvasElemSize(canvas: HTMLCanvasElement, newSize: Vector2): boolean;
|
|
55
|
+
static getCanvasElemSize(canvas: HTMLCanvasElement): Vector2;
|
|
56
|
+
_dispose(): void;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/core/canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAQ,SAAS,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC;AACxD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5C,qBAAa,cAAc;IAYJ,QAAQ,EAAE,cAAc;IAX3C,mDAAmD;IAC5C,cAAc,EAAE,iBAAiB,CAAC;IACzC;;;MAGE;IACK,aAAa,EAAE,iBAAiB,CAAC;IAExC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,KAAK,CAAyB;gBAEnB,QAAQ,EAAE,cAAc;IA0D3C,wCAAwC;IACxC,OAAO,IAAI;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,iBAAiB,CAAA;KAAE;IAStD;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,GAAE,OAAO,CAAC,iBAAiB,CAAM;IA2BhE,8CAA8C;IAC9C,OAAO,IAAI,OAAO;IAIlB,wCAAwC;IACxC,OAAO,IAAI,SAAS;IAIpB,uDAAuD;IACvD,oBAAoB,IAAI,OAAO;IAO/B,yBAAyB;IACzB,aAAa,IAAI,OAAO;IAIxB,sBAAsB;IACtB,aAAa,CAAC,UAAU,EAAE,OAAO;IASjC,qDAAqD;IACrD,QAAQ;IAyCR,qCAAqC;IACrC,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO;IAO9E,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO;IAI5D,QAAQ,IAAI,IAAI;CAGnB"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { Rect } from "../math/rect";
|
|
2
|
+
import { Vec2 } from "../math/vector2";
|
|
3
|
+
export class CanvasInternal {
|
|
4
|
+
constructor(dispatch) {
|
|
5
|
+
Object.defineProperty(this, "dispatch", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: dispatch
|
|
10
|
+
});
|
|
11
|
+
/** The ultimately visible canvas in the browser */
|
|
12
|
+
Object.defineProperty(this, "_displayCanvas", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true,
|
|
16
|
+
value: void 0
|
|
17
|
+
});
|
|
18
|
+
/** The canvas that we're drawing to with `like.gfx` functions.
|
|
19
|
+
* If it's the same as _displayCanvas, we're in native mode.
|
|
20
|
+
* Otherwise, we're in pixelart mode, consisting of nearest -> linear scaling.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(this, "_renderCanvas", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: void 0
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "resizeTimeoutId", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: 0
|
|
33
|
+
});
|
|
34
|
+
Object.defineProperty(this, "abort", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
configurable: true,
|
|
37
|
+
writable: true,
|
|
38
|
+
value: new AbortController()
|
|
39
|
+
});
|
|
40
|
+
this._displayCanvas = document.createElement('canvas');
|
|
41
|
+
this._displayCanvas.tabIndex = 0;
|
|
42
|
+
this._displayCanvas.style.width = '100%';
|
|
43
|
+
this._displayCanvas.style.height = '100%';
|
|
44
|
+
this._renderCanvas = this._displayCanvas;
|
|
45
|
+
this.setMode('native');
|
|
46
|
+
/** Only the canvas can really transform the mouse to the game size.
|
|
47
|
+
* This hack sends an event for the mouse module to listen to.
|
|
48
|
+
*/
|
|
49
|
+
this._displayCanvas.addEventListener('mousemove', (ev) => {
|
|
50
|
+
let pos;
|
|
51
|
+
let delta;
|
|
52
|
+
const rawPos = [ev.offsetX, ev.offsetY];
|
|
53
|
+
const rawDelta = [ev.movementX, ev.movementY];
|
|
54
|
+
if (this._renderCanvas == this._displayCanvas) {
|
|
55
|
+
/* Native mode. */
|
|
56
|
+
pos = Vec2.mul(rawPos, window.devicePixelRatio ?? 1);
|
|
57
|
+
delta = Vec2.mul(rawDelta, window.devicePixelRatio ?? 1);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
/* Pixelart mode. This math simulates object-fit: contain,
|
|
61
|
+
* which preserves aspect ratio.
|
|
62
|
+
*/
|
|
63
|
+
const csize = [
|
|
64
|
+
this._displayCanvas.clientWidth,
|
|
65
|
+
this._displayCanvas.clientHeight
|
|
66
|
+
];
|
|
67
|
+
/* Scale of both dimensions */
|
|
68
|
+
const scale = calcAspectFriendlyScale(this.getSize(), csize);
|
|
69
|
+
/* Upper-left corner */
|
|
70
|
+
const offset = Vec2.div(Vec2.sub(csize, Vec2.mul(this.getSize(), scale)), 2);
|
|
71
|
+
pos = Vec2.div(Vec2.sub(rawPos, offset), scale);
|
|
72
|
+
delta = Vec2.div(rawDelta, scale);
|
|
73
|
+
/* Only handle mousemove events that are in bounds. */
|
|
74
|
+
if (!Rect.containsPoint(this.getRect(), pos)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
this._displayCanvas.dispatchEvent(new CustomEvent('like:mousemoved', {
|
|
79
|
+
detail: {
|
|
80
|
+
pos,
|
|
81
|
+
delta,
|
|
82
|
+
renderSize: this.getSize(),
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
}, { signal: this.abort.signal });
|
|
86
|
+
}
|
|
87
|
+
/** Get a unified canvas info object. */
|
|
88
|
+
getMode() {
|
|
89
|
+
return {
|
|
90
|
+
size: this.getSize(),
|
|
91
|
+
flags: {
|
|
92
|
+
fullscreen: this.getFullscreen(),
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/** Set the game's apparent resolution, fullscreen, etc.
|
|
97
|
+
*
|
|
98
|
+
* ### `'native'` mode
|
|
99
|
+
* Keeps the canvas pixel resolution
|
|
100
|
+
* the same as the physical pixel resolution of the
|
|
101
|
+
* device.
|
|
102
|
+
*
|
|
103
|
+
* ### Pixel art mode `[width, height]`
|
|
104
|
+
* The canvas will use prescaling to keep your pixel
|
|
105
|
+
* games looking sharp, but without the uneven pixels
|
|
106
|
+
* caused by the naive approach.
|
|
107
|
+
*
|
|
108
|
+
* @param size 'native' for native mode, otherwise [width, height]
|
|
109
|
+
* @param flags optional options.
|
|
110
|
+
*/
|
|
111
|
+
setMode(size, flags = {}) {
|
|
112
|
+
// set up sizing / render target
|
|
113
|
+
const prevRenderCanvas = this._renderCanvas;
|
|
114
|
+
if (size == 'native') {
|
|
115
|
+
this._displayCanvas.style.objectFit = 'fill';
|
|
116
|
+
this._renderCanvas = this._displayCanvas;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this._displayCanvas.style.objectFit = 'contain';
|
|
120
|
+
this._renderCanvas = document.createElement('canvas');
|
|
121
|
+
const changed = CanvasInternal.setCanvasElemSize(this._renderCanvas, size);
|
|
122
|
+
if (changed) {
|
|
123
|
+
this.dispatch('resize', [size]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (prevRenderCanvas != this._renderCanvas) {
|
|
127
|
+
this._displayCanvas.dispatchEvent(new CustomEvent('like:updateRenderTarget', {
|
|
128
|
+
detail: {
|
|
129
|
+
target: this._renderCanvas,
|
|
130
|
+
}
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
if ('fullscreen' in flags) {
|
|
134
|
+
this.setFullscreen(flags.fullscreen);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Get the apparent (in-game) canvas size. */
|
|
138
|
+
getSize() {
|
|
139
|
+
return [this._renderCanvas.width, this._renderCanvas.height];
|
|
140
|
+
}
|
|
141
|
+
/** Sometimes you want a screen rect! */
|
|
142
|
+
getRect() {
|
|
143
|
+
return [0, 0, ...this.getSize()];
|
|
144
|
+
}
|
|
145
|
+
/** Get the actual (physical) canvas size on screen. */
|
|
146
|
+
_getDisplayPixelSize() {
|
|
147
|
+
return Vec2.round(Vec2.mul([this._displayCanvas.clientWidth, this._displayCanvas.clientHeight], window.devicePixelRatio ?? 1));
|
|
148
|
+
}
|
|
149
|
+
/** Are we fullscreen? */
|
|
150
|
+
getFullscreen() {
|
|
151
|
+
return this._displayCanvas === document.fullscreenElement;
|
|
152
|
+
}
|
|
153
|
+
/** Set fullscreen. */
|
|
154
|
+
setFullscreen(fullscreen) {
|
|
155
|
+
if (fullscreen) {
|
|
156
|
+
this._displayCanvas.requestFullscreen();
|
|
157
|
+
this._displayCanvas.focus();
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
document.exitFullscreen();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** Called every frame by the engine after drawing */
|
|
164
|
+
_present() {
|
|
165
|
+
if (this._renderCanvas == this._displayCanvas) {
|
|
166
|
+
const realSize = this._getDisplayPixelSize();
|
|
167
|
+
if ((realSize[0] != this._displayCanvas.width ||
|
|
168
|
+
realSize[1] != this._displayCanvas.height) &&
|
|
169
|
+
!this.resizeTimeoutId) {
|
|
170
|
+
/** In native scaling mode, zooming and resizing the window cause us
|
|
171
|
+
* to set canvas width and height every frame, which could cause
|
|
172
|
+
* tons of canvas bitmap reallocations. So wait 1/4 second..
|
|
173
|
+
*/
|
|
174
|
+
CanvasInternal.setCanvasElemSize(this._displayCanvas, realSize);
|
|
175
|
+
this.dispatch('resize', [realSize]);
|
|
176
|
+
this.resizeTimeoutId = setTimeout(() => { this.resizeTimeoutId = 0; }, 250);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else if (this._renderCanvas != this._displayCanvas) {
|
|
180
|
+
/* We're in pixelart mode,
|
|
181
|
+
* so set output canvas size to an ideal integer scale.
|
|
182
|
+
* No debounce: changes to integer ratio are infrequent.
|
|
183
|
+
*/
|
|
184
|
+
CanvasInternal.setCanvasElemSize(this._displayCanvas, Vec2.mul(this.getSize(), Math.round(calcAspectFriendlyScale(this.getSize(), this._getDisplayPixelSize()))));
|
|
185
|
+
// Copy the internal canvas to the visible one.
|
|
186
|
+
const ctx = this._displayCanvas.getContext('2d');
|
|
187
|
+
ctx.imageSmoothingEnabled = false;
|
|
188
|
+
ctx.drawImage(this._renderCanvas, 0, 0, this._renderCanvas.width, this._renderCanvas.height, 0, 0, this._displayCanvas.width, this._displayCanvas.height);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/** @returns if size was changed. */
|
|
192
|
+
static setCanvasElemSize(canvas, newSize) {
|
|
193
|
+
if (canvas.width != newSize[0] || canvas.height != newSize[1]) {
|
|
194
|
+
[canvas.width, canvas.height] = newSize;
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
static getCanvasElemSize(canvas) {
|
|
200
|
+
return [canvas.width, canvas.height];
|
|
201
|
+
}
|
|
202
|
+
_dispose() {
|
|
203
|
+
this.abort.abort();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** How much could this image be scaled, preserving aspect? */
|
|
207
|
+
function calcAspectFriendlyScale(imageSize, containerSize) {
|
|
208
|
+
return Math.min(...Vec2.div(containerSize, imageSize));
|
|
209
|
+
}
|
package/dist/core/events.d.ts
CHANGED
|
@@ -1,20 +1,105 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module events
|
|
3
|
+
* @description All events that flow through the engine.
|
|
4
|
+
*
|
|
5
|
+
* ## Overview
|
|
6
|
+
*
|
|
7
|
+
* LIKE uses events at its core.
|
|
8
|
+
* These pass through the engine and down to your
|
|
9
|
+
* callbacks or scene.
|
|
10
|
+
*
|
|
11
|
+
* This module is the single source of truth for what
|
|
12
|
+
* events are possible.
|
|
13
|
+
*
|
|
14
|
+
* Use it as a reference.
|
|
15
|
+
*
|
|
16
|
+
* @see {@link EventMap} lists every event.
|
|
17
|
+
* @see {@link Scene} for implementing callbacks in a class
|
|
18
|
+
* @see {@link Like} for global callback assignment
|
|
19
|
+
* @see {@link Input} for action mapping
|
|
20
|
+
*/
|
|
21
|
+
import type { Vector2 } from '../math/vector2';
|
|
22
|
+
import { LikeButton } from './gamepad-mapping';
|
|
23
|
+
export type MouseButton = 'left' | 'middle' | 'right';
|
|
24
|
+
declare global {
|
|
25
|
+
interface HTMLElementEventMap {
|
|
26
|
+
['like:mousemoved']: CustomEvent<{
|
|
27
|
+
pos: Vector2;
|
|
28
|
+
delta: Vector2;
|
|
29
|
+
renderSize: Vector2;
|
|
30
|
+
}>;
|
|
31
|
+
['like:updateRenderTarget']: CustomEvent<{
|
|
32
|
+
target: HTMLCanvasElement;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The master type will all events on it.
|
|
38
|
+
*
|
|
39
|
+
* Each frame:
|
|
40
|
+
* 1. `update(dt)` - Game logic with delta time in seconds
|
|
41
|
+
* 2. `draw` - Render the frame
|
|
42
|
+
*
|
|
43
|
+
* Input events fire immediately when they occur:
|
|
44
|
+
* - `keypressed`/`keyreleased` - Keyboard input
|
|
45
|
+
* - `mousemoved`/`mousepressed`/`mousereleased` - Mouse input
|
|
46
|
+
* - `gamepadpressed`/`gamepadreleased` - Controller input
|
|
47
|
+
* - `actionpressed`/`actionreleased` - Mapped actions (see {@link Input})
|
|
48
|
+
*
|
|
49
|
+
* Window events:
|
|
50
|
+
* - `focus`/`blur` - Tab/window focus changes
|
|
51
|
+
* - `resize` - Canvas size changes
|
|
52
|
+
*
|
|
53
|
+
* Lifecycle:
|
|
54
|
+
* - `load` - Called once when the game starts
|
|
55
|
+
*/
|
|
2
56
|
export type EventMap = {
|
|
57
|
+
/** Game initialization. Called once before the first frame. */
|
|
3
58
|
load: [];
|
|
59
|
+
/** Frame update. dt is delta time in seconds (time since last frame). */
|
|
4
60
|
update: [dt: number];
|
|
61
|
+
/** Render frame. Clear the screen and draw your game here. */
|
|
5
62
|
draw: [];
|
|
6
|
-
|
|
63
|
+
/** Canvas was resized. Used mostly in native mode, though setMode may send it too. */
|
|
64
|
+
resize: [size: Vector2];
|
|
65
|
+
/** Physical key pressed. scancode is the physical key, keycode is the character. */
|
|
7
66
|
keypressed: [scancode: string, keycode: string];
|
|
67
|
+
/** Physical key released. */
|
|
8
68
|
keyreleased: [scancode: string, keycode: string];
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
69
|
+
/** Canvas or tab gained focus. Game may resume audio/updates. */
|
|
70
|
+
focus: [source: 'canvas' | 'tab'];
|
|
71
|
+
/** Canvas or tab lost focus. Game may pause audio/updates. */
|
|
72
|
+
blur: [source: 'canvas' | 'tab'];
|
|
73
|
+
/** Mouse moved event. `pos` is absolute, `delta` is relative. */
|
|
74
|
+
mousemoved: [pos: Vector2, delta: Vector2];
|
|
75
|
+
/** Mouse button pressed. pos in canvas pixels. Button: 1=left, 2=middle, 3=right. */
|
|
76
|
+
mousepressed: [pos: Vector2, button: MouseButton];
|
|
77
|
+
/** Mouse button released. */
|
|
78
|
+
mousereleased: [pos: Vector2, button: MouseButton];
|
|
79
|
+
/** Gamepad button pressed. index is controller number (0-3). */
|
|
80
|
+
gamepadpressed: [source: number, num: number, name: LikeButton];
|
|
81
|
+
/** Gamepad button released. */
|
|
82
|
+
gamepadreleased: [source: number, num: number, name: LikeButton];
|
|
83
|
+
/** Mapped action triggered. See {@link Input} for action mapping. */
|
|
13
84
|
actionpressed: [action: string];
|
|
85
|
+
/** Mapped action released. */
|
|
14
86
|
actionreleased: [action: string];
|
|
15
87
|
};
|
|
16
88
|
export type EventType = keyof EventMap;
|
|
17
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Discriminated union of all event objects.
|
|
91
|
+
* Use this with `handleEvent` to receive all events generically.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* handleEvent(like: Like, event: Like2DEvent) {
|
|
96
|
+
* if (event.type === 'update') {
|
|
97
|
+
* const dt = event.args[0];
|
|
98
|
+
* }
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export type LikeEvent = {
|
|
18
103
|
[K in EventType]: {
|
|
19
104
|
type: K;
|
|
20
105
|
args: EventMap[K];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/core/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/core/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,mBAAmB;QAC3B,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC;YAAC,GAAG,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,OAAO,CAAC;YAAC,UAAU,EAAE,OAAO,CAAA;SAAC,CAAC,CAAC;QACtF,CAAC,yBAAyB,CAAC,EAAE,WAAW,CAAC;YAAC,MAAM,EAAE,iBAAiB,CAAA;SAAC,CAAC,CAAC;KACvE;CACF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,+DAA+D;IAC/D,IAAI,EAAE,EAAE,CAAC;IAET,yEAAyE;IACzE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAErB,8DAA8D;IAC9D,IAAI,EAAE,EAAE,CAAC;IAET,sFAAsF;IACtF,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAExB,oFAAoF;IACpF,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAEhD,6BAA6B;IAC7B,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAEjD,iEAAiE;IACjE,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAC;IAElC,8DAA8D;IAC9D,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAC;IAEjC,iEAAiE;IACjE,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAE3C,qFAAqF;IACrF,YAAY,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAElD,6BAA6B;IAC7B,aAAa,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAEnD,gEAAgE;IAChE,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IAEhE,+BAA+B;IAC/B,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IAEjE,qEAAqE;IACrE,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC,8BAA8B;IAC9B,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC;AAEvC;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,SAAS,GAAG;KACrB,CAAC,IAAI,SAAS,GAAG;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;CACpE,CAAC,SAAS,CAAC,CAAC"}
|
package/dist/core/events.js
CHANGED
|
@@ -1 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module events
|
|
3
|
+
* @description All events that flow through the engine.
|
|
4
|
+
*
|
|
5
|
+
* ## Overview
|
|
6
|
+
*
|
|
7
|
+
* LIKE uses events at its core.
|
|
8
|
+
* These pass through the engine and down to your
|
|
9
|
+
* callbacks or scene.
|
|
10
|
+
*
|
|
11
|
+
* This module is the single source of truth for what
|
|
12
|
+
* events are possible.
|
|
13
|
+
*
|
|
14
|
+
* Use it as a reference.
|
|
15
|
+
*
|
|
16
|
+
* @see {@link EventMap} lists every event.
|
|
17
|
+
* @see {@link Scene} for implementing callbacks in a class
|
|
18
|
+
* @see {@link Like} for global callback assignment
|
|
19
|
+
* @see {@link Input} for action mapping
|
|
20
|
+
*/
|
|
1
21
|
export {};
|