@dryanovski/gamefoo 0.2.3 → 0.2.5
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/dist/core/animate.js +147 -0
- package/dist/core/asset.js +74 -0
- package/dist/core/behaviour.js +88 -0
- package/dist/core/behaviours/collidable.js +186 -0
- package/dist/core/behaviours/control.js +75 -0
- package/dist/core/behaviours/healtkit.js +153 -0
- package/dist/core/behaviours/sprite_render.js +193 -0
- package/dist/core/camera.js +134 -0
- package/dist/core/engine.d.ts +1 -1
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +527 -0
- package/dist/core/fonts/font_bitmap.js +205 -0
- package/dist/core/fonts/font_bitmap_prebuild.js +137 -0
- package/dist/core/fonts/internal/font_3x5.js +169 -0
- package/dist/core/fonts/internal/font_4x6.js +171 -0
- package/dist/core/fonts/internal/font_5x5.js +129 -0
- package/dist/core/fonts/internal/font_6x8.js +171 -0
- package/dist/core/fonts/internal/font_8x13.js +171 -0
- package/dist/core/fonts/internal/font_8x8.js +171 -0
- package/dist/core/game_object_register.js +134 -0
- package/dist/core/input.js +170 -0
- package/dist/core/sprite.js +222 -0
- package/dist/core/utils/perlin_noise.js +183 -0
- package/dist/core/world.js +304 -0
- package/dist/debug/monitor.js +47 -0
- package/dist/decorators/index.js +1 -0
- package/dist/decorators/log.js +42 -0
- package/dist/entities/dynamic_entity.js +99 -0
- package/dist/entities/entity.js +283 -0
- package/dist/entities/player.js +93 -0
- package/dist/entities/text.js +62 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -29
- package/dist/subsystems/camera_system.d.ts +2 -2
- package/dist/subsystems/camera_system.d.ts.map +1 -1
- package/dist/subsystems/camera_system.js +41 -0
- package/dist/subsystems/collision_system.js +17 -0
- package/dist/subsystems/monitor_system.js +20 -0
- package/dist/subsystems/object_system.d.ts +1 -1
- package/dist/subsystems/object_system.d.ts.map +1 -1
- package/dist/subsystems/object_system.js +29 -0
- package/dist/subsystems/types.d.ts +1 -1
- package/dist/subsystems/types.d.ts.map +1 -1
- package/dist/subsystems/types.js +0 -0
- package/dist/types.js +10 -0
- package/package.json +17 -6
- package/src/core/animate.ts +159 -0
- package/src/core/asset.ts +76 -0
- package/src/core/behaviour.ts +145 -0
- package/src/core/behaviours/collidable.ts +296 -0
- package/src/core/behaviours/control.ts +80 -0
- package/src/core/behaviours/healtkit.ts +166 -0
- package/src/core/behaviours/sprite_render.ts +216 -0
- package/src/core/camera.ts +145 -0
- package/src/core/engine.ts +607 -0
- package/src/core/fonts/font_bitmap.ts +232 -0
- package/src/core/fonts/font_bitmap_prebuild.ts +141 -0
- package/src/core/fonts/internal/font_3x5.ts +178 -0
- package/src/core/fonts/internal/font_4x6.ts +180 -0
- package/src/core/fonts/internal/font_5x5.ts +137 -0
- package/src/core/fonts/internal/font_6x8.ts +180 -0
- package/src/core/fonts/internal/font_8x13.ts +180 -0
- package/src/core/fonts/internal/font_8x8.ts +180 -0
- package/src/core/game_object_register.ts +146 -0
- package/src/core/input.ts +182 -0
- package/src/core/sprite.ts +339 -0
- package/src/core/utils/perlin_noise.ts +196 -0
- package/src/core/world.ts +331 -0
- package/src/debug/monitor.ts +60 -0
- package/src/decorators/index.ts +1 -0
- package/src/decorators/log.ts +45 -0
- package/src/entities/dynamic_entity.ts +106 -0
- package/src/entities/entity.ts +322 -0
- package/src/entities/player.ts +99 -0
- package/src/entities/text.ts +72 -0
- package/src/index.ts +51 -0
- package/src/subsystems/camera_system.ts +52 -0
- package/src/subsystems/collision_system.ts +21 -0
- package/src/subsystems/monitor_system.ts +26 -0
- package/src/subsystems/object_system.ts +37 -0
- package/src/subsystems/types.ts +46 -0
- package/src/types.ts +178 -0
- package/dist/index.js.map +0 -9
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in 8x8 pixel bitmap font.
|
|
3
|
+
*
|
|
4
|
+
* Each character is a 8-pixel-wide, 8-pixel-tall glyph stored as an
|
|
5
|
+
* array of 8 integers. Each integer is a bitmask where bit 7
|
|
6
|
+
* (MSB) corresponds to the leftmost pixel and bit 0 to the rightmost.
|
|
7
|
+
*
|
|
8
|
+
* Chunky square glyphs, ideal at 2× or 4× scale.
|
|
9
|
+
*
|
|
10
|
+
* Supported characters: uppercase A–Z, lowercase a–z, digits 0–9,
|
|
11
|
+
* and common punctuation / special characters.
|
|
12
|
+
*
|
|
13
|
+
* @category Fonts
|
|
14
|
+
* @since 0.2.0
|
|
15
|
+
* @internal
|
|
16
|
+
*
|
|
17
|
+
* @example Reading a glyph
|
|
18
|
+
* ```ts
|
|
19
|
+
* import FONT_8x8 from "./font_8x8";
|
|
20
|
+
*
|
|
21
|
+
* const letterA = FONT_8x8["A"];
|
|
22
|
+
* // 24, 36, 66, 66, 126, 66, 66, 0
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** Catalogue name used by {@link FontBitmap} to look up this font. */
|
|
27
|
+
export const FONT_8x8_NAME = "8x8";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Glyph width in pixels (excluding spacing).
|
|
31
|
+
* @defaultValue `8`
|
|
32
|
+
*/
|
|
33
|
+
export const FONT_8x8_WIDTH = 8;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Glyph height in pixels.
|
|
37
|
+
* @defaultValue `8`
|
|
38
|
+
*/
|
|
39
|
+
export const FONT_8x8_HEIGHT = 8;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Horizontal spacing between glyphs in pixels.
|
|
43
|
+
* @defaultValue `1`
|
|
44
|
+
*/
|
|
45
|
+
export const FONT_8x8_SPACING = 1;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Complete set of supported characters as a single string.
|
|
49
|
+
*/
|
|
50
|
+
export const FONT_8x8_CHARS =
|
|
51
|
+
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz !?.,:;-+*/\\()[]{}<>=#%&@^_'\"`~|$";
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Glyph data keyed by character.
|
|
55
|
+
*
|
|
56
|
+
* Each value is a 8-element `number[]` where each entry is a 8-bit
|
|
57
|
+
* row bitmask (bit 7 = leftmost pixel, bit 0 = rightmost pixel).
|
|
58
|
+
*
|
|
59
|
+
* @see {@link FontBitmap} — consumes this data for rendering
|
|
60
|
+
*/
|
|
61
|
+
export const FONT_8x8: Record<string, number[]> = {
|
|
62
|
+
"0": [60, 66, 70, 74, 82, 98, 60, 0],
|
|
63
|
+
"1": [24, 56, 24, 24, 24, 24, 126, 0],
|
|
64
|
+
"2": [60, 66, 2, 12, 48, 64, 126, 0],
|
|
65
|
+
"3": [126, 2, 4, 28, 2, 2, 124, 0],
|
|
66
|
+
"4": [4, 12, 20, 36, 68, 254, 4, 0],
|
|
67
|
+
"5": [254, 128, 252, 2, 2, 66, 60, 0],
|
|
68
|
+
"6": [62, 64, 128, 252, 130, 130, 124, 0],
|
|
69
|
+
"7": [254, 4, 8, 16, 32, 64, 128, 0],
|
|
70
|
+
"8": [60, 66, 66, 60, 66, 66, 60, 0],
|
|
71
|
+
"9": [60, 66, 66, 62, 2, 2, 60, 0],
|
|
72
|
+
A: [24, 36, 66, 66, 126, 66, 66, 0],
|
|
73
|
+
B: [252, 66, 66, 124, 66, 66, 252, 0],
|
|
74
|
+
C: [62, 64, 128, 128, 128, 64, 62, 0],
|
|
75
|
+
D: [248, 68, 66, 66, 66, 68, 248, 0],
|
|
76
|
+
E: [254, 128, 128, 252, 128, 128, 254, 0],
|
|
77
|
+
F: [254, 128, 128, 252, 128, 128, 128, 0],
|
|
78
|
+
G: [62, 64, 128, 142, 130, 66, 62, 0],
|
|
79
|
+
H: [66, 66, 66, 126, 66, 66, 66, 0],
|
|
80
|
+
I: [126, 24, 24, 24, 24, 24, 126, 0],
|
|
81
|
+
J: [62, 4, 4, 4, 132, 136, 112, 0],
|
|
82
|
+
K: [130, 132, 136, 240, 136, 132, 130, 0],
|
|
83
|
+
L: [128, 128, 128, 128, 128, 128, 254, 0],
|
|
84
|
+
M: [130, 198, 170, 146, 130, 130, 130, 0],
|
|
85
|
+
N: [130, 194, 162, 146, 138, 134, 130, 0],
|
|
86
|
+
O: [60, 66, 129, 129, 129, 66, 60, 0],
|
|
87
|
+
P: [252, 130, 130, 252, 128, 128, 128, 0],
|
|
88
|
+
Q: [60, 66, 129, 129, 133, 66, 61, 0],
|
|
89
|
+
R: [252, 130, 130, 252, 136, 132, 130, 0],
|
|
90
|
+
S: [62, 64, 128, 124, 2, 2, 252, 0],
|
|
91
|
+
T: [254, 16, 16, 16, 16, 16, 16, 0],
|
|
92
|
+
U: [130, 130, 130, 130, 130, 68, 56, 0],
|
|
93
|
+
V: [130, 130, 68, 68, 40, 40, 16, 0],
|
|
94
|
+
W: [130, 130, 130, 146, 170, 198, 130, 0],
|
|
95
|
+
X: [130, 68, 40, 16, 40, 68, 130, 0],
|
|
96
|
+
Y: [130, 68, 40, 16, 16, 16, 16, 0],
|
|
97
|
+
Z: [254, 4, 8, 16, 32, 64, 254, 0],
|
|
98
|
+
a: [0, 0, 60, 2, 62, 66, 62, 0],
|
|
99
|
+
b: [64, 64, 124, 66, 66, 66, 124, 0],
|
|
100
|
+
c: [0, 0, 62, 64, 64, 64, 62, 0],
|
|
101
|
+
d: [2, 2, 62, 66, 66, 66, 62, 0],
|
|
102
|
+
e: [0, 0, 60, 66, 126, 64, 62, 0],
|
|
103
|
+
f: [14, 16, 16, 62, 16, 16, 16, 0],
|
|
104
|
+
g: [0, 62, 66, 66, 62, 2, 60, 0],
|
|
105
|
+
h: [64, 64, 124, 66, 66, 66, 66, 0],
|
|
106
|
+
i: [16, 0, 48, 16, 16, 16, 62, 0],
|
|
107
|
+
j: [8, 0, 8, 8, 8, 72, 48, 0],
|
|
108
|
+
k: [64, 68, 72, 112, 72, 68, 66, 0],
|
|
109
|
+
l: [96, 32, 32, 32, 32, 32, 126, 0],
|
|
110
|
+
m: [0, 0, 218, 170, 170, 130, 130, 0],
|
|
111
|
+
n: [0, 0, 124, 66, 66, 66, 66, 0],
|
|
112
|
+
o: [0, 0, 60, 66, 66, 66, 60, 0],
|
|
113
|
+
p: [0, 124, 66, 66, 124, 64, 64, 0],
|
|
114
|
+
q: [0, 62, 66, 66, 62, 2, 2, 0],
|
|
115
|
+
r: [0, 0, 94, 96, 64, 64, 64, 0],
|
|
116
|
+
s: [0, 0, 62, 64, 60, 2, 124, 0],
|
|
117
|
+
t: [32, 32, 126, 32, 32, 34, 28, 0],
|
|
118
|
+
u: [0, 0, 66, 66, 66, 70, 58, 0],
|
|
119
|
+
v: [0, 0, 66, 66, 36, 40, 16, 0],
|
|
120
|
+
w: [0, 0, 130, 130, 146, 170, 68, 0],
|
|
121
|
+
x: [0, 0, 66, 36, 24, 36, 66, 0],
|
|
122
|
+
y: [0, 66, 66, 66, 62, 2, 60, 0],
|
|
123
|
+
z: [0, 0, 254, 4, 24, 96, 254, 0],
|
|
124
|
+
" ": [0, 0, 0, 0, 0, 0, 0, 0],
|
|
125
|
+
"!": [24, 24, 24, 24, 24, 0, 24, 0],
|
|
126
|
+
"?": [60, 66, 2, 4, 8, 0, 8, 0],
|
|
127
|
+
".": [0, 0, 0, 0, 0, 48, 48, 0],
|
|
128
|
+
",": [0, 0, 0, 0, 48, 48, 96, 0],
|
|
129
|
+
":": [0, 24, 24, 0, 24, 24, 0, 0],
|
|
130
|
+
";": [0, 24, 24, 0, 24, 24, 48, 0],
|
|
131
|
+
"-": [0, 0, 0, 254, 0, 0, 0, 0],
|
|
132
|
+
"+": [0, 16, 16, 254, 16, 16, 0, 0],
|
|
133
|
+
"*": [0, 130, 68, 56, 68, 130, 0, 0],
|
|
134
|
+
"/": [2, 4, 8, 16, 32, 64, 128, 0],
|
|
135
|
+
"\\": [128, 64, 32, 16, 8, 4, 2, 0],
|
|
136
|
+
"(": [12, 16, 32, 32, 32, 16, 12, 0],
|
|
137
|
+
")": [48, 8, 4, 4, 4, 8, 48, 0],
|
|
138
|
+
"[": [120, 64, 64, 64, 64, 64, 120, 0],
|
|
139
|
+
"]": [30, 2, 2, 2, 2, 2, 30, 0],
|
|
140
|
+
"{": [30, 48, 16, 96, 16, 48, 30, 0],
|
|
141
|
+
"}": [120, 12, 8, 6, 8, 12, 120, 0],
|
|
142
|
+
"<": [4, 8, 16, 32, 16, 8, 4, 0],
|
|
143
|
+
">": [32, 16, 8, 4, 8, 16, 32, 0],
|
|
144
|
+
"=": [0, 0, 254, 0, 254, 0, 0, 0],
|
|
145
|
+
"#": [36, 36, 254, 36, 254, 36, 36, 0],
|
|
146
|
+
"%": [194, 196, 8, 16, 32, 67, 131, 0],
|
|
147
|
+
"&": [56, 68, 72, 48, 74, 68, 58, 0],
|
|
148
|
+
"@": [60, 66, 157, 165, 158, 64, 60, 0],
|
|
149
|
+
"^": [16, 40, 68, 130, 0, 0, 0, 0],
|
|
150
|
+
_: [0, 0, 0, 0, 0, 0, 254, 0],
|
|
151
|
+
"'": [24, 24, 48, 0, 0, 0, 0, 0],
|
|
152
|
+
'"': [72, 72, 0, 0, 0, 0, 0, 0],
|
|
153
|
+
"`": [48, 24, 8, 0, 0, 0, 0, 0],
|
|
154
|
+
"~": [0, 70, 137, 144, 0, 0, 0, 0],
|
|
155
|
+
"|": [24, 24, 24, 24, 24, 24, 24, 0],
|
|
156
|
+
$: [24, 62, 96, 60, 6, 124, 24, 0],
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Complete metadata object for the 8x8 font, used by the
|
|
161
|
+
* {@link FontBitmap} catalogue at module load time.
|
|
162
|
+
*
|
|
163
|
+
* @internal
|
|
164
|
+
*/
|
|
165
|
+
export const metadata = {
|
|
166
|
+
/** Catalogue name. */
|
|
167
|
+
name: FONT_8x8_NAME,
|
|
168
|
+
/** Cell width including spacing (8 + 1 = 9). */
|
|
169
|
+
width: FONT_8x8_WIDTH + FONT_8x8_SPACING,
|
|
170
|
+
/** Cell height (8). */
|
|
171
|
+
height: FONT_8x8_HEIGHT,
|
|
172
|
+
/** Inter-glyph spacing (1). */
|
|
173
|
+
spacing: FONT_8x8_SPACING,
|
|
174
|
+
/** Supported character string. */
|
|
175
|
+
chars: FONT_8x8_CHARS,
|
|
176
|
+
/** Glyph bitmask data. */
|
|
177
|
+
data: FONT_8x8,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export default FONT_8x8;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { GameObject } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Central registry that stores and manages all non-player
|
|
5
|
+
* {@link GameObject | game objects} within the engine.
|
|
6
|
+
*
|
|
7
|
+
* Objects are keyed by their `id` property, so each ID must be unique.
|
|
8
|
+
* The {@link Engine} delegates per-frame `update` and `render` calls to
|
|
9
|
+
* this register.
|
|
10
|
+
*
|
|
11
|
+
* @category Core
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
*
|
|
14
|
+
* @example Registering and retrieving objects
|
|
15
|
+
* ```ts
|
|
16
|
+
* const register = new GameObjectRegister();
|
|
17
|
+
*
|
|
18
|
+
* register.register(tree);
|
|
19
|
+
* register.register(rock);
|
|
20
|
+
*
|
|
21
|
+
* const found = register.get("tree"); // Entity | undefined
|
|
22
|
+
* console.log(register.has("rock")); // true
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example Bulk update / render
|
|
26
|
+
* ```ts
|
|
27
|
+
* // Called internally by Engine each frame:
|
|
28
|
+
* register.updateAll(deltaTime);
|
|
29
|
+
* register.renderAll(ctx);
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @see {@link Engine.attachObjects} — convenience method that delegates here
|
|
33
|
+
*/
|
|
34
|
+
export default class GameObjectRegister {
|
|
35
|
+
/**
|
|
36
|
+
* Internal map from entity ID to its {@link GameObject} instance.
|
|
37
|
+
*/
|
|
38
|
+
private objects: Map<string, GameObject> = new Map();
|
|
39
|
+
|
|
40
|
+
private _cache: GameObject[] | null = null;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Adds a game object to the registry.
|
|
44
|
+
*
|
|
45
|
+
* If an object with the same `id` already exists it will be
|
|
46
|
+
* silently overwritten.
|
|
47
|
+
*
|
|
48
|
+
* @param object - The game object to register.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* register.register(new Crate("crate_1", 200, 150, 32, 32));
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
register(object: GameObject) {
|
|
56
|
+
this.objects.set(object.id, object);
|
|
57
|
+
this._cache = null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Retrieves a registered object by its unique ID.
|
|
62
|
+
*
|
|
63
|
+
* @param id - The ID of the object to find.
|
|
64
|
+
* @returns The matching {@link GameObject}, or `undefined` if not found.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* const crate = register.get("crate_1");
|
|
69
|
+
* if (crate) crate.x += 10;
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
get(id: string): GameObject | undefined {
|
|
73
|
+
return this.objects.get(id);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Checks whether an object with the given ID is registered.
|
|
78
|
+
*
|
|
79
|
+
* @param id - The ID to look up.
|
|
80
|
+
* @returns `true` if the registry contains the object.
|
|
81
|
+
*/
|
|
82
|
+
has(id: string): boolean {
|
|
83
|
+
return this.objects.has(id);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Returns all registered objects as an array.
|
|
88
|
+
*
|
|
89
|
+
* Make sure to also cache the objects
|
|
90
|
+
*
|
|
91
|
+
* @since 0.2.0
|
|
92
|
+
*
|
|
93
|
+
* @returns An array of all {@link GameObject} instances in the registry.
|
|
94
|
+
*/
|
|
95
|
+
toArray(): GameObject[] {
|
|
96
|
+
if (!this._cache) {
|
|
97
|
+
this._cache = Array.from(this.objects.values());
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return this._cache;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Returns all registered objects that pass the supplied filter.
|
|
105
|
+
*
|
|
106
|
+
* @param filter - (optional) A predicate function. Return `true` to include the
|
|
107
|
+
* object in the result.
|
|
108
|
+
* @returns An array of matching {@link GameObject} instances.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* const enemies = register.getAll(() => true);
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
getAll(filter?: () => true): GameObject[] {
|
|
116
|
+
if (typeof filter === "function") {
|
|
117
|
+
return this.toArray().filter(filter);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return this.toArray();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Calls {@link GameObject.update | update(deltaTime)} on every
|
|
125
|
+
* registered object.
|
|
126
|
+
*
|
|
127
|
+
* @param deltaTime - Seconds elapsed since the previous frame.
|
|
128
|
+
*/
|
|
129
|
+
updateAll(deltaTime: number): void {
|
|
130
|
+
for (const obj of this.getAll()) {
|
|
131
|
+
obj.update(deltaTime);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Calls {@link GameObject.render | render(ctx)} on every registered
|
|
137
|
+
* object.
|
|
138
|
+
*
|
|
139
|
+
* @param ctx - The canvas 2-D rendering context.
|
|
140
|
+
*/
|
|
141
|
+
renderAll(ctx: CanvasRenderingContext2D): void {
|
|
142
|
+
for (const obj of this.getAll()) {
|
|
143
|
+
obj.render(ctx);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified keyboard and mouse input manager.
|
|
3
|
+
*
|
|
4
|
+
* `Input` listens to `keydown`, `keyup`, `mousedown`, `mouseup`, and
|
|
5
|
+
* `mousemove` events on the `window` and exposes a polling API so game
|
|
6
|
+
* logic can query the current state at any point during a frame rather
|
|
7
|
+
* than relying on event callbacks.
|
|
8
|
+
*
|
|
9
|
+
* All keyboard keys are stored **lowercased** for case-insensitive
|
|
10
|
+
* look-ups.
|
|
11
|
+
*
|
|
12
|
+
* @category Core
|
|
13
|
+
* @since 0.1.0
|
|
14
|
+
*
|
|
15
|
+
* @example Polling keys
|
|
16
|
+
* ```ts
|
|
17
|
+
* const input = new Input();
|
|
18
|
+
*
|
|
19
|
+
* function update() {
|
|
20
|
+
* if (input.isKeyDown("w")) {
|
|
21
|
+
* player.y -= speed;
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @example Checking mouse state
|
|
27
|
+
* ```ts
|
|
28
|
+
* const input = new Input();
|
|
29
|
+
*
|
|
30
|
+
* if (input.isMouseButtonDown(0)) { // left-click
|
|
31
|
+
* const { x, y } = input.getMousePosition();
|
|
32
|
+
* shoot(x, y);
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see {@link Control} — behaviour that consumes `Input` for player movement
|
|
37
|
+
*/
|
|
38
|
+
export default class Input {
|
|
39
|
+
/**
|
|
40
|
+
* Set of currently-pressed keyboard keys (lowercased).
|
|
41
|
+
*
|
|
42
|
+
* Populated on `keydown`, cleared on `keyup`.
|
|
43
|
+
*/
|
|
44
|
+
private keys: Set<string> = new Set();
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Set of currently-pressed mouse button indices.
|
|
48
|
+
*
|
|
49
|
+
* Standard mapping: `0` = left, `1` = middle, `2` = right.
|
|
50
|
+
*/
|
|
51
|
+
private mouseButtons: Set<number> = new Set();
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Last known mouse position in **client** (viewport) coordinates.
|
|
55
|
+
*
|
|
56
|
+
* @defaultValue `{ x: 0, y: 0 }`
|
|
57
|
+
*/
|
|
58
|
+
private mousePosition: { x: number; y: number } = { x: 0, y: 0 };
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates a new `Input` instance and attaches global event listeners
|
|
62
|
+
* to the `window`.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* Only one `Input` instance should exist at a time to avoid
|
|
66
|
+
* duplicate listeners. If you need to tear down, call {@link Input.reset}
|
|
67
|
+
* to clear tracked state.
|
|
68
|
+
*/
|
|
69
|
+
constructor() {
|
|
70
|
+
window.addEventListener("keydown", (e) => {
|
|
71
|
+
this.keys.add(e.key.toLowerCase());
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
window.addEventListener("keyup", (e) => {
|
|
75
|
+
this.keys.delete(e.key.toLowerCase());
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
window.addEventListener("mousedown", (e) => {
|
|
79
|
+
this.mouseButtons.add(e.button);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
window.addEventListener("mouseup", (e) => {
|
|
83
|
+
this.mouseButtons.delete(e.button);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
window.addEventListener("mousemove", (e) => {
|
|
87
|
+
this.mousePosition.x = e.clientX;
|
|
88
|
+
this.mousePosition.y = e.clientY;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Checks whether a specific key is currently held down.
|
|
94
|
+
*
|
|
95
|
+
* @param key - The key name to check (case-insensitive).
|
|
96
|
+
* Uses the standard {@link KeyboardEvent.key} values (e.g. `"a"`,
|
|
97
|
+
* `"ArrowLeft"`, `"Shift"`).
|
|
98
|
+
* @returns `true` if the key is currently pressed.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* if (input.isKeyDown("space")) {
|
|
103
|
+
* player.jump();
|
|
104
|
+
* }
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
isKeyDown(key: string): boolean {
|
|
108
|
+
return this.keys.has(key.toLowerCase());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Returns a snapshot of all keys that are currently held down.
|
|
113
|
+
*
|
|
114
|
+
* The returned `Set` is a **copy** — mutating it does not affect
|
|
115
|
+
* the internal state.
|
|
116
|
+
*
|
|
117
|
+
* @returns A new `Set<string>` of pressed key names (lowercased).
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* const pressed = input.getPressedKeys();
|
|
122
|
+
* console.log([...pressed]); // e.g. ["w", "shift"]
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
getPressedKeys(): Set<string> {
|
|
126
|
+
return new Set(this.keys);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Checks whether a specific mouse button is currently held down.
|
|
131
|
+
*
|
|
132
|
+
* @param button - The mouse button index (`0` = left, `1` = middle,
|
|
133
|
+
* `2` = right).
|
|
134
|
+
* @returns `true` if the button is currently pressed.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* if (input.isMouseButtonDown(2)) {
|
|
139
|
+
* openContextMenu();
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
isMouseButtonDown(button: number): boolean {
|
|
144
|
+
return this.mouseButtons.has(button);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Returns the last known mouse position in client (viewport)
|
|
149
|
+
* coordinates.
|
|
150
|
+
*
|
|
151
|
+
* The returned object is a **copy** — mutating it does not affect
|
|
152
|
+
* the internal state.
|
|
153
|
+
*
|
|
154
|
+
* @returns An `{ x, y }` object with the mouse coordinates.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* const pos = input.getMousePosition();
|
|
159
|
+
* ctx.fillRect(pos.x, pos.y, 4, 4); // draw cursor dot
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
getMousePosition(): { x: number; y: number } {
|
|
163
|
+
return { ...this.mousePosition };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Clears all tracked key and mouse-button state.
|
|
168
|
+
*
|
|
169
|
+
* Useful when pausing the game or switching scenes to prevent stale
|
|
170
|
+
* input from carrying over.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* engine.pause();
|
|
175
|
+
* input.reset();
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
reset(): void {
|
|
179
|
+
this.keys.clear();
|
|
180
|
+
this.mouseButtons.clear();
|
|
181
|
+
}
|
|
182
|
+
}
|