like2d 2.9.0 → 2.10.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 +21 -17
- package/dist/__benchmarks__/vector2.bench.d.ts +2 -0
- package/dist/__benchmarks__/vector2.bench.d.ts.map +1 -0
- package/dist/__benchmarks__/vector2.bench.js +74 -0
- package/dist/{core → audio}/audio.d.ts +12 -3
- package/dist/audio/audio.d.ts.map +1 -0
- package/dist/{core → audio}/audio.js +10 -2
- package/dist/audio/index.d.ts +2 -0
- package/dist/audio/index.d.ts.map +1 -0
- package/dist/audio/index.js +1 -0
- package/dist/engine.d.ts +12 -42
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +34 -76
- package/dist/{core/events.d.ts → events.d.ts} +27 -50
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +5 -0
- package/dist/gamecontrollerdb.txt +2222 -0
- package/dist/{core → graphics}/canvas.d.ts +18 -11
- package/dist/graphics/canvas.d.ts.map +1 -0
- package/dist/{core → graphics}/canvas.js +73 -58
- package/dist/{core/graphics.d.ts → graphics/drawing.d.ts} +25 -25
- package/dist/graphics/drawing.d.ts.map +1 -0
- package/dist/{core/graphics.js → graphics/drawing.js} +59 -52
- package/dist/graphics/index.d.ts +19 -0
- package/dist/graphics/index.d.ts.map +1 -0
- package/dist/graphics/index.js +13 -0
- package/dist/index.d.ts +3 -30
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -21
- package/dist/input/gamepad-mapping.d.ts +134 -0
- package/dist/input/gamepad-mapping.d.ts.map +1 -0
- package/dist/input/gamepad-mapping.js +146 -0
- package/dist/input/gamepad.d.ts +74 -0
- package/dist/input/gamepad.d.ts.map +1 -0
- package/dist/input/gamepad.js +288 -0
- package/dist/input/index.d.ts +6 -0
- package/dist/input/index.d.ts.map +1 -0
- package/dist/input/index.js +1 -0
- package/dist/input/input.d.ts +76 -0
- package/dist/input/input.d.ts.map +1 -0
- package/dist/input/input.js +164 -0
- package/dist/input/keyboard.d.ts +15 -0
- package/dist/input/keyboard.d.ts.map +1 -0
- package/dist/{core → input}/keyboard.js +11 -21
- package/dist/input/mouse.d.ts +108 -0
- package/dist/input/mouse.d.ts.map +1 -0
- package/dist/input/mouse.js +241 -0
- package/dist/like.d.ts +80 -0
- package/dist/like.d.ts.map +1 -0
- package/dist/like.js +5 -0
- package/dist/math/index.d.ts +16 -0
- package/dist/math/index.d.ts.map +1 -1
- package/dist/math/index.js +16 -0
- package/dist/math/rect.d.ts +24 -27
- package/dist/math/rect.d.ts.map +1 -1
- package/dist/math/rect.js +47 -73
- package/dist/math/vector2.d.ts +87 -32
- package/dist/math/vector2.d.ts.map +1 -1
- package/dist/math/vector2.js +92 -110
- package/dist/prefab-scenes/index.d.ts +1 -0
- package/dist/prefab-scenes/index.d.ts.map +1 -1
- package/dist/prefab-scenes/index.js +1 -0
- package/dist/prefab-scenes/mapGamepad.d.ts +28 -0
- package/dist/prefab-scenes/mapGamepad.d.ts.map +1 -0
- package/dist/prefab-scenes/mapGamepad.js +181 -0
- package/dist/prefab-scenes/startScreen.d.ts +2 -2
- package/dist/prefab-scenes/startScreen.js +2 -2
- package/dist/scene.d.ts +2 -2
- package/dist/scene.d.ts.map +1 -1
- package/dist/timer/index.d.ts +2 -0
- package/dist/timer/index.d.ts.map +1 -0
- package/dist/timer/index.js +1 -0
- package/dist/timer/timer.d.ts +32 -0
- package/dist/timer/timer.d.ts.map +1 -0
- package/dist/{core → timer}/timer.js +20 -3
- package/package.json +22 -13
- package/dist/core/audio.d.ts.map +0 -1
- package/dist/core/canvas.d.ts.map +0 -1
- package/dist/core/events.d.ts.map +0 -1
- package/dist/core/events.js +0 -21
- package/dist/core/gamepad-mapping.d.ts +0 -58
- package/dist/core/gamepad-mapping.d.ts.map +0 -1
- package/dist/core/gamepad-mapping.js +0 -23
- package/dist/core/gamepad.d.ts +0 -37
- package/dist/core/gamepad.d.ts.map +0 -1
- package/dist/core/gamepad.js +0 -103
- package/dist/core/graphics.d.ts.map +0 -1
- package/dist/core/input-state.d.ts +0 -14
- package/dist/core/input-state.d.ts.map +0 -1
- package/dist/core/input-state.js +0 -50
- package/dist/core/input.d.ts +0 -40
- package/dist/core/input.d.ts.map +0 -1
- package/dist/core/input.js +0 -105
- package/dist/core/keyboard.d.ts +0 -15
- package/dist/core/keyboard.d.ts.map +0 -1
- package/dist/core/like.d.ts +0 -113
- package/dist/core/like.d.ts.map +0 -1
- package/dist/core/like.js +0 -9
- package/dist/core/mouse.d.ts +0 -52
- package/dist/core/mouse.d.ts.map +0 -1
- package/dist/core/mouse.js +0 -142
- package/dist/core/timer.d.ts +0 -15
- package/dist/core/timer.d.ts.map +0 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { defaultMapping, fullButtonName, getSdlMapping, mapStick } from './gamepad-mapping';
|
|
2
|
+
/** LIKE Gamepad Wrapper
|
|
3
|
+
*
|
|
4
|
+
* - Allows events/callbacks to be sent from joy buttons.
|
|
5
|
+
* - Can track if any gamepad has a button pressed / just pressed.
|
|
6
|
+
* - Remaps raw input numbers to readable strings -- by default using SDL database.
|
|
7
|
+
*
|
|
8
|
+
* If you're planning on supporting gamepads, please include a
|
|
9
|
+
* way to generate {@link GamepadMapping} and set it with {@link Gamepad.setMapping}.
|
|
10
|
+
*
|
|
11
|
+
* If you don't want to make your own, take a look at `prefab-scenes/mapGamepad`.
|
|
12
|
+
*/
|
|
13
|
+
export class Gamepad {
|
|
14
|
+
constructor(props) {
|
|
15
|
+
Object.defineProperty(this, "dispatch", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: void 0
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "gamepads", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: {}
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(this, "autoLoadMapping", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: true
|
|
32
|
+
});
|
|
33
|
+
this.dispatch = props.dispatch;
|
|
34
|
+
const { abort } = props;
|
|
35
|
+
// Register event listeners
|
|
36
|
+
window.addEventListener("gamepadconnected", this.onGamepadConnected.bind(this), {
|
|
37
|
+
signal: abort,
|
|
38
|
+
});
|
|
39
|
+
window.addEventListener("gamepaddisconnected", (ev) => {
|
|
40
|
+
console.log(`[Gamepad] Disconnected ${ev.gamepad.id}`);
|
|
41
|
+
delete this.gamepads[ev.gamepad.index];
|
|
42
|
+
this.dispatch("gamepaddisconnected", [ev.gamepad.index]);
|
|
43
|
+
}, { signal: abort });
|
|
44
|
+
window.addEventListener("blur", () => {
|
|
45
|
+
for (const gps of Object.values(this.gamepads)) {
|
|
46
|
+
gps.clear();
|
|
47
|
+
}
|
|
48
|
+
}, { signal: abort });
|
|
49
|
+
}
|
|
50
|
+
onGamepadConnected(ev) {
|
|
51
|
+
const gps = new GamepadState(ev.gamepad.index);
|
|
52
|
+
this.gamepads[ev.gamepad.index] = gps;
|
|
53
|
+
console.log(`[Gamepad] Connected ${ev.gamepad.id}. buttons: ${ev.gamepad.buttons.length}, axes: ${ev.gamepad.axes.length}`);
|
|
54
|
+
const mapping = this.loadMapping(ev.gamepad.index);
|
|
55
|
+
if (this.autoLoadMapping && mapping) {
|
|
56
|
+
gps.mapping = mapping;
|
|
57
|
+
console.log(`[Gamepad] Applied presaved mapping.`);
|
|
58
|
+
}
|
|
59
|
+
else if (ev.gamepad.mapping == 'standard') {
|
|
60
|
+
gps.mapping = defaultMapping(ev.gamepad.axes.length / 2);
|
|
61
|
+
console.log(`Loaded standard mapping.`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const sdlMapping = getSdlMapping(ev.gamepad);
|
|
65
|
+
if (sdlMapping) {
|
|
66
|
+
gps.mapping.buttons = sdlMapping.mapping;
|
|
67
|
+
console.log(`[Gamepad] Connected, applied SDL database mapping.`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.log(`[Gamepad] Could not find mapping for gamepad. Consider remapping it.`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
this.dispatch("gamepadconnected", [ev.gamepad.index]);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @private
|
|
77
|
+
* Called by the engine every frame.
|
|
78
|
+
*/
|
|
79
|
+
update() {
|
|
80
|
+
Object.values(this.gamepads).forEach((gp) => gp.update(this.dispatch));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
*
|
|
84
|
+
* @param target Which controller?
|
|
85
|
+
* @returns all of the sticks. Convention is 0 = left, 1 = right.
|
|
86
|
+
*/
|
|
87
|
+
getSticks(target) {
|
|
88
|
+
const gp = this.gamepads[target];
|
|
89
|
+
if (gp) {
|
|
90
|
+
return gp.getSticks();
|
|
91
|
+
}
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
fullButtonName(name) {
|
|
95
|
+
return fullButtonName.get(name) ?? name;
|
|
96
|
+
}
|
|
97
|
+
checkButton(target, button, mode) {
|
|
98
|
+
if (target == "any") {
|
|
99
|
+
return Object.values(this.gamepads).some((gp) => gp[mode](button));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return this.gamepads[target]?.[mode](button);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Check if a gamepad button is down. */
|
|
106
|
+
isDown(target, button) {
|
|
107
|
+
return this.checkButton(target, button, "isDown");
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Returns true for only one frame/update if a button is pressed.
|
|
111
|
+
* Considered an alternative to `like.gamepadpressed`.
|
|
112
|
+
*/
|
|
113
|
+
justPressed(target, button) {
|
|
114
|
+
return this.checkButton(target, button, "justPressed");
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get a controller mapping.
|
|
118
|
+
* Note that modifying this mapping in place will modify the target controller.
|
|
119
|
+
* However, use `setMapping` to finalize the mapping.
|
|
120
|
+
*/
|
|
121
|
+
getMapping(index) {
|
|
122
|
+
return this.gamepads[index]?.mapping;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Set the mapping for a particular controller.
|
|
126
|
+
*
|
|
127
|
+
* Set `save = false` if you don't want this written into localstorage.
|
|
128
|
+
*/
|
|
129
|
+
setMapping(index, mapping, save = true) {
|
|
130
|
+
const gp = this.gamepads[index];
|
|
131
|
+
if (gp) {
|
|
132
|
+
gp.mapping = mapping;
|
|
133
|
+
if (save) {
|
|
134
|
+
this.saveMapping(index, mapping);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get saved mapping from db, if it exists.
|
|
140
|
+
*/
|
|
141
|
+
loadMapping(index) {
|
|
142
|
+
const gp = navigator.getGamepads()?.[index];
|
|
143
|
+
if (gp) {
|
|
144
|
+
const path = getLocalstoragePath(gp);
|
|
145
|
+
console.log(`[Gamepad] Found saved mapping for ${gp.id}.`);
|
|
146
|
+
const item = localStorage.getItem(path);
|
|
147
|
+
if (item) {
|
|
148
|
+
return JSON.parse(item);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Save a mapping to persistant storage
|
|
154
|
+
*/
|
|
155
|
+
saveMapping(index, mapping) {
|
|
156
|
+
const gp = navigator.getGamepads()?.[index];
|
|
157
|
+
if (gp) {
|
|
158
|
+
const path = getLocalstoragePath(gp);
|
|
159
|
+
console.log(`[Gamepad] Saved mapping ${path} to localStorage.`);
|
|
160
|
+
localStorage.setItem(path, JSON.stringify(mapping));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Enable automatically loading mappings.
|
|
165
|
+
*
|
|
166
|
+
* When a gamepad with a known (to this system) ID is plugged in,
|
|
167
|
+
* this will load the previously saved mapping.
|
|
168
|
+
*
|
|
169
|
+
* @param enable
|
|
170
|
+
*/
|
|
171
|
+
enableAutoLoadMapping(enable) {
|
|
172
|
+
this.autoLoadMapping = enable;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function getLocalstoragePath(gamepad) {
|
|
176
|
+
return `${gamepad.axes.length}A${gamepad.buttons.length}B${gamepad.id}`;
|
|
177
|
+
}
|
|
178
|
+
const maxButtons = 64;
|
|
179
|
+
/** Internal class: Using it externally could result
|
|
180
|
+
* in a gamepad being disconnected and still trying to maintain
|
|
181
|
+
* its state.
|
|
182
|
+
*/
|
|
183
|
+
class GamepadState {
|
|
184
|
+
constructor(index) {
|
|
185
|
+
Object.defineProperty(this, "index", {
|
|
186
|
+
enumerable: true,
|
|
187
|
+
configurable: true,
|
|
188
|
+
writable: true,
|
|
189
|
+
value: index
|
|
190
|
+
});
|
|
191
|
+
Object.defineProperty(this, "mapping", {
|
|
192
|
+
enumerable: true,
|
|
193
|
+
configurable: true,
|
|
194
|
+
writable: true,
|
|
195
|
+
value: void 0
|
|
196
|
+
});
|
|
197
|
+
Object.defineProperty(this, "downNums", {
|
|
198
|
+
enumerable: true,
|
|
199
|
+
configurable: true,
|
|
200
|
+
writable: true,
|
|
201
|
+
value: []
|
|
202
|
+
});
|
|
203
|
+
Object.defineProperty(this, "lastDownNums", {
|
|
204
|
+
enumerable: true,
|
|
205
|
+
configurable: true,
|
|
206
|
+
writable: true,
|
|
207
|
+
value: []
|
|
208
|
+
});
|
|
209
|
+
Object.defineProperty(this, "down", {
|
|
210
|
+
enumerable: true,
|
|
211
|
+
configurable: true,
|
|
212
|
+
writable: true,
|
|
213
|
+
value: {}
|
|
214
|
+
});
|
|
215
|
+
Object.defineProperty(this, "lastDown", {
|
|
216
|
+
enumerable: true,
|
|
217
|
+
configurable: true,
|
|
218
|
+
writable: true,
|
|
219
|
+
value: {}
|
|
220
|
+
});
|
|
221
|
+
const gp = navigator.getGamepads()[this.index];
|
|
222
|
+
this.mapping = defaultMapping(gp.axes.length);
|
|
223
|
+
}
|
|
224
|
+
isDown(button) {
|
|
225
|
+
return typeof (button) == "number" ? !!this.downNums[button] : !!this.down[button];
|
|
226
|
+
}
|
|
227
|
+
justPressed(button) {
|
|
228
|
+
return typeof (button) == "number" ?
|
|
229
|
+
(!!this.downNums[button] && !this.lastDownNums[button]) :
|
|
230
|
+
(!!this.down[button] && !this.lastDown[button]);
|
|
231
|
+
}
|
|
232
|
+
map(button) {
|
|
233
|
+
return (this.mapping.buttons[button] ??
|
|
234
|
+
(button < maxButtons
|
|
235
|
+
? `Button${button}`
|
|
236
|
+
: `Axis${Math.floor((button - maxButtons) / 2)}${button % 2 ? "-" : "+"}`));
|
|
237
|
+
}
|
|
238
|
+
update(dispatch) {
|
|
239
|
+
const gp = navigator.getGamepads()[this.index];
|
|
240
|
+
if (!gp)
|
|
241
|
+
return;
|
|
242
|
+
[this.downNums, this.lastDownNums] = [this.lastDownNums, this.downNums];
|
|
243
|
+
for (const k in this.downNums) {
|
|
244
|
+
delete this.downNums[Number(k)];
|
|
245
|
+
}
|
|
246
|
+
gp.buttons.forEach((btn, i) => {
|
|
247
|
+
if (btn.pressed) {
|
|
248
|
+
this.downNums[i] = true;
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
gp.axes.forEach((axis, i) => {
|
|
252
|
+
const pos = Math.round(axis);
|
|
253
|
+
if (pos == 0)
|
|
254
|
+
return;
|
|
255
|
+
const index = 64 + i * 2 + (pos == -1 ? 1 : 0);
|
|
256
|
+
this.downNums[index] = true;
|
|
257
|
+
});
|
|
258
|
+
[this.down, this.lastDown] = [this.lastDown, this.down];
|
|
259
|
+
for (const k in this.down) {
|
|
260
|
+
delete this.down[k];
|
|
261
|
+
}
|
|
262
|
+
for (const i in this.downNums) {
|
|
263
|
+
const name = this.map(Number(i));
|
|
264
|
+
this.down[name] = true;
|
|
265
|
+
if (!this.lastDownNums[Number(i)]) {
|
|
266
|
+
dispatch("gamepadpressed", [this.index, name, Number(i)]);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
for (const i in this.lastDownNums) {
|
|
270
|
+
if (!this.downNums[Number(i)]) {
|
|
271
|
+
const name = this.map(Number(i));
|
|
272
|
+
dispatch("gamepadreleased", [this.index, name, Number(i)]);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
getSticks() {
|
|
277
|
+
const gp = navigator.getGamepads()[this.index];
|
|
278
|
+
return this.mapping.sticks.map((stick) => mapStick(gp, stick));
|
|
279
|
+
}
|
|
280
|
+
clear() {
|
|
281
|
+
for (const k in this.lastDown) {
|
|
282
|
+
delete this.lastDown[k];
|
|
283
|
+
}
|
|
284
|
+
for (const k in this.down) {
|
|
285
|
+
delete this.down[k];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { Input, InputType, InputBinding, } from "./input";
|
|
2
|
+
export type { GamepadTarget, Gamepad, } from "./gamepad";
|
|
3
|
+
export type { LikeButton, GamepadMapping, StickMapping, StickAxisMapping, } from "./gamepad-mapping";
|
|
4
|
+
export { defaultMapping, allButtons } from "./gamepad-mapping";
|
|
5
|
+
export type { MouseSetMode, Mouse, } from "./mouse";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/input/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,KAAK,EACL,SAAS,EACT,YAAY,GACb,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,aAAa,EACb,OAAO,GACR,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,UAAU,EACV,cAAc,EACd,YAAY,EACZ,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/D,YAAY,EACV,YAAY,EACZ,KAAK,GACN,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { defaultMapping, allButtons } from "./gamepad-mapping";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Keyboard } from './keyboard';
|
|
2
|
+
import type { Mouse } from './mouse';
|
|
3
|
+
import { Gamepad, GamepadTarget } from './gamepad';
|
|
4
|
+
import { LikeButton } from './gamepad-mapping';
|
|
5
|
+
import { MouseButton, Dispatcher } from '../events';
|
|
6
|
+
export type InputType = InputBinding['type'];
|
|
7
|
+
export type InputBinding = {
|
|
8
|
+
type: 'keyboard';
|
|
9
|
+
scancode: string;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'mouse';
|
|
12
|
+
button: MouseButton;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'gamepad';
|
|
15
|
+
gamepad: GamepadTarget;
|
|
16
|
+
button: LikeButton;
|
|
17
|
+
};
|
|
18
|
+
export declare class Input {
|
|
19
|
+
private dispatch;
|
|
20
|
+
private currState;
|
|
21
|
+
private prevState;
|
|
22
|
+
private actionTable;
|
|
23
|
+
private keyboard;
|
|
24
|
+
private mouse;
|
|
25
|
+
private gamepad;
|
|
26
|
+
constructor(dispatch: Dispatcher<'actionpressed' | 'actionreleased'>, deps: {
|
|
27
|
+
keyboard: Keyboard;
|
|
28
|
+
mouse: Mouse;
|
|
29
|
+
gamepad: Gamepad;
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* This is the easiest way to set-and-forget input => action mapping.
|
|
33
|
+
*
|
|
34
|
+
* Or, it's a helper to remove actions -- `setAction(action)`
|
|
35
|
+
* will simply clear the action away.
|
|
36
|
+
*
|
|
37
|
+
* For input strings:
|
|
38
|
+
* - Mouse is `MouseLeft`, `MouseRight`, or `MouseMiddle`.
|
|
39
|
+
* - Joypad is 'Left', 'L1', or any joypad button. {@link LikeButton}
|
|
40
|
+
* - Keyboard is the name of scancodes, which are based on key positions. Choose from a subset of portable, web-safe scancodes:
|
|
41
|
+
* - Alphabetical: `KeyA`, `KeyB`, ...
|
|
42
|
+
* - Numeric: `Digit0`, `Digit1`, ...
|
|
43
|
+
* - `ArrowLeft`, `ArrowRight`, `ArrowUp`, `ArrowDown`
|
|
44
|
+
* - `ShiftLeft`, `ShiftRight`
|
|
45
|
+
* - `Minus`
|
|
46
|
+
* - `Equal` (also has a plus sign)
|
|
47
|
+
* - `BracketLeft` and `BracketRight`
|
|
48
|
+
* - `Semicolon`
|
|
49
|
+
* - `Quote`
|
|
50
|
+
* - `Backquote` (also has tilde)
|
|
51
|
+
* - `Backslash`
|
|
52
|
+
* - `Comma`
|
|
53
|
+
* - `Period`
|
|
54
|
+
* - `Slash`
|
|
55
|
+
* - `Backspace`
|
|
56
|
+
* - `Enter`
|
|
57
|
+
*
|
|
58
|
+
* @param action
|
|
59
|
+
* @param inputs
|
|
60
|
+
*/
|
|
61
|
+
setAction(action: string, inputs?: (string | InputBinding)[]): void;
|
|
62
|
+
/**
|
|
63
|
+
* Map a single input onto an action.
|
|
64
|
+
*/
|
|
65
|
+
appendToAction(action: string, input: InputBinding | string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Get the mapping entry for a specific action.
|
|
68
|
+
*/
|
|
69
|
+
getActionMapping(action: string): InputBinding[];
|
|
70
|
+
isDown(action: string): boolean;
|
|
71
|
+
justPressed(action: string): boolean;
|
|
72
|
+
justReleased(action: string): boolean;
|
|
73
|
+
update(): void;
|
|
74
|
+
private isBindingActive;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/input/input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAc,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEpD,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AAC7C,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAEpE,qBAAa,KAAK;IASd,OAAO,CAAC,QAAQ;IARlB,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,OAAO,CAAU;gBAGf,QAAQ,EAAE,UAAU,CAAC,eAAe,GAAG,gBAAgB,CAAC,EAChE,IAAI,EAAE;QACJ,QAAQ,EAAE,QAAQ,CAAC;QACnB,KAAK,EAAE,KAAK,CAAC;QACb,OAAO,EAAE,OAAO,CAAC;KAClB;IAOH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,CAAC,MAAM,GAAG,YAAY,CAAC,EAAO,GAAG,IAAI;IAUvE;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM;IAM3D;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE;IAIhD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAM/B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIpC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIrC,MAAM;IAiBN,OAAO,CAAC,eAAe;CAaxB"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { allButtons } from './gamepad-mapping';
|
|
2
|
+
export class Input {
|
|
3
|
+
constructor(dispatch, deps) {
|
|
4
|
+
Object.defineProperty(this, "dispatch", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: dispatch
|
|
9
|
+
});
|
|
10
|
+
Object.defineProperty(this, "currState", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
writable: true,
|
|
14
|
+
value: new Set()
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(this, "prevState", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: new Set()
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(this, "actionTable", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: {}
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "keyboard", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: void 0
|
|
33
|
+
});
|
|
34
|
+
Object.defineProperty(this, "mouse", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
configurable: true,
|
|
37
|
+
writable: true,
|
|
38
|
+
value: void 0
|
|
39
|
+
});
|
|
40
|
+
Object.defineProperty(this, "gamepad", {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
writable: true,
|
|
44
|
+
value: void 0
|
|
45
|
+
});
|
|
46
|
+
this.keyboard = deps.keyboard;
|
|
47
|
+
this.mouse = deps.mouse;
|
|
48
|
+
this.gamepad = deps.gamepad;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* This is the easiest way to set-and-forget input => action mapping.
|
|
52
|
+
*
|
|
53
|
+
* Or, it's a helper to remove actions -- `setAction(action)`
|
|
54
|
+
* will simply clear the action away.
|
|
55
|
+
*
|
|
56
|
+
* For input strings:
|
|
57
|
+
* - Mouse is `MouseLeft`, `MouseRight`, or `MouseMiddle`.
|
|
58
|
+
* - Joypad is 'Left', 'L1', or any joypad button. {@link LikeButton}
|
|
59
|
+
* - Keyboard is the name of scancodes, which are based on key positions. Choose from a subset of portable, web-safe scancodes:
|
|
60
|
+
* - Alphabetical: `KeyA`, `KeyB`, ...
|
|
61
|
+
* - Numeric: `Digit0`, `Digit1`, ...
|
|
62
|
+
* - `ArrowLeft`, `ArrowRight`, `ArrowUp`, `ArrowDown`
|
|
63
|
+
* - `ShiftLeft`, `ShiftRight`
|
|
64
|
+
* - `Minus`
|
|
65
|
+
* - `Equal` (also has a plus sign)
|
|
66
|
+
* - `BracketLeft` and `BracketRight`
|
|
67
|
+
* - `Semicolon`
|
|
68
|
+
* - `Quote`
|
|
69
|
+
* - `Backquote` (also has tilde)
|
|
70
|
+
* - `Backslash`
|
|
71
|
+
* - `Comma`
|
|
72
|
+
* - `Period`
|
|
73
|
+
* - `Slash`
|
|
74
|
+
* - `Backspace`
|
|
75
|
+
* - `Enter`
|
|
76
|
+
*
|
|
77
|
+
* @param action
|
|
78
|
+
* @param inputs
|
|
79
|
+
*/
|
|
80
|
+
setAction(action, inputs = []) {
|
|
81
|
+
if (inputs.length) {
|
|
82
|
+
this.actionTable[action] = inputs.map(parseInput);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
delete this.actionTable[action];
|
|
86
|
+
this.currState.delete(action);
|
|
87
|
+
this.prevState.delete(action);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Map a single input onto an action.
|
|
92
|
+
*/
|
|
93
|
+
appendToAction(action, input) {
|
|
94
|
+
const am = this.actionTable[action] ?? [];
|
|
95
|
+
am.push(parseInput(input));
|
|
96
|
+
this.actionTable[action] = am;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get the mapping entry for a specific action.
|
|
100
|
+
*/
|
|
101
|
+
getActionMapping(action) {
|
|
102
|
+
return this.actionTable[action] ?? [];
|
|
103
|
+
}
|
|
104
|
+
isDown(action) {
|
|
105
|
+
const bindings = this.actionTable[action];
|
|
106
|
+
if (!bindings)
|
|
107
|
+
return false;
|
|
108
|
+
return bindings.some((binding) => this.isBindingActive(binding));
|
|
109
|
+
}
|
|
110
|
+
justPressed(action) {
|
|
111
|
+
return this.currState.has(action) && !this.prevState.has(action);
|
|
112
|
+
}
|
|
113
|
+
justReleased(action) {
|
|
114
|
+
return !this.currState.has(action) && this.prevState.has(action);
|
|
115
|
+
}
|
|
116
|
+
update() {
|
|
117
|
+
this.gamepad.update();
|
|
118
|
+
[this.prevState, this.currState] = [this.currState, this.prevState];
|
|
119
|
+
this.currState.clear();
|
|
120
|
+
for (const action of Object.keys(this.actionTable)) {
|
|
121
|
+
if (this.isDown(action)) {
|
|
122
|
+
if (!this.prevState.has(action)) {
|
|
123
|
+
this.dispatch('actionpressed', [action]);
|
|
124
|
+
}
|
|
125
|
+
this.currState.add(action);
|
|
126
|
+
}
|
|
127
|
+
else if (this.prevState.has(action)) {
|
|
128
|
+
this.dispatch('actionreleased', [action]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
isBindingActive(binding) {
|
|
133
|
+
switch (binding.type) {
|
|
134
|
+
case "keyboard":
|
|
135
|
+
return this.keyboard.isDown(binding.scancode);
|
|
136
|
+
case "mouse":
|
|
137
|
+
return this.mouse.isDown(binding.button);
|
|
138
|
+
case "gamepad": {
|
|
139
|
+
return !!this.gamepad.isDown(binding.gamepad, binding.button);
|
|
140
|
+
}
|
|
141
|
+
default:
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function parseInput(input) {
|
|
147
|
+
if (typeof input !== "string")
|
|
148
|
+
return input;
|
|
149
|
+
const normalized = input.trim();
|
|
150
|
+
if (normalized.startsWith("Mouse")) {
|
|
151
|
+
const buttonCode = normalized.replace("Mouse", "").toLowerCase();
|
|
152
|
+
return { type: "mouse", button: buttonCode };
|
|
153
|
+
}
|
|
154
|
+
if (allButtons.has(input) ||
|
|
155
|
+
normalized.startsWith("Button") ||
|
|
156
|
+
normalized.startsWith("Axis")) {
|
|
157
|
+
return {
|
|
158
|
+
type: "gamepad",
|
|
159
|
+
gamepad: "any",
|
|
160
|
+
button: normalized,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return { type: "keyboard", scancode: normalized };
|
|
164
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { EngineProps } from "../engine";
|
|
2
|
+
type KEvent = 'keypressed' | 'keyreleased';
|
|
3
|
+
export declare class Keyboard {
|
|
4
|
+
private pressedScancodes;
|
|
5
|
+
private canvas;
|
|
6
|
+
private dispatch;
|
|
7
|
+
constructor(props: EngineProps<KEvent>);
|
|
8
|
+
private handleKeyDown;
|
|
9
|
+
private handleKeyUp;
|
|
10
|
+
private handleBlur;
|
|
11
|
+
isDown(scancode: string): boolean;
|
|
12
|
+
isAnyDown(...scancodes: string[]): boolean;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=keyboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../src/input/keyboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGxC,KAAK,MAAM,GAAG,YAAY,GAAG,aAAa,CAAC;AAE3C,qBAAa,QAAQ;IACnB,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,QAAQ,CAAqB;gBAEzB,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC;IAUtC,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,UAAU;IAIlB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIjC,SAAS,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO;CAG3C"}
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
export class
|
|
2
|
-
constructor(
|
|
3
|
-
Object.defineProperty(this, "dispatch", {
|
|
4
|
-
enumerable: true,
|
|
5
|
-
configurable: true,
|
|
6
|
-
writable: true,
|
|
7
|
-
value: dispatch
|
|
8
|
-
});
|
|
1
|
+
export class Keyboard {
|
|
2
|
+
constructor(props) {
|
|
9
3
|
Object.defineProperty(this, "pressedScancodes", {
|
|
10
4
|
enumerable: true,
|
|
11
5
|
configurable: true,
|
|
@@ -16,20 +10,20 @@ export class KeyboardInternal {
|
|
|
16
10
|
enumerable: true,
|
|
17
11
|
configurable: true,
|
|
18
12
|
writable: true,
|
|
19
|
-
value:
|
|
13
|
+
value: void 0
|
|
20
14
|
});
|
|
21
|
-
Object.defineProperty(this, "
|
|
15
|
+
Object.defineProperty(this, "dispatch", {
|
|
22
16
|
enumerable: true,
|
|
23
17
|
configurable: true,
|
|
24
18
|
writable: true,
|
|
25
|
-
value:
|
|
19
|
+
value: void 0
|
|
26
20
|
});
|
|
27
|
-
this.canvas = canvas;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
21
|
+
this.canvas = props.canvas;
|
|
22
|
+
this.dispatch = props.dispatch;
|
|
23
|
+
const { abort } = props;
|
|
24
|
+
this.canvas.addEventListener('keydown', this.handleKeyDown.bind(this), { signal: abort });
|
|
25
|
+
this.canvas.addEventListener('keyup', this.handleKeyUp.bind(this), { signal: abort });
|
|
26
|
+
this.canvas.addEventListener('blur', this.handleBlur.bind(this), { signal: abort });
|
|
33
27
|
}
|
|
34
28
|
handleKeyDown(e) {
|
|
35
29
|
const scrollKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Space', 'PageUp', 'PageDown', 'Home', 'End'];
|
|
@@ -50,10 +44,6 @@ export class KeyboardInternal {
|
|
|
50
44
|
handleBlur() {
|
|
51
45
|
this.pressedScancodes.clear();
|
|
52
46
|
}
|
|
53
|
-
_dispose() {
|
|
54
|
-
this.abort.abort();
|
|
55
|
-
this.pressedScancodes.clear();
|
|
56
|
-
}
|
|
57
47
|
isDown(scancode) {
|
|
58
48
|
return this.pressedScancodes.has(scancode);
|
|
59
49
|
}
|