like2d 2.9.0 → 2.10.1
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/{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/controllerdb.json +1 -0
- package/dist/input/gamepad-mapping.d.ts +119 -0
- package/dist/input/gamepad-mapping.d.ts.map +1 -0
- package/dist/input/gamepad-mapping.js +114 -0
- package/dist/input/gamepad.d.ts +73 -0
- package/dist/input/gamepad.d.ts.map +1 -0
- package/dist/input/gamepad.js +291 -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 +163 -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 +30 -0
- package/dist/prefab-scenes/mapGamepad.d.ts.map +1 -0
- package/dist/prefab-scenes/mapGamepad.js +192 -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 +21 -12
- 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,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module GamepadMapping
|
|
3
|
+
*
|
|
4
|
+
* A database, generated on module load,
|
|
5
|
+
* which uses SDL's database to coerce
|
|
6
|
+
* browser APIs into physical gamepad button
|
|
7
|
+
* mappings.
|
|
8
|
+
*
|
|
9
|
+
*
|
|
10
|
+
* Browser API shortcomings:
|
|
11
|
+
* - [No standard way of exposing vendor/product, Safari doesn't even do it.](https://github.com/w3c/gamepad/issues/199)
|
|
12
|
+
* - Vendor and product alone doesn't suffice for GUID -- Different controllers have the same, it's good-enough.
|
|
13
|
+
* - D-pads either get mapped to an axis (last pair of axes in Chromium) or buttons (Firefox).
|
|
14
|
+
* - Analog axes get mapped differently in Firefox and Chromium.
|
|
15
|
+
*
|
|
16
|
+
* How we overcome them:
|
|
17
|
+
* - We parse out vendor and product based on currently known formats.
|
|
18
|
+
* - We go with best-match and always fall back on manual mapping.
|
|
19
|
+
*/
|
|
20
|
+
import type { Vector2 } from "../math";
|
|
21
|
+
/**
|
|
22
|
+
* ref: https://www.w3.org/TR/gamepad/#dfn-standard-gamepad
|
|
23
|
+
* note: `num` is only the corresponding number on standard mapping above.
|
|
24
|
+
*
|
|
25
|
+
* The point of the mapping system is to apply that _or_ non-standard mappings,
|
|
26
|
+
* Which are exceedingly common.
|
|
27
|
+
*/
|
|
28
|
+
declare const buttonMap: readonly [{
|
|
29
|
+
readonly like: "BBottom";
|
|
30
|
+
readonly num: number;
|
|
31
|
+
readonly name: "Bottom Face Button";
|
|
32
|
+
}, {
|
|
33
|
+
readonly like: "BRight";
|
|
34
|
+
readonly num: 1;
|
|
35
|
+
readonly name: "Right Face Button";
|
|
36
|
+
}, {
|
|
37
|
+
readonly like: "BLeft";
|
|
38
|
+
readonly num: 2;
|
|
39
|
+
readonly name: "Left Face Button";
|
|
40
|
+
}, {
|
|
41
|
+
readonly like: "BTop";
|
|
42
|
+
readonly num: 3;
|
|
43
|
+
readonly name: "Top Face Button";
|
|
44
|
+
}, {
|
|
45
|
+
readonly like: "L1";
|
|
46
|
+
readonly num: 4;
|
|
47
|
+
readonly name: "Left shoulder (front)";
|
|
48
|
+
}, {
|
|
49
|
+
readonly like: "R1";
|
|
50
|
+
readonly num: 5;
|
|
51
|
+
readonly name: "Right shoulder (front)";
|
|
52
|
+
}, {
|
|
53
|
+
readonly like: "L2";
|
|
54
|
+
readonly num: 6;
|
|
55
|
+
readonly name: "Left shoulder (rear)";
|
|
56
|
+
}, {
|
|
57
|
+
readonly like: "R2";
|
|
58
|
+
readonly num: 7;
|
|
59
|
+
readonly name: "Right shoulder (rear)";
|
|
60
|
+
}, {
|
|
61
|
+
readonly like: "MenuLeft";
|
|
62
|
+
readonly num: 8;
|
|
63
|
+
readonly name: "Left Menu Button";
|
|
64
|
+
}, {
|
|
65
|
+
readonly like: "MenuRight";
|
|
66
|
+
readonly num: 9;
|
|
67
|
+
readonly name: "Right Menu Button";
|
|
68
|
+
}, {
|
|
69
|
+
readonly like: "LeftStick";
|
|
70
|
+
readonly num: 10;
|
|
71
|
+
readonly name: "Left Stick Button";
|
|
72
|
+
}, {
|
|
73
|
+
readonly like: "RightStick";
|
|
74
|
+
readonly num: 11;
|
|
75
|
+
readonly name: "Right Stick Button";
|
|
76
|
+
}, {
|
|
77
|
+
readonly like: "Up";
|
|
78
|
+
readonly num: 12;
|
|
79
|
+
readonly name: "D-Pad Up";
|
|
80
|
+
}, {
|
|
81
|
+
readonly like: "Down";
|
|
82
|
+
readonly num: 13;
|
|
83
|
+
readonly name: "D-Pad Down";
|
|
84
|
+
}, {
|
|
85
|
+
readonly like: "Left";
|
|
86
|
+
readonly num: 14;
|
|
87
|
+
readonly name: "D-Pad Left";
|
|
88
|
+
}, {
|
|
89
|
+
readonly like: "Right";
|
|
90
|
+
readonly num: 15;
|
|
91
|
+
readonly name: "D-Pad right";
|
|
92
|
+
}];
|
|
93
|
+
export type LikeButton = (typeof buttonMap)[number]["like"] | `Button${number}` | `Axis${number}+` | `Axis${number}-`;
|
|
94
|
+
export type GamepadMapping = {
|
|
95
|
+
buttons: ButtonMapping;
|
|
96
|
+
sticks: StickMapping[];
|
|
97
|
+
};
|
|
98
|
+
export type ButtonMapping = Record<number, LikeButton>;
|
|
99
|
+
export type StickMapping = [StickAxisMapping, StickAxisMapping];
|
|
100
|
+
export type StickAxisMapping = {
|
|
101
|
+
index: number;
|
|
102
|
+
invert: boolean;
|
|
103
|
+
};
|
|
104
|
+
export declare const defaultMapping: (stickCount: number) => GamepadMapping;
|
|
105
|
+
export declare const standardButtonMapping: () => ButtonMapping;
|
|
106
|
+
export declare const allButtons: Set<string>;
|
|
107
|
+
export declare const fullButtonName: Map<"BBottom" | "BRight" | "BLeft" | "BTop" | "L1" | "R1" | "L2" | "R2" | "MenuLeft" | "MenuRight" | "LeftStick" | "RightStick" | "Up" | "Down" | "Left" | "Right", "Bottom Face Button" | "Right Face Button" | "Left Face Button" | "Top Face Button" | "Left shoulder (front)" | "Right shoulder (front)" | "Left shoulder (rear)" | "Right shoulder (rear)" | "Left Menu Button" | "Right Menu Button" | "Left Stick Button" | "Right Stick Button" | "D-Pad Up" | "D-Pad Down" | "D-Pad Left" | "D-Pad right">;
|
|
108
|
+
export declare const mapStick: (gp: Gamepad, mapping: StickMapping) => Vector2;
|
|
109
|
+
type SdlMapping = {
|
|
110
|
+
vendor: number;
|
|
111
|
+
product: number;
|
|
112
|
+
name: string;
|
|
113
|
+
mapping: Record<number, LikeButton>;
|
|
114
|
+
browserName?: string;
|
|
115
|
+
id?: string;
|
|
116
|
+
};
|
|
117
|
+
export declare function getSdlMapping(gamepad: Gamepad): SdlMapping | undefined;
|
|
118
|
+
export {};
|
|
119
|
+
//# sourceMappingURL=gamepad-mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gamepad-mapping.d.ts","sourceRoot":"","sources":["../../src/input/gamepad-mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC;;;;;;GAMG;AACH,QAAA,MAAM,SAAS;;kBACgB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoB3B,CAAC;AAUX,MAAM,MAAM,UAAU,GAClB,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAClC,SAAS,MAAM,EAAE,GACjB,OAAO,MAAM,GAAG,GAChB,OAAO,MAAM,GAAG,CAAC;AAIrB,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AACvD,MAAM,MAAM,YAAY,GAAG,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;AAChE,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAElE,eAAO,MAAM,cAAc,GAAI,YAAY,MAAM,KAAG,cAQlD,CAAC;AAEH,eAAO,MAAM,qBAAqB,QAAO,aAC0B,CAAC;AACpE,eAAO,MAAM,UAAU,aAAqD,CAAC;AAC7E,eAAO,MAAM,cAAc,qfAE1B,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,IAAI,OAAO,EAAE,SAAS,YAAY,KAAG,OAK7D,CAAC;AAkBF,KAAK,UAAU,GAAG;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAwBtE"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module GamepadMapping
|
|
3
|
+
*
|
|
4
|
+
* A database, generated on module load,
|
|
5
|
+
* which uses SDL's database to coerce
|
|
6
|
+
* browser APIs into physical gamepad button
|
|
7
|
+
* mappings.
|
|
8
|
+
*
|
|
9
|
+
*
|
|
10
|
+
* Browser API shortcomings:
|
|
11
|
+
* - [No standard way of exposing vendor/product, Safari doesn't even do it.](https://github.com/w3c/gamepad/issues/199)
|
|
12
|
+
* - Vendor and product alone doesn't suffice for GUID -- Different controllers have the same, it's good-enough.
|
|
13
|
+
* - D-pads either get mapped to an axis (last pair of axes in Chromium) or buttons (Firefox).
|
|
14
|
+
* - Analog axes get mapped differently in Firefox and Chromium.
|
|
15
|
+
*
|
|
16
|
+
* How we overcome them:
|
|
17
|
+
* - We parse out vendor and product based on currently known formats.
|
|
18
|
+
* - We go with best-match and always fall back on manual mapping.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* ref: https://www.w3.org/TR/gamepad/#dfn-standard-gamepad
|
|
22
|
+
* note: `num` is only the corresponding number on standard mapping above.
|
|
23
|
+
*
|
|
24
|
+
* The point of the mapping system is to apply that _or_ non-standard mappings,
|
|
25
|
+
* Which are exceedingly common.
|
|
26
|
+
*/
|
|
27
|
+
const buttonMap = [
|
|
28
|
+
{ like: "BBottom", num: 0, name: "Bottom Face Button" },
|
|
29
|
+
{ like: "BRight", num: 1, name: "Right Face Button" },
|
|
30
|
+
{ like: "BLeft", num: 2, name: "Left Face Button" },
|
|
31
|
+
{ like: "BTop", num: 3, name: "Top Face Button" },
|
|
32
|
+
{ like: "L1", num: 4, name: "Left shoulder (front)" },
|
|
33
|
+
{ like: "R1", num: 5, name: "Right shoulder (front)" },
|
|
34
|
+
{ like: "L2", num: 6, name: "Left shoulder (rear)" },
|
|
35
|
+
{ like: "R2", num: 7, name: "Right shoulder (rear)" },
|
|
36
|
+
{ like: "MenuLeft", num: 8, name: "Left Menu Button" },
|
|
37
|
+
{ like: "MenuRight", num: 9, name: "Right Menu Button" },
|
|
38
|
+
{ like: "LeftStick", num: 10, name: "Left Stick Button" },
|
|
39
|
+
{ like: "RightStick", num: 11, name: "Right Stick Button" },
|
|
40
|
+
{ like: "Up", num: 12, name: "D-Pad Up" },
|
|
41
|
+
{ like: "Down", num: 13, name: "D-Pad Down" },
|
|
42
|
+
{ like: "Left", num: 14, name: "D-Pad Left" },
|
|
43
|
+
{ like: "Right", num: 15, name: "D-Pad right" },
|
|
44
|
+
];
|
|
45
|
+
const detectedOs = ((s) => [
|
|
46
|
+
["Android", "Android"],
|
|
47
|
+
["iPhone", "iOS"],
|
|
48
|
+
["iPad", "iOS"],
|
|
49
|
+
["Win", "Windows"],
|
|
50
|
+
["Mac", "Mac OS X"],
|
|
51
|
+
].find(([ss]) => s.includes(ss))?.[1] ?? "Linux")(navigator.userAgent);
|
|
52
|
+
export const defaultMapping = (stickCount) => ({
|
|
53
|
+
buttons: {},
|
|
54
|
+
sticks: Array(stickCount)
|
|
55
|
+
.fill(0)
|
|
56
|
+
.map((_, i) => [
|
|
57
|
+
{ index: i * 2, invert: false },
|
|
58
|
+
{ index: i * 2 + 1, invert: false },
|
|
59
|
+
]),
|
|
60
|
+
});
|
|
61
|
+
export const standardButtonMapping = () => Object.fromEntries(buttonMap.map(({ like, num }) => [num, like]));
|
|
62
|
+
export const allButtons = new Set(buttonMap.map(({ like }) => like));
|
|
63
|
+
export const fullButtonName = new Map(buttonMap.map(({ like, name }) => [like, name]));
|
|
64
|
+
export const mapStick = (gp, mapping) => {
|
|
65
|
+
return mapping.map((axis) => (axis.invert ? -1 : 1) * (gp.axes[axis.index] ?? 0));
|
|
66
|
+
};
|
|
67
|
+
//// ************* SDL Gamepad auto-binding system ******************* ////
|
|
68
|
+
import mappingDbRaw from "./controllerdb.json";
|
|
69
|
+
const mappingDb = new Map(Object.entries(mappingDbRaw[detectedOs]).map(([k, v]) => [
|
|
70
|
+
Number(k),
|
|
71
|
+
{
|
|
72
|
+
...v,
|
|
73
|
+
mapping: Object.fromEntries(Object.entries(v.mapping).map(([k, v]) => [Number(k), v])),
|
|
74
|
+
},
|
|
75
|
+
]));
|
|
76
|
+
export function getSdlMapping(gamepad) {
|
|
77
|
+
const parsed = trySplitId(gamepad.id);
|
|
78
|
+
if (parsed) {
|
|
79
|
+
const [vendorStr, productStr, nameStr] = parsed;
|
|
80
|
+
const vendor = parseInt(vendorStr, 16);
|
|
81
|
+
const product = parseInt(productStr, 16);
|
|
82
|
+
const name = nameStr.trim();
|
|
83
|
+
const mapping = mappingDb.get(vendor * 0x10000 + product);
|
|
84
|
+
if (mapping) {
|
|
85
|
+
console.log(`[Gamepad] Found SDL db mapping for '${name}'`);
|
|
86
|
+
return {
|
|
87
|
+
...mapping,
|
|
88
|
+
browserName: name,
|
|
89
|
+
id: gamepad.id,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
console.log(`[Gamepad] No SDL db mapping found for '${gamepad.id}.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.log(`[Gamepad] Failed to parse id: ${gamepad.id}. Please report this bug with the name of your web browser.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function trySplitId(id) {
|
|
101
|
+
const infoC = id.match(/^([^(]+)\(Vendor: ([0-9a-f]+) Product: ([0-9a-f]+)/i);
|
|
102
|
+
if (infoC) {
|
|
103
|
+
// chrome pattern: Name(Vendor: VEND Product: PROD)
|
|
104
|
+
const [, name, vendor, product] = infoC;
|
|
105
|
+
return [vendor, product, name.trim()];
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// firefox pattern: VEND-PROD-Name
|
|
109
|
+
const infoF = id.split("-");
|
|
110
|
+
if (infoF.length == 3) {
|
|
111
|
+
return infoF;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { GamepadMapping, LikeButton } from './gamepad-mapping';
|
|
2
|
+
import { type LikeGamepadEvent } from '../events';
|
|
3
|
+
import { Vector2 } from '../math/vector2';
|
|
4
|
+
import { EngineProps } from '../engine';
|
|
5
|
+
/** A selector for a gamepad. */
|
|
6
|
+
export type GamepadTarget = number | "any";
|
|
7
|
+
/** LIKE Gamepad Wrapper
|
|
8
|
+
*
|
|
9
|
+
* - Allows events/callbacks to be sent from joy buttons.
|
|
10
|
+
* - Can track if any gamepad has a button pressed / just pressed.
|
|
11
|
+
* - Remaps raw input numbers to readable strings -- by default using SDL database.
|
|
12
|
+
*
|
|
13
|
+
* If you're planning on supporting gamepads, please include a
|
|
14
|
+
* way to generate {@link GamepadMapping} and set it with {@link Gamepad.setMapping}.
|
|
15
|
+
*
|
|
16
|
+
* If you don't want to make your own, take a look at `prefab-scenes/mapGamepad`.
|
|
17
|
+
*/
|
|
18
|
+
export declare class Gamepad {
|
|
19
|
+
private dispatch;
|
|
20
|
+
private gamepads;
|
|
21
|
+
private autoLoadMapping;
|
|
22
|
+
constructor(props: EngineProps<LikeGamepadEvent>);
|
|
23
|
+
private onGamepadConnected;
|
|
24
|
+
/**
|
|
25
|
+
* Called by the engine every frame.
|
|
26
|
+
*/
|
|
27
|
+
private update;
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param target Which controller?
|
|
31
|
+
* @returns all of the sticks. Convention is 0 = left, 1 = right.
|
|
32
|
+
*/
|
|
33
|
+
getSticks(target: number): Vector2[];
|
|
34
|
+
fullButtonName(name: LikeButton): string;
|
|
35
|
+
private checkButton;
|
|
36
|
+
/** Check if a gamepad button is down. */
|
|
37
|
+
isDown(target: GamepadTarget, button: LikeButton | number): boolean | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Returns true for only one frame/update if a button is pressed.
|
|
40
|
+
* Considered an alternative to `like.gamepadpressed`.
|
|
41
|
+
*/
|
|
42
|
+
justPressed(target: GamepadTarget, button: LikeButton | number): boolean | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Get a controller mapping.
|
|
45
|
+
* Note that modifying this mapping in place will modify the target controller.
|
|
46
|
+
* However, use `setMapping` to finalize the mapping.
|
|
47
|
+
*/
|
|
48
|
+
getMapping(index: number): GamepadMapping | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Set the mapping for a particular controller.
|
|
51
|
+
*
|
|
52
|
+
* Set `save = false` if you don't want this written into localstorage.
|
|
53
|
+
*/
|
|
54
|
+
setMapping(index: number, mapping: GamepadMapping, save?: boolean): void;
|
|
55
|
+
/**
|
|
56
|
+
* Get saved mapping from db, if it exists.
|
|
57
|
+
*/
|
|
58
|
+
loadMapping(index: number): GamepadMapping | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Save a mapping to persistant storage
|
|
61
|
+
*/
|
|
62
|
+
saveMapping(index: number, mapping: GamepadMapping): void;
|
|
63
|
+
/**
|
|
64
|
+
* Enable automatically loading mappings.
|
|
65
|
+
*
|
|
66
|
+
* When a gamepad with a known (to this system) ID is plugged in,
|
|
67
|
+
* this will load the previously saved mapping.
|
|
68
|
+
*
|
|
69
|
+
* @param enable
|
|
70
|
+
*/
|
|
71
|
+
enableAutoLoadMapping(enable: boolean): void;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=gamepad.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gamepad.d.ts","sourceRoot":"","sources":["../../src/input/gamepad.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,cAAc,EAAiB,UAAU,EAAmC,MAAM,mBAAmB,CAAC;AAC/I,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,gCAAgC;AAChC,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,eAAe,CAAQ;gBAEnB,KAAK,EAAE,WAAW,CAAC,gBAAgB,CAAC;IAmChD,OAAO,CAAC,kBAAkB;IAgC1B;;OAEG;IACH,OAAO,CAAC,MAAM;IAId;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE;IAQpC,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAIxC,OAAO,CAAC,WAAW;IAYnB,yCAAyC;IACzC,MAAM,CACJ,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,UAAU,GAAG,MAAM,GAC1B,OAAO,GAAG,SAAS;IAItB;;;OAGG;IACH,WAAW,CACT,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,UAAU,GAAG,MAAM,GAC1B,OAAO,GAAG,SAAS;IAItB;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIrD;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,UAAO;IAU9D;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAYtD;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc;IASlD;;;;;;;OAOG;IACH,qBAAqB,CAAC,MAAM,EAAE,OAAO;CAGtC"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { defaultMapping, fullButtonName, getSdlMapping, mapStick, standardButtonMapping } 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
|
+
props.canvas.addEventListener("like:update", this.update.bind(this), {
|
|
50
|
+
signal: abort,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
onGamepadConnected(ev) {
|
|
54
|
+
const gps = new GamepadState(ev.gamepad.index);
|
|
55
|
+
this.gamepads[ev.gamepad.index] = gps;
|
|
56
|
+
console.log(`[Gamepad] Connected ${ev.gamepad.id}. buttons: ${ev.gamepad.buttons.length}, axes: ${ev.gamepad.axes.length}`);
|
|
57
|
+
const mapping = this.loadMapping(ev.gamepad.index);
|
|
58
|
+
if (this.autoLoadMapping && mapping) {
|
|
59
|
+
gps.mapping = mapping;
|
|
60
|
+
console.log(`[Gamepad] Applied presaved mapping.`);
|
|
61
|
+
}
|
|
62
|
+
else if (ev.gamepad.mapping == 'standard') {
|
|
63
|
+
gps.mapping = defaultMapping(ev.gamepad.axes.length / 2);
|
|
64
|
+
gps.mapping.buttons = standardButtonMapping();
|
|
65
|
+
console.log(`Loaded standard mapping.`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const sdlMapping = getSdlMapping(ev.gamepad);
|
|
69
|
+
if (sdlMapping) {
|
|
70
|
+
gps.mapping.buttons = sdlMapping.mapping;
|
|
71
|
+
console.log(`[Gamepad] Connected, applied SDL database mapping.`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
console.log(`[Gamepad] Could not find mapping for gamepad. Consider remapping it.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
this.dispatch("gamepadconnected", [ev.gamepad.index]);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Called by the engine every frame.
|
|
81
|
+
*/
|
|
82
|
+
update() {
|
|
83
|
+
Object.values(this.gamepads).forEach((gp) => gp.update(this.dispatch));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
*
|
|
87
|
+
* @param target Which controller?
|
|
88
|
+
* @returns all of the sticks. Convention is 0 = left, 1 = right.
|
|
89
|
+
*/
|
|
90
|
+
getSticks(target) {
|
|
91
|
+
const gp = this.gamepads[target];
|
|
92
|
+
if (gp) {
|
|
93
|
+
return gp.getSticks();
|
|
94
|
+
}
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
fullButtonName(name) {
|
|
98
|
+
return fullButtonName.get(name) ?? name;
|
|
99
|
+
}
|
|
100
|
+
checkButton(target, button, mode) {
|
|
101
|
+
if (target == "any") {
|
|
102
|
+
return Object.values(this.gamepads).some((gp) => gp[mode](button));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
return this.gamepads[target]?.[mode](button);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Check if a gamepad button is down. */
|
|
109
|
+
isDown(target, button) {
|
|
110
|
+
return this.checkButton(target, button, "isDown");
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Returns true for only one frame/update if a button is pressed.
|
|
114
|
+
* Considered an alternative to `like.gamepadpressed`.
|
|
115
|
+
*/
|
|
116
|
+
justPressed(target, button) {
|
|
117
|
+
return this.checkButton(target, button, "justPressed");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get a controller mapping.
|
|
121
|
+
* Note that modifying this mapping in place will modify the target controller.
|
|
122
|
+
* However, use `setMapping` to finalize the mapping.
|
|
123
|
+
*/
|
|
124
|
+
getMapping(index) {
|
|
125
|
+
return this.gamepads[index]?.mapping;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Set the mapping for a particular controller.
|
|
129
|
+
*
|
|
130
|
+
* Set `save = false` if you don't want this written into localstorage.
|
|
131
|
+
*/
|
|
132
|
+
setMapping(index, mapping, save = true) {
|
|
133
|
+
const gp = this.gamepads[index];
|
|
134
|
+
if (gp) {
|
|
135
|
+
gp.mapping = mapping;
|
|
136
|
+
if (save) {
|
|
137
|
+
this.saveMapping(index, mapping);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get saved mapping from db, if it exists.
|
|
143
|
+
*/
|
|
144
|
+
loadMapping(index) {
|
|
145
|
+
const gp = navigator.getGamepads()?.[index];
|
|
146
|
+
if (gp) {
|
|
147
|
+
const path = getLocalstoragePath(gp);
|
|
148
|
+
console.log(`[Gamepad] Found saved mapping for ${gp.id}.`);
|
|
149
|
+
const item = localStorage.getItem(path);
|
|
150
|
+
if (item) {
|
|
151
|
+
return JSON.parse(item);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Save a mapping to persistant storage
|
|
157
|
+
*/
|
|
158
|
+
saveMapping(index, mapping) {
|
|
159
|
+
const gp = navigator.getGamepads()?.[index];
|
|
160
|
+
if (gp) {
|
|
161
|
+
const path = getLocalstoragePath(gp);
|
|
162
|
+
console.log(`[Gamepad] Saved mapping ${path} to localStorage.`);
|
|
163
|
+
localStorage.setItem(path, JSON.stringify(mapping));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Enable automatically loading mappings.
|
|
168
|
+
*
|
|
169
|
+
* When a gamepad with a known (to this system) ID is plugged in,
|
|
170
|
+
* this will load the previously saved mapping.
|
|
171
|
+
*
|
|
172
|
+
* @param enable
|
|
173
|
+
*/
|
|
174
|
+
enableAutoLoadMapping(enable) {
|
|
175
|
+
this.autoLoadMapping = enable;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function getLocalstoragePath(gamepad) {
|
|
179
|
+
return `${gamepad.axes.length}A${gamepad.buttons.length}B${gamepad.id}`;
|
|
180
|
+
}
|
|
181
|
+
const maxButtons = 64;
|
|
182
|
+
/** Internal class: Using it externally could result
|
|
183
|
+
* in a gamepad being disconnected and still trying to maintain
|
|
184
|
+
* its state.
|
|
185
|
+
*/
|
|
186
|
+
class GamepadState {
|
|
187
|
+
constructor(index) {
|
|
188
|
+
Object.defineProperty(this, "index", {
|
|
189
|
+
enumerable: true,
|
|
190
|
+
configurable: true,
|
|
191
|
+
writable: true,
|
|
192
|
+
value: index
|
|
193
|
+
});
|
|
194
|
+
Object.defineProperty(this, "mapping", {
|
|
195
|
+
enumerable: true,
|
|
196
|
+
configurable: true,
|
|
197
|
+
writable: true,
|
|
198
|
+
value: void 0
|
|
199
|
+
});
|
|
200
|
+
Object.defineProperty(this, "downNums", {
|
|
201
|
+
enumerable: true,
|
|
202
|
+
configurable: true,
|
|
203
|
+
writable: true,
|
|
204
|
+
value: []
|
|
205
|
+
});
|
|
206
|
+
Object.defineProperty(this, "lastDownNums", {
|
|
207
|
+
enumerable: true,
|
|
208
|
+
configurable: true,
|
|
209
|
+
writable: true,
|
|
210
|
+
value: []
|
|
211
|
+
});
|
|
212
|
+
Object.defineProperty(this, "down", {
|
|
213
|
+
enumerable: true,
|
|
214
|
+
configurable: true,
|
|
215
|
+
writable: true,
|
|
216
|
+
value: {}
|
|
217
|
+
});
|
|
218
|
+
Object.defineProperty(this, "lastDown", {
|
|
219
|
+
enumerable: true,
|
|
220
|
+
configurable: true,
|
|
221
|
+
writable: true,
|
|
222
|
+
value: {}
|
|
223
|
+
});
|
|
224
|
+
const gp = navigator.getGamepads()[this.index];
|
|
225
|
+
this.mapping = defaultMapping(gp.axes.length);
|
|
226
|
+
}
|
|
227
|
+
isDown(button) {
|
|
228
|
+
return typeof (button) == "number" ? !!this.downNums[button] : !!this.down[button];
|
|
229
|
+
}
|
|
230
|
+
justPressed(button) {
|
|
231
|
+
return typeof (button) == "number" ?
|
|
232
|
+
(!!this.downNums[button] && !this.lastDownNums[button]) :
|
|
233
|
+
(!!this.down[button] && !this.lastDown[button]);
|
|
234
|
+
}
|
|
235
|
+
map(button) {
|
|
236
|
+
return (this.mapping.buttons[button] ??
|
|
237
|
+
(button < maxButtons
|
|
238
|
+
? `Button${button}`
|
|
239
|
+
: `Axis${Math.floor((button - maxButtons) / 2)}${button % 2 ? "-" : "+"}`));
|
|
240
|
+
}
|
|
241
|
+
update(dispatch) {
|
|
242
|
+
const gp = navigator.getGamepads()[this.index];
|
|
243
|
+
if (!gp)
|
|
244
|
+
return;
|
|
245
|
+
[this.downNums, this.lastDownNums] = [this.lastDownNums, this.downNums];
|
|
246
|
+
for (const k in this.downNums) {
|
|
247
|
+
delete this.downNums[Number(k)];
|
|
248
|
+
}
|
|
249
|
+
gp.buttons.forEach((btn, i) => {
|
|
250
|
+
if (btn.pressed) {
|
|
251
|
+
this.downNums[i] = true;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
gp.axes.forEach((axis, i) => {
|
|
255
|
+
const pos = Math.round(axis);
|
|
256
|
+
if (pos == 0)
|
|
257
|
+
return;
|
|
258
|
+
const index = 64 + i * 2 + (pos == -1 ? 1 : 0);
|
|
259
|
+
this.downNums[index] = true;
|
|
260
|
+
});
|
|
261
|
+
[this.down, this.lastDown] = [this.lastDown, this.down];
|
|
262
|
+
for (const k in this.down) {
|
|
263
|
+
delete this.down[k];
|
|
264
|
+
}
|
|
265
|
+
for (const i in this.downNums) {
|
|
266
|
+
const name = this.map(Number(i));
|
|
267
|
+
this.down[name] = true;
|
|
268
|
+
if (!this.lastDownNums[Number(i)]) {
|
|
269
|
+
dispatch("gamepadpressed", [this.index, name, Number(i)]);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
for (const i in this.lastDownNums) {
|
|
273
|
+
if (!this.downNums[Number(i)]) {
|
|
274
|
+
const name = this.map(Number(i));
|
|
275
|
+
dispatch("gamepadreleased", [this.index, name, Number(i)]);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
getSticks() {
|
|
280
|
+
const gp = navigator.getGamepads()[this.index];
|
|
281
|
+
return this.mapping.sticks.map((stick) => mapStick(gp, stick));
|
|
282
|
+
}
|
|
283
|
+
clear() {
|
|
284
|
+
for (const k in this.lastDown) {
|
|
285
|
+
delete this.lastDown[k];
|
|
286
|
+
}
|
|
287
|
+
for (const k in this.down) {
|
|
288
|
+
delete this.down[k];
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -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";
|