emeraldengine 2.2.1 → 3.0.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 +2359 -968
- package/dist/types/index.d.ts +72 -32
- package/dist/types/src/Animator.d.ts +50 -0
- package/dist/types/{BitmapText.d.ts → src/BitmapText.d.ts} +19 -21
- package/dist/types/src/Camera.d.ts +122 -0
- package/dist/types/src/CameraController.d.ts +107 -0
- package/dist/types/src/CanvasText.d.ts +91 -0
- package/dist/types/src/CollisionLayers.d.ts +58 -0
- package/dist/types/{Color.d.ts → src/Color.d.ts} +5 -6
- package/dist/types/src/Coroutine.d.ts +111 -0
- package/dist/types/src/DebugOverlay.d.ts +86 -0
- package/dist/types/src/Drawable.d.ts +271 -0
- package/dist/types/src/Easing.d.ts +22 -0
- package/dist/types/src/Emerald.d.ts +420 -0
- package/dist/types/src/EmeraldDB.d.ts +159 -0
- package/dist/types/{FPSCounter.d.ts → src/FPSCounter.d.ts} +1 -3
- package/dist/types/src/GLUtils.d.ts +4 -0
- package/dist/types/{Instance.d.ts → src/Instance.d.ts} +37 -15
- package/dist/types/{InstancedTexture.d.ts → src/InstancedTexture.d.ts} +60 -23
- package/dist/types/src/Interpolator.d.ts +66 -0
- package/dist/types/src/Material.d.ts +87 -0
- package/dist/types/src/MathUtils.d.ts +80 -0
- package/dist/types/src/ParticleEmitter.d.ts +131 -0
- package/dist/types/{Physics.d.ts → src/Physics.d.ts} +88 -20
- package/dist/types/src/Pool.d.ts +53 -0
- package/dist/types/src/PostEffects.d.ts +68 -0
- package/dist/types/src/PostProcessor.d.ts +124 -0
- package/dist/types/src/RenderTarget.d.ts +56 -0
- package/dist/types/src/Scene.d.ts +62 -0
- package/dist/types/src/ScreenEffects.d.ts +111 -0
- package/dist/types/src/Serializer.d.ts +86 -0
- package/dist/types/src/Shaders.d.ts +2 -0
- package/dist/types/{Shapes.d.ts → src/Shapes.d.ts} +4 -6
- package/dist/types/src/SpatialGrid.d.ts +50 -0
- package/dist/types/src/SpriteBatch.d.ts +89 -0
- package/dist/types/src/StateMachine.d.ts +59 -0
- package/dist/types/src/Storage.d.ts +89 -0
- package/dist/types/{Texture.d.ts → src/Texture.d.ts} +3 -4
- package/dist/types/src/TextureAtlas.d.ts +55 -0
- package/dist/types/src/Tilemap.d.ts +91 -0
- package/dist/types/src/Time.d.ts +53 -0
- package/dist/types/src/Timer.d.ts +54 -0
- package/dist/types/src/Transform.d.ts +94 -0
- package/dist/types/src/Tween.d.ts +81 -0
- package/dist/types/src/UI.d.ts +121 -0
- package/dist/types/src/components/Behaviour.d.ts +71 -0
- package/dist/types/{components → src/components}/BoxCollider.d.ts +14 -14
- package/dist/types/src/components/BoxColliderDebug.d.ts +15 -0
- package/dist/types/{components → src/components}/CircleCollider.d.ts +14 -12
- package/dist/types/src/components/CircleColliderDebug.d.ts +15 -0
- package/dist/types/src/components/Collider.d.ts +96 -0
- package/dist/types/src/components/GameObject.d.ts +135 -0
- package/dist/types/src/components/RigidBody.d.ts +183 -0
- package/dist/types/src/importers/Aseprite.d.ts +79 -0
- package/dist/types/src/importers/TiledMap.d.ts +62 -0
- package/dist/types/{lights → src/lights}/DirectionalLight.d.ts +7 -9
- package/dist/types/{lights → src/lights}/PointLight.d.ts +6 -9
- package/dist/types/src/managers/AssetManager.d.ts +116 -0
- package/dist/types/src/managers/AudioManager.d.ts +259 -0
- package/dist/types/{managers → src/managers}/CameraManager.d.ts +8 -8
- package/dist/types/{managers → src/managers}/EventManager.d.ts +46 -32
- package/dist/types/src/managers/GLManager.d.ts +84 -0
- package/dist/types/src/managers/GLState.d.ts +37 -0
- package/dist/types/src/managers/IDManager.d.ts +31 -0
- package/dist/types/src/managers/InputManager.d.ts +290 -0
- package/dist/types/src/managers/NetworkManager.d.ts +93 -0
- package/dist/types/src/managers/RenderStats.d.ts +34 -0
- package/dist/types/src/managers/SceneManager.d.ts +44 -0
- package/dist/types/src/managers/ShaderManager.d.ts +55 -0
- package/dist/types/src/managers/TextureManager.d.ts +102 -0
- package/dist/types/src/particlesystem/Particle.d.ts +64 -0
- package/dist/types/{particlesystem → src/particlesystem}/ParticleSettings.d.ts +32 -24
- package/dist/types/{particlesystem → src/particlesystem}/Particles.d.ts +20 -12
- package/index.js +72 -0
- package/package.json +74 -60
- package/src/Animator.js +95 -0
- package/src/BitmapText.js +6 -5
- package/src/Camera.js +183 -0
- package/src/CameraController.js +192 -0
- package/src/CanvasText.js +281 -0
- package/src/CollisionLayers.js +86 -0
- package/src/Color.js +18 -18
- package/src/Coroutine.js +259 -0
- package/src/DebugOverlay.js +246 -0
- package/src/Drawable.js +842 -582
- package/src/Easing.js +57 -0
- package/src/Emerald.js +1150 -459
- package/src/EmeraldDB.js +328 -0
- package/src/FPSCounter.js +43 -43
- package/src/GLUtils.js +60 -67
- package/src/Instance.js +41 -5
- package/src/InstancedTexture.js +251 -118
- package/src/Interpolator.js +124 -0
- package/src/Material.js +202 -0
- package/src/MathUtils.js +133 -0
- package/src/ParticleEmitter.js +284 -0
- package/src/Physics.js +186 -5
- package/src/Pool.js +85 -0
- package/src/PostEffects.js +296 -0
- package/src/PostProcessor.js +304 -0
- package/src/RenderTarget.js +134 -0
- package/src/Scene.js +115 -83
- package/src/ScreenEffects.js +266 -0
- package/src/Serializer.js +131 -0
- package/src/Shaders.js +150 -165
- package/src/Shapes.js +118 -129
- package/src/SpatialGrid.js +111 -0
- package/src/SpriteBatch.js +299 -0
- package/src/StateMachine.js +82 -0
- package/src/Storage.js +175 -47
- package/src/Texture.js +58 -67
- package/src/TextureAtlas.js +96 -0
- package/src/Tilemap.js +274 -0
- package/src/Time.js +51 -6
- package/src/Timer.js +99 -0
- package/src/Transform.js +100 -7
- package/src/Tween.js +160 -0
- package/src/UI.js +394 -0
- package/src/components/Behaviour.js +90 -0
- package/src/components/BoxCollider.js +22 -3
- package/src/components/CircleCollider.js +19 -3
- package/src/components/CircleColliderDebug.js +24 -24
- package/src/components/Collider.js +140 -34
- package/src/components/GameObject.js +130 -21
- package/src/components/RigidBody.js +123 -2
- package/src/importers/Aseprite.js +142 -0
- package/src/importers/TiledMap.js +158 -0
- package/src/lights/DirectionalLight.js +6 -15
- package/src/lights/PointLight.js +4 -4
- package/src/managers/AssetManager.js +239 -0
- package/src/managers/AudioManager.js +565 -146
- package/src/managers/EventManager.js +488 -477
- package/src/managers/GLManager.js +57 -0
- package/src/managers/GLState.js +70 -0
- package/src/managers/IDManager.js +24 -2
- package/src/managers/InputManager.js +779 -0
- package/src/managers/NetworkManager.js +178 -0
- package/src/managers/RenderStats.js +34 -0
- package/src/managers/SceneManager.js +30 -0
- package/src/managers/ShaderManager.js +0 -2
- package/src/managers/TextureManager.js +218 -0
- package/src/particlesystem/Particle.js +82 -7
- package/src/particlesystem/ParticleSettings.js +21 -3
- package/src/particlesystem/Particles.js +80 -31
- package/dist/types/Drawable.d.ts +0 -157
- package/dist/types/Emerald.d.ts +0 -73
- package/dist/types/GLUtils.d.ts +0 -6
- package/dist/types/Scene.d.ts +0 -39
- package/dist/types/Shaders.d.ts +0 -4
- package/dist/types/Storage.d.ts +0 -46
- package/dist/types/Time.d.ts +0 -22
- package/dist/types/Transform.d.ts +0 -41
- package/dist/types/components/BoxColliderDebug.d.ts +0 -19
- package/dist/types/components/CircleColliderDebug.d.ts +0 -19
- package/dist/types/components/Collider.d.ts +0 -53
- package/dist/types/components/GameObject.d.ts +0 -72
- package/dist/types/components/RigidBody.d.ts +0 -104
- package/dist/types/managers/AudioManager.d.ts +0 -60
- package/dist/types/managers/GLManager.d.ts +0 -47
- package/dist/types/managers/IDManager.d.ts +0 -21
- package/dist/types/managers/SceneManager.d.ts +0 -22
- package/dist/types/particlesystem/Particle.d.ts +0 -42
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical W3C "standard gamepad" button layout, with vendor-neutral
|
|
3
|
+
* (south/east/west/north) and vendor (Xbox a/b/x/y, PlayStation
|
|
4
|
+
* cross/circle/square/triangle) aliases all pointing at the same index. This is
|
|
5
|
+
* the table used when a pad reports `mapping === "standard"` (Xbox pads, most
|
|
6
|
+
* controllers, and anything routed through Steam Input / XInput).
|
|
7
|
+
*/
|
|
8
|
+
const STANDARD_BUTTONS = {
|
|
9
|
+
south: 0,
|
|
10
|
+
a: 0,
|
|
11
|
+
cross: 0,
|
|
12
|
+
east: 1,
|
|
13
|
+
b: 1,
|
|
14
|
+
circle: 1,
|
|
15
|
+
west: 2,
|
|
16
|
+
x: 2,
|
|
17
|
+
square: 2,
|
|
18
|
+
north: 3,
|
|
19
|
+
y: 3,
|
|
20
|
+
triangle: 3,
|
|
21
|
+
l1: 4,
|
|
22
|
+
lb: 4,
|
|
23
|
+
leftShoulder: 4,
|
|
24
|
+
r1: 5,
|
|
25
|
+
rb: 5,
|
|
26
|
+
rightShoulder: 5,
|
|
27
|
+
l2: 6,
|
|
28
|
+
lt: 6,
|
|
29
|
+
leftTrigger: 6,
|
|
30
|
+
r2: 7,
|
|
31
|
+
rt: 7,
|
|
32
|
+
rightTrigger: 7,
|
|
33
|
+
select: 8,
|
|
34
|
+
back: 8,
|
|
35
|
+
view: 8,
|
|
36
|
+
share: 8,
|
|
37
|
+
start: 9,
|
|
38
|
+
menu: 9,
|
|
39
|
+
options: 9,
|
|
40
|
+
l3: 10,
|
|
41
|
+
leftStick: 10,
|
|
42
|
+
r3: 11,
|
|
43
|
+
rightStick: 11,
|
|
44
|
+
up: 12,
|
|
45
|
+
dpadUp: 12,
|
|
46
|
+
down: 13,
|
|
47
|
+
dpadDown: 13,
|
|
48
|
+
left: 14,
|
|
49
|
+
dpadLeft: 14,
|
|
50
|
+
right: 15,
|
|
51
|
+
dpadRight: 15,
|
|
52
|
+
home: 16,
|
|
53
|
+
guide: 16,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const STANDARD_AXES = { leftX: 0, leftY: 1, rightX: 2, rightY: 3 };
|
|
57
|
+
|
|
58
|
+
const CUSTOM_MAPPINGS = [];
|
|
59
|
+
|
|
60
|
+
const BUILTIN_MAPPINGS = [
|
|
61
|
+
{
|
|
62
|
+
match: "logitech dual action",
|
|
63
|
+
buttons: {
|
|
64
|
+
west: 0,
|
|
65
|
+
x: 0,
|
|
66
|
+
square: 0,
|
|
67
|
+
south: 1,
|
|
68
|
+
a: 1,
|
|
69
|
+
cross: 1,
|
|
70
|
+
east: 2,
|
|
71
|
+
b: 2,
|
|
72
|
+
circle: 2,
|
|
73
|
+
north: 3,
|
|
74
|
+
y: 3,
|
|
75
|
+
triangle: 3,
|
|
76
|
+
l1: 4,
|
|
77
|
+
lb: 4,
|
|
78
|
+
r1: 5,
|
|
79
|
+
rb: 5,
|
|
80
|
+
l2: 6,
|
|
81
|
+
lt: 6,
|
|
82
|
+
r2: 7,
|
|
83
|
+
rt: 7,
|
|
84
|
+
select: 8,
|
|
85
|
+
back: 8,
|
|
86
|
+
start: 9,
|
|
87
|
+
l3: 10,
|
|
88
|
+
r3: 11,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
match: "twin usb",
|
|
93
|
+
buttons: {
|
|
94
|
+
north: 0,
|
|
95
|
+
y: 0,
|
|
96
|
+
triangle: 0,
|
|
97
|
+
east: 1,
|
|
98
|
+
b: 1,
|
|
99
|
+
circle: 1,
|
|
100
|
+
south: 2,
|
|
101
|
+
a: 2,
|
|
102
|
+
cross: 2,
|
|
103
|
+
west: 3,
|
|
104
|
+
x: 3,
|
|
105
|
+
square: 3,
|
|
106
|
+
l2: 4,
|
|
107
|
+
lt: 4,
|
|
108
|
+
r2: 5,
|
|
109
|
+
rt: 5,
|
|
110
|
+
l1: 6,
|
|
111
|
+
lb: 6,
|
|
112
|
+
r1: 7,
|
|
113
|
+
rb: 7,
|
|
114
|
+
select: 8,
|
|
115
|
+
back: 8,
|
|
116
|
+
start: 9,
|
|
117
|
+
l3: 10,
|
|
118
|
+
r3: 11,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
match: "8bitdo",
|
|
123
|
+
buttons: {
|
|
124
|
+
east: 0,
|
|
125
|
+
b: 0,
|
|
126
|
+
circle: 0,
|
|
127
|
+
south: 1,
|
|
128
|
+
a: 1,
|
|
129
|
+
cross: 1,
|
|
130
|
+
north: 3,
|
|
131
|
+
y: 3,
|
|
132
|
+
triangle: 3,
|
|
133
|
+
west: 4,
|
|
134
|
+
x: 4,
|
|
135
|
+
square: 4,
|
|
136
|
+
l1: 6,
|
|
137
|
+
lb: 6,
|
|
138
|
+
r1: 7,
|
|
139
|
+
rb: 7,
|
|
140
|
+
l2: 8,
|
|
141
|
+
lt: 8,
|
|
142
|
+
r2: 9,
|
|
143
|
+
rt: 9,
|
|
144
|
+
select: 10,
|
|
145
|
+
back: 10,
|
|
146
|
+
start: 11,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
const STICK_DIRECTIONS = {
|
|
152
|
+
leftStickLeft: ["leftX", -1],
|
|
153
|
+
leftStickRight: ["leftX", 1],
|
|
154
|
+
leftStickUp: ["leftY", -1],
|
|
155
|
+
leftStickDown: ["leftY", 1],
|
|
156
|
+
rightStickLeft: ["rightX", -1],
|
|
157
|
+
rightStickRight: ["rightX", 1],
|
|
158
|
+
rightStickUp: ["rightY", -1],
|
|
159
|
+
rightStickDown: ["rightY", 1],
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
for (const b of BUILTIN_MAPPINGS) {
|
|
163
|
+
CUSTOM_MAPPINGS.push({
|
|
164
|
+
test: (id) => (id || "").toLowerCase().includes(b.match),
|
|
165
|
+
buttons: { ...STANDARD_BUTTONS, ...b.buttons },
|
|
166
|
+
axes: { ...STANDARD_AXES, ...(b.axes || {}) },
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @class InputManager
|
|
172
|
+
* @description Unified, pollable input: keyboard, mouse, touch, and full
|
|
173
|
+
* gamepad support (analog sticks/triggers, semantic button names, rumble,
|
|
174
|
+
* connect events, and per-controller mapping), with rebindable named actions.
|
|
175
|
+
* Call `update()` once per frame so `justPressed`/`justReleased` edge queries
|
|
176
|
+
* work for every device — including the gamepad.
|
|
177
|
+
*
|
|
178
|
+
* Gamepad tokens (usable anywhere a key token is, including in mapAction and
|
|
179
|
+
* justPressed) — `<i>` is the pad index:
|
|
180
|
+
* "pad:<i>:south" / "pad:<i>:a" - face buttons (also east/b, west/x, north/y)
|
|
181
|
+
* "pad:<i>:l1" / "pad:<i>:r2" ... - shoulders / triggers
|
|
182
|
+
* "pad:<i>:start" / "pad:<i>:select" - center buttons
|
|
183
|
+
* "pad:<i>:dpadLeft" ... - d-pad (works via buttons or a hat axis)
|
|
184
|
+
* "pad:<i>:<n>" - raw button index (mapping-independent)
|
|
185
|
+
* "pad:<i>:axis<n>+" / "axis<n>-" - analog axis past the deadzone
|
|
186
|
+
* "pad:<i>:leftStickUp" / "Down" / "Left" / "Right" - stick as a d-pad
|
|
187
|
+
* "pad:<i>:rightStickUp" ... - (threshold: stickPressThreshold)
|
|
188
|
+
* "gamepad:<n>" - legacy: raw button <n> on pad 0
|
|
189
|
+
* Names resolve through the active mapping, so "pad:0:south" is the bottom face
|
|
190
|
+
* button regardless of whether the pad reports Xbox or PlayStation ordering.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* const input = new InputManager();
|
|
194
|
+
* input.mapAction("jump", ["Space", " ", "pad:0:south"]);
|
|
195
|
+
* input.mapAction("left", ["a", "ArrowLeft", "pad:0:dpadLeft"]);
|
|
196
|
+
* input.onGamepadConnected((info) => console.log("pad:", info.id, info.mapping));
|
|
197
|
+
* // in the loop:
|
|
198
|
+
* if (input.justPressed("jump")) { player.jump(); input.rumble(0, { duration: 120 }); }
|
|
199
|
+
* const { x } = input.getGamepadStick("left"); // analog, deadzoned
|
|
200
|
+
* input.update();
|
|
201
|
+
*/
|
|
202
|
+
class InputManager {
|
|
203
|
+
/**
|
|
204
|
+
* @method registerGamepadMapping
|
|
205
|
+
* @description Registers a custom button/axis mapping for controllers whose
|
|
206
|
+
* `mapping` is not "standard" (so their raw button indices differ). Match by
|
|
207
|
+
* a substring of `gamepad.id` (case-insensitive), a RegExp, or a predicate.
|
|
208
|
+
* @param {string|RegExp|Function} match - id matcher
|
|
209
|
+
* @param {Object} mapping - { buttons:{name:index}, axes:{name:index} };
|
|
210
|
+
* merged over the standard table, so you only specify what differs.
|
|
211
|
+
*/
|
|
212
|
+
static registerGamepadMapping(match, mapping = {}) {
|
|
213
|
+
const test =
|
|
214
|
+
typeof match === "function"
|
|
215
|
+
? match
|
|
216
|
+
: match instanceof RegExp
|
|
217
|
+
? (id) => match.test(id)
|
|
218
|
+
: (id) =>
|
|
219
|
+
(id || "").toLowerCase().includes(String(match).toLowerCase());
|
|
220
|
+
CUSTOM_MAPPINGS.unshift({
|
|
221
|
+
test,
|
|
222
|
+
buttons: { ...STANDARD_BUTTONS, ...(mapping.buttons || {}) },
|
|
223
|
+
axes: { ...STANDARD_AXES, ...(mapping.axes || {}) },
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
constructor(options = {}) {
|
|
228
|
+
this.target =
|
|
229
|
+
options.target || (typeof window !== "undefined" ? window : null);
|
|
230
|
+
this.actions = new Map();
|
|
231
|
+
this.down = new Set();
|
|
232
|
+
this.prevDown = new Set();
|
|
233
|
+
this.mouse = { x: 0, y: 0, buttons: new Set() };
|
|
234
|
+
this.touches = [];
|
|
235
|
+
this.gamepads = [];
|
|
236
|
+
|
|
237
|
+
this.gamepadDeadzone = options.gamepadDeadzone ?? 0.3;
|
|
238
|
+
this.gamepadCurve = options.gamepadCurve ?? 1;
|
|
239
|
+
this.stickPressThreshold = options.stickPressThreshold ?? 0.5;
|
|
240
|
+
/** @private */
|
|
241
|
+
this._gamepadTokens = new Set();
|
|
242
|
+
/** @private */
|
|
243
|
+
this._connectHandlers = [];
|
|
244
|
+
/** @private */
|
|
245
|
+
this._disconnectHandlers = [];
|
|
246
|
+
/** @private */
|
|
247
|
+
this._connected = new Map();
|
|
248
|
+
|
|
249
|
+
/** @private */
|
|
250
|
+
this._onKeyDown = (e) => this.down.add(this._normKey(e.key));
|
|
251
|
+
/** @private */
|
|
252
|
+
this._onKeyUp = (e) => this.down.delete(this._normKey(e.key));
|
|
253
|
+
/** @private */
|
|
254
|
+
this._onMouseDown = (e) => {
|
|
255
|
+
this.down.add(`mouse:${e.button}`);
|
|
256
|
+
this.mouse.buttons.add(e.button);
|
|
257
|
+
};
|
|
258
|
+
/** @private */
|
|
259
|
+
this._onMouseUp = (e) => {
|
|
260
|
+
this.down.delete(`mouse:${e.button}`);
|
|
261
|
+
this.mouse.buttons.delete(e.button);
|
|
262
|
+
};
|
|
263
|
+
/** @private */
|
|
264
|
+
this._onMouseMove = (e) => {
|
|
265
|
+
this.mouse.x = e.clientX;
|
|
266
|
+
this.mouse.y = e.clientY;
|
|
267
|
+
};
|
|
268
|
+
/** @private */
|
|
269
|
+
this._onTouch = (e) => {
|
|
270
|
+
this.touches = Array.from(e.touches || []).map((t) => ({
|
|
271
|
+
x: t.clientX,
|
|
272
|
+
y: t.clientY,
|
|
273
|
+
id: t.identifier,
|
|
274
|
+
}));
|
|
275
|
+
};
|
|
276
|
+
/** @private */
|
|
277
|
+
this._onGamepadConnected = (e) => {
|
|
278
|
+
const gp = e.gamepad;
|
|
279
|
+
if (!gp) return;
|
|
280
|
+
const info = this._infoFromPad(gp);
|
|
281
|
+
this._connected.set(gp.index, info);
|
|
282
|
+
for (const h of this._connectHandlers) h(info);
|
|
283
|
+
};
|
|
284
|
+
/** @private */
|
|
285
|
+
this._onGamepadDisconnected = (e) => {
|
|
286
|
+
const gp = e.gamepad;
|
|
287
|
+
if (!gp) return;
|
|
288
|
+
const info = this._connected.get(gp.index) || this._infoFromPad(gp);
|
|
289
|
+
this._connected.delete(gp.index);
|
|
290
|
+
for (const h of this._disconnectHandlers) h(info);
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
if (this.target) {
|
|
294
|
+
this.target.addEventListener("keydown", this._onKeyDown);
|
|
295
|
+
this.target.addEventListener("keyup", this._onKeyUp);
|
|
296
|
+
this.target.addEventListener("mousedown", this._onMouseDown);
|
|
297
|
+
this.target.addEventListener("mouseup", this._onMouseUp);
|
|
298
|
+
this.target.addEventListener("mousemove", this._onMouseMove);
|
|
299
|
+
this.target.addEventListener("touchstart", this._onTouch);
|
|
300
|
+
this.target.addEventListener("touchmove", this._onTouch);
|
|
301
|
+
this.target.addEventListener("touchend", this._onTouch);
|
|
302
|
+
this.target.addEventListener(
|
|
303
|
+
"gamepadconnected",
|
|
304
|
+
this._onGamepadConnected
|
|
305
|
+
);
|
|
306
|
+
this.target.addEventListener(
|
|
307
|
+
"gamepaddisconnected",
|
|
308
|
+
this._onGamepadDisconnected
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** @private */
|
|
314
|
+
_normKey(key) {
|
|
315
|
+
return key && key.length <= 1 ? key.toLowerCase() : key;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* @method mapAction
|
|
320
|
+
* @description Binds an action name to one or more input tokens (keys,
|
|
321
|
+
* "mouse:0", or any gamepad token above).
|
|
322
|
+
* @param {string} name - Action name
|
|
323
|
+
* @param {string[]} tokens - Input tokens
|
|
324
|
+
* @returns {InputManager} - this
|
|
325
|
+
*/
|
|
326
|
+
mapAction(name, tokens) {
|
|
327
|
+
this.actions.set(
|
|
328
|
+
name,
|
|
329
|
+
tokens.map((t) => this._normKey(t))
|
|
330
|
+
);
|
|
331
|
+
return this;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** @private */
|
|
335
|
+
_tokensFor(actionOrToken) {
|
|
336
|
+
return this.actions.get(actionOrToken) || [this._normKey(actionOrToken)];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** @private */
|
|
340
|
+
_tokenDown(token) {
|
|
341
|
+
if (this.down.has(token)) return true;
|
|
342
|
+
if (this._gamepadTokens.has(token)) return true;
|
|
343
|
+
if (token.startsWith("gamepad:")) {
|
|
344
|
+
const idx = parseInt(token.slice(8), 10);
|
|
345
|
+
const gp = this.gamepads[0];
|
|
346
|
+
return !!(gp && gp.buttons[idx] && gp.buttons[idx].pressed);
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @method isDown
|
|
353
|
+
* @description Whether the action (or raw token) is currently held.
|
|
354
|
+
*/
|
|
355
|
+
isDown(actionOrToken) {
|
|
356
|
+
return this._tokensFor(actionOrToken).some((t) => this._tokenDown(t));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* @method justPressed
|
|
361
|
+
* @description Whether the action became pressed this frame.
|
|
362
|
+
*/
|
|
363
|
+
justPressed(actionOrToken) {
|
|
364
|
+
return this._tokensFor(actionOrToken).some(
|
|
365
|
+
(t) => this.down.has(t) && !this.prevDown.has(t)
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* @method justReleased
|
|
371
|
+
* @description Whether the action was released this frame.
|
|
372
|
+
*/
|
|
373
|
+
justReleased(actionOrToken) {
|
|
374
|
+
return this._tokensFor(actionOrToken).some(
|
|
375
|
+
(t) => !this.down.has(t) && this.prevDown.has(t)
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* @method getAxis
|
|
381
|
+
* @description Returns -1/0/+1 based on two opposing actions.
|
|
382
|
+
*/
|
|
383
|
+
getAxis(negative, positive) {
|
|
384
|
+
return (this.isDown(positive) ? 1 : 0) - (this.isDown(negative) ? 1 : 0);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* @method getGamepad
|
|
389
|
+
* @description Returns the polled gamepad at an index (or null).
|
|
390
|
+
*/
|
|
391
|
+
getGamepad(index = 0) {
|
|
392
|
+
return this.gamepads[index] || null;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* @method setGamepadDeadzone
|
|
397
|
+
* @description Sets the analog deadzone (0..1) for axes and trigger buttons.
|
|
398
|
+
* @returns {InputManager} - this
|
|
399
|
+
*/
|
|
400
|
+
setGamepadDeadzone(dz) {
|
|
401
|
+
this.gamepadDeadzone = dz;
|
|
402
|
+
return this;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* @method setGamepadCurve
|
|
407
|
+
* @description Sets the response-curve exponent applied to deadzoned analog
|
|
408
|
+
* values: 1 = linear (default), >1 = softer near center for fine aiming.
|
|
409
|
+
* @returns {InputManager} - this
|
|
410
|
+
*/
|
|
411
|
+
setGamepadCurve(exp) {
|
|
412
|
+
this.gamepadCurve = exp;
|
|
413
|
+
return this;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* @method _rescaleAxis
|
|
418
|
+
* @description Deadzone + rescale + curve for a single axis value. Unlike a
|
|
419
|
+
* hard cutoff, the output ramps smoothly from 0 at the deadzone edge to ±1
|
|
420
|
+
* at full deflection (no jump, and the full range stays reachable).
|
|
421
|
+
* @private
|
|
422
|
+
*/
|
|
423
|
+
_rescaleAxis(v, dz = this.gamepadDeadzone, curve = this.gamepadCurve) {
|
|
424
|
+
const a = Math.abs(v);
|
|
425
|
+
if (a < dz) return 0;
|
|
426
|
+
let t = Math.min(1, (a - dz) / (1 - dz || 1));
|
|
427
|
+
if (curve !== 1) t = Math.pow(t, curve);
|
|
428
|
+
return v < 0 ? -t : t;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @method _buttonMapFor
|
|
433
|
+
* @description Resolves the button-name → index table for a gamepad: a
|
|
434
|
+
* registered custom mapping (matched by id) wins; otherwise the standard
|
|
435
|
+
* table is used (also as the best-effort fallback for unknown non-standard
|
|
436
|
+
* pads).
|
|
437
|
+
* @private
|
|
438
|
+
*/
|
|
439
|
+
_buttonMapFor(gp) {
|
|
440
|
+
if (gp.mapping !== "standard") {
|
|
441
|
+
const id = gp.id || "";
|
|
442
|
+
for (const m of CUSTOM_MAPPINGS) {
|
|
443
|
+
if (m.test(id)) return m.buttons;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return STANDARD_BUTTONS;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* @method getGamepadAxis
|
|
451
|
+
* @description Returns a deadzoned analog axis value (-1..1). The value is
|
|
452
|
+
* RESCALED past the deadzone (0 at the edge, ±1 at full deflection) and runs
|
|
453
|
+
* through the response curve, so there is no jump at the threshold. Standard
|
|
454
|
+
* mapping: axis 0/1 = left stick X/Y, 2/3 = right stick X/Y.
|
|
455
|
+
* @param {number} axisIndex
|
|
456
|
+
* @param {number} [padIndex=0]
|
|
457
|
+
* @returns {number}
|
|
458
|
+
*/
|
|
459
|
+
getGamepadAxis(axisIndex, padIndex = 0) {
|
|
460
|
+
const gp = this.gamepads[padIndex];
|
|
461
|
+
if (!gp || !gp.axes) return 0;
|
|
462
|
+
return this._rescaleAxis(gp.axes[axisIndex] || 0);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* @method getGamepadStick
|
|
467
|
+
* @description Returns the left or right stick with a RADIAL deadzone: the
|
|
468
|
+
* deadzone applies to the stick's distance from center (not per axis), so
|
|
469
|
+
* diagonals aren't clipped square and direction is preserved exactly. The
|
|
470
|
+
* magnitude is rescaled to use the full 0..1 range and shaped by the
|
|
471
|
+
* response curve.
|
|
472
|
+
* @param {"left"|"right"} [side="left"]
|
|
473
|
+
* @param {number} [padIndex=0]
|
|
474
|
+
* @param {Object} [opts] - { deadzone, curve, invertY } overrides. invertY
|
|
475
|
+
* flips the browser's y-down convention so up = +1.
|
|
476
|
+
* @returns {{x:number, y:number, magnitude:number, angle:number}}
|
|
477
|
+
*/
|
|
478
|
+
getGamepadStick(side = "left", padIndex = 0, opts = {}) {
|
|
479
|
+
const gp = this.gamepads[padIndex];
|
|
480
|
+
if (!gp || !gp.axes) return { x: 0, y: 0, magnitude: 0, angle: 0 };
|
|
481
|
+
const map = this._axisMapFor(gp);
|
|
482
|
+
const ax = side === "right" ? map.rightX : map.leftX;
|
|
483
|
+
const ay = side === "right" ? map.rightY : map.leftY;
|
|
484
|
+
const rx = gp.axes[ax] || 0;
|
|
485
|
+
const ry = gp.axes[ay] || 0;
|
|
486
|
+
const dz = opts.deadzone ?? this.gamepadDeadzone;
|
|
487
|
+
const curve = opts.curve ?? this.gamepadCurve;
|
|
488
|
+
|
|
489
|
+
const mag = Math.hypot(rx, ry);
|
|
490
|
+
if (mag < dz) return { x: 0, y: 0, magnitude: 0, angle: 0 };
|
|
491
|
+
let t = Math.min(1, (mag - dz) / (1 - dz || 1));
|
|
492
|
+
if (curve !== 1) t = Math.pow(t, curve);
|
|
493
|
+
const s = t / mag;
|
|
494
|
+
const x = rx * s;
|
|
495
|
+
const y = (opts.invertY ? -ry : ry) * s;
|
|
496
|
+
return { x, y, magnitude: t, angle: Math.atan2(y, x) };
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* @method _axisMapFor
|
|
501
|
+
* @description Axis-name table for a pad (custom/built-in mapping wins for
|
|
502
|
+
* non-standard pads, else the standard table).
|
|
503
|
+
* @private
|
|
504
|
+
*/
|
|
505
|
+
_axisMapFor(gp) {
|
|
506
|
+
if (gp.mapping !== "standard") {
|
|
507
|
+
const id = gp.id || "";
|
|
508
|
+
for (const m of CUSTOM_MAPPINGS) {
|
|
509
|
+
if (m.test(id)) return m.axes;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return STANDARD_AXES;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* @method getGamepadButton
|
|
517
|
+
* @description Looks up a button by semantic name (e.g. "south", "a", "r1",
|
|
518
|
+
* "start", "dpadLeft") or raw index through the pad's active mapping.
|
|
519
|
+
* @param {string|number} name
|
|
520
|
+
* @param {number} [padIndex=0]
|
|
521
|
+
* @returns {{pressed:boolean, value:number, index:number}}
|
|
522
|
+
*/
|
|
523
|
+
getGamepadButton(name, padIndex = 0) {
|
|
524
|
+
const gp = this.gamepads[padIndex];
|
|
525
|
+
if (!gp || !gp.buttons) return { pressed: false, value: 0, index: -1 };
|
|
526
|
+
const map = this._buttonMapFor(gp);
|
|
527
|
+
const idx = typeof name === "number" ? name : map[name];
|
|
528
|
+
if (idx == null) return { pressed: false, value: 0, index: -1 };
|
|
529
|
+
const btn = gp.buttons[idx];
|
|
530
|
+
const value = btn ? btn.value || 0 : 0;
|
|
531
|
+
return {
|
|
532
|
+
pressed: !!(btn && (btn.pressed || value > this.gamepadDeadzone)),
|
|
533
|
+
value,
|
|
534
|
+
index: idx,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* @method getGamepadTrigger
|
|
540
|
+
* @description Returns the analog value (0..1) of a trigger.
|
|
541
|
+
* @param {"left"|"right"} [side="left"]
|
|
542
|
+
* @param {number} [padIndex=0]
|
|
543
|
+
* @returns {number}
|
|
544
|
+
*/
|
|
545
|
+
getGamepadTrigger(side = "left", padIndex = 0) {
|
|
546
|
+
return this.getGamepadButton(side === "right" ? "r2" : "l2", padIndex)
|
|
547
|
+
.value;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* @method rumble
|
|
552
|
+
* @description Plays a vibration effect on a pad (where supported).
|
|
553
|
+
* @param {number} [padIndex=0]
|
|
554
|
+
* @param {Object} [opts] - { duration=200, strong=1, weak=1 }
|
|
555
|
+
* @returns {Promise|undefined}
|
|
556
|
+
*/
|
|
557
|
+
rumble(padIndex = 0, opts = {}) {
|
|
558
|
+
const gp = this.gamepads[padIndex];
|
|
559
|
+
const act =
|
|
560
|
+
gp &&
|
|
561
|
+
(gp.vibrationActuator || (gp.hapticActuators && gp.hapticActuators[0]));
|
|
562
|
+
if (!act) return;
|
|
563
|
+
const { duration = 200, strong = 1, weak = 1 } = opts;
|
|
564
|
+
if (typeof act.playEffect === "function") {
|
|
565
|
+
return act.playEffect("dual-rumble", {
|
|
566
|
+
duration,
|
|
567
|
+
strongMagnitude: strong,
|
|
568
|
+
weakMagnitude: weak,
|
|
569
|
+
startDelay: 0,
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
if (typeof act.pulse === "function")
|
|
573
|
+
return act.pulse(Math.max(strong, weak), duration);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* @method onGamepadConnected
|
|
578
|
+
* @description Registers a callback fired when a pad connects (also called for
|
|
579
|
+
* pads already connected at registration time). Receives an info object.
|
|
580
|
+
* @param {Function} cb
|
|
581
|
+
* @returns {InputManager} - this
|
|
582
|
+
*/
|
|
583
|
+
onGamepadConnected(cb) {
|
|
584
|
+
this._connectHandlers.push(cb);
|
|
585
|
+
for (const info of this.getConnectedGamepads()) cb(info);
|
|
586
|
+
return this;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* @method onGamepadDisconnected
|
|
591
|
+
* @description Registers a callback fired when a pad disconnects.
|
|
592
|
+
* @returns {InputManager} - this
|
|
593
|
+
*/
|
|
594
|
+
onGamepadDisconnected(cb) {
|
|
595
|
+
this._disconnectHandlers.push(cb);
|
|
596
|
+
return this;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/** @private */
|
|
600
|
+
_infoFromPad(gp) {
|
|
601
|
+
return {
|
|
602
|
+
index: gp.index,
|
|
603
|
+
id: gp.id || "",
|
|
604
|
+
mapping: gp.mapping || "",
|
|
605
|
+
buttonCount: (gp.buttons || []).length,
|
|
606
|
+
axesCount: (gp.axes || []).length,
|
|
607
|
+
standard: gp.mapping === "standard",
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* @method getGamepadInfo
|
|
613
|
+
* @description Diagnostic info for a connected pad (id, mapping, counts).
|
|
614
|
+
* @param {number} [padIndex=0]
|
|
615
|
+
* @returns {Object|null}
|
|
616
|
+
*/
|
|
617
|
+
getGamepadInfo(padIndex = 0) {
|
|
618
|
+
const gp = this.gamepads[padIndex];
|
|
619
|
+
return gp ? this._infoFromPad(gp) : null;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* @method getConnectedGamepads
|
|
624
|
+
* @description Info for every currently polled pad.
|
|
625
|
+
* @returns {Object[]}
|
|
626
|
+
*/
|
|
627
|
+
getConnectedGamepads() {
|
|
628
|
+
return this.gamepads.filter(Boolean).map((gp) => this._infoFromPad(gp));
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* @method isGamepadConnected
|
|
633
|
+
* @returns {boolean}
|
|
634
|
+
*/
|
|
635
|
+
isGamepadConnected(padIndex = 0) {
|
|
636
|
+
return !!this.gamepads[padIndex];
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* @method getPressedButtons
|
|
641
|
+
* @description Diagnostic: raw indices of all currently pressed buttons on a
|
|
642
|
+
* pad. Handy for discovering an unknown controller's layout.
|
|
643
|
+
* @param {number} [padIndex=0]
|
|
644
|
+
* @returns {number[]}
|
|
645
|
+
*/
|
|
646
|
+
getPressedButtons(padIndex = 0) {
|
|
647
|
+
const gp = this.gamepads[padIndex];
|
|
648
|
+
if (!gp || !gp.buttons) return [];
|
|
649
|
+
const out = [];
|
|
650
|
+
for (let b = 0; b < gp.buttons.length; b++) {
|
|
651
|
+
const btn = gp.buttons[b];
|
|
652
|
+
if (btn && (btn.pressed || (btn.value || 0) > this.gamepadDeadzone))
|
|
653
|
+
out.push(b);
|
|
654
|
+
}
|
|
655
|
+
return out;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* @method _decodeHat
|
|
660
|
+
* @description Some non-standard pads report the d-pad as an 8-way "hat" on a
|
|
661
|
+
* single axis instead of buttons 12-15. When a pad is non-standard and exposes
|
|
662
|
+
* an extra (odd) trailing axis, decode it into d-pad direction tokens.
|
|
663
|
+
* @private
|
|
664
|
+
*/
|
|
665
|
+
_decodeHat(gp, i, set) {
|
|
666
|
+
if (gp.mapping === "standard") return;
|
|
667
|
+
const axes = gp.axes || [];
|
|
668
|
+
if (axes.length < 5 || axes.length % 2 === 0) return;
|
|
669
|
+
const v = axes[axes.length - 1];
|
|
670
|
+
if (v == null || v > 1.2 || v < -1.2) return;
|
|
671
|
+
const dirs = [
|
|
672
|
+
["dpadUp"],
|
|
673
|
+
["dpadUp", "dpadRight"],
|
|
674
|
+
["dpadRight"],
|
|
675
|
+
["dpadDown", "dpadRight"],
|
|
676
|
+
["dpadDown"],
|
|
677
|
+
["dpadDown", "dpadLeft"],
|
|
678
|
+
["dpadLeft"],
|
|
679
|
+
["dpadUp", "dpadLeft"],
|
|
680
|
+
];
|
|
681
|
+
const bucket = Math.round((v + 1) * 3.5);
|
|
682
|
+
const names = dirs[Math.max(0, Math.min(7, bucket))];
|
|
683
|
+
for (const n of names) set.add(`pad:${i}:${n}`);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* @method _collectGamepadTokens
|
|
688
|
+
* @description Rebuilds the active gamepad token set (raw indices, semantic
|
|
689
|
+
* names via the pad's mapping, d-pad, and axis directions) for every connected
|
|
690
|
+
* pad, so gamepad input flows through the same down/justPressed machinery as
|
|
691
|
+
* the keyboard.
|
|
692
|
+
* @private
|
|
693
|
+
*/
|
|
694
|
+
_collectGamepadTokens() {
|
|
695
|
+
const set = this._gamepadTokens;
|
|
696
|
+
set.clear();
|
|
697
|
+
const dz = this.gamepadDeadzone;
|
|
698
|
+
|
|
699
|
+
for (let i = 0; i < this.gamepads.length; i++) {
|
|
700
|
+
const gp = this.gamepads[i];
|
|
701
|
+
if (!gp) continue;
|
|
702
|
+
const map = this._buttonMapFor(gp);
|
|
703
|
+
const buttons = gp.buttons || [];
|
|
704
|
+
|
|
705
|
+
for (let b = 0; b < buttons.length; b++) {
|
|
706
|
+
const btn = buttons[b];
|
|
707
|
+
if (btn && (btn.pressed || (btn.value || 0) > dz)) {
|
|
708
|
+
set.add(`pad:${i}:${b}`);
|
|
709
|
+
if (i === 0) set.add(`gamepad:${b}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
for (const name in map) {
|
|
713
|
+
const btn = buttons[map[name]];
|
|
714
|
+
if (btn && (btn.pressed || (btn.value || 0) > dz))
|
|
715
|
+
set.add(`pad:${i}:${name}`);
|
|
716
|
+
}
|
|
717
|
+
this._decodeHat(gp, i, set);
|
|
718
|
+
|
|
719
|
+
const axes = gp.axes || [];
|
|
720
|
+
for (let a = 0; a < axes.length; a++) {
|
|
721
|
+
const v = axes[a] || 0;
|
|
722
|
+
if (v <= -dz) set.add(`pad:${i}:axis${a}-`);
|
|
723
|
+
else if (v >= dz) set.add(`pad:${i}:axis${a}+`);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const axisMap = this._axisMapFor(gp);
|
|
727
|
+
const press = this.stickPressThreshold;
|
|
728
|
+
for (const name in STICK_DIRECTIONS) {
|
|
729
|
+
const [axisName, sign] = STICK_DIRECTIONS[name];
|
|
730
|
+
const idx = axisMap[axisName];
|
|
731
|
+
if (idx == null || axes[idx] == null) continue;
|
|
732
|
+
const v = this._rescaleAxis(axes[idx]);
|
|
733
|
+
if (sign * v >= press) set.add(`pad:${i}:${name}`);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return set;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* @method update
|
|
741
|
+
* @description Polls gamepads and advances edge-detection state. Call once
|
|
742
|
+
* per frame after reading input.
|
|
743
|
+
*/
|
|
744
|
+
update() {
|
|
745
|
+
if (typeof navigator !== "undefined" && navigator.getGamepads) {
|
|
746
|
+
this.gamepads = Array.from(navigator.getGamepads()).filter(Boolean);
|
|
747
|
+
}
|
|
748
|
+
this.prevDown = new Set(this.down);
|
|
749
|
+
for (const t of this._gamepadTokens) this.down.delete(t);
|
|
750
|
+
this._collectGamepadTokens();
|
|
751
|
+
for (const t of this._gamepadTokens) this.down.add(t);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* @method destroy
|
|
756
|
+
* @description Removes all event listeners.
|
|
757
|
+
*/
|
|
758
|
+
destroy() {
|
|
759
|
+
if (!this.target) return;
|
|
760
|
+
this.target.removeEventListener("keydown", this._onKeyDown);
|
|
761
|
+
this.target.removeEventListener("keyup", this._onKeyUp);
|
|
762
|
+
this.target.removeEventListener("mousedown", this._onMouseDown);
|
|
763
|
+
this.target.removeEventListener("mouseup", this._onMouseUp);
|
|
764
|
+
this.target.removeEventListener("mousemove", this._onMouseMove);
|
|
765
|
+
this.target.removeEventListener("touchstart", this._onTouch);
|
|
766
|
+
this.target.removeEventListener("touchmove", this._onTouch);
|
|
767
|
+
this.target.removeEventListener("touchend", this._onTouch);
|
|
768
|
+
this.target.removeEventListener(
|
|
769
|
+
"gamepadconnected",
|
|
770
|
+
this._onGamepadConnected
|
|
771
|
+
);
|
|
772
|
+
this.target.removeEventListener(
|
|
773
|
+
"gamepaddisconnected",
|
|
774
|
+
this._onGamepadDisconnected
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
export default InputManager;
|