canvasengine 2.0.0-beta.38 → 2.0.0-beta.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DebugRenderer-DxJSMb9B.js → DebugRenderer-Rrw9FlTd.js} +2 -2
- package/dist/{DebugRenderer-DxJSMb9B.js.map → DebugRenderer-Rrw9FlTd.js.map} +1 -1
- package/dist/components/Button.d.ts +50 -3
- package/dist/components/Button.d.ts.map +1 -1
- package/dist/components/Joystick.d.ts +36 -0
- package/dist/components/Joystick.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/directives/Controls.d.ts +16 -7
- package/dist/directives/Controls.d.ts.map +1 -1
- package/dist/directives/GamepadControls.d.ts +3 -1
- package/dist/directives/GamepadControls.d.ts.map +1 -1
- package/dist/directives/JoystickControls.d.ts +172 -0
- package/dist/directives/JoystickControls.d.ts.map +1 -0
- package/dist/directives/index.d.ts +1 -0
- package/dist/directives/index.d.ts.map +1 -1
- package/dist/engine/reactive.d.ts.map +1 -1
- package/dist/{index-BgNWflRE.js → index-BQ99FClW.js} +5543 -5141
- package/dist/index-BQ99FClW.js.map +1 -0
- package/dist/index.global.js +7 -7
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +59 -57
- package/package.json +1 -1
- package/src/components/Button.ts +168 -41
- package/src/components/Joystick.ts +361 -0
- package/src/components/index.ts +2 -1
- package/src/directives/Controls.ts +42 -8
- package/src/directives/GamepadControls.ts +40 -11
- package/src/directives/JoystickControls.ts +396 -0
- package/src/directives/index.ts +1 -0
- package/src/engine/reactive.ts +41 -14
- package/dist/index-BgNWflRE.js.map +0 -1
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Joystick
|
|
3
|
+
*
|
|
4
|
+
* Inspired by https://github.com/endel/pixi-virtual-joystick
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as PIXI from "pixi.js";
|
|
8
|
+
import { Container, Graphics, Sprite, h, signal, isSignal } from "../";
|
|
9
|
+
|
|
10
|
+
export interface JoystickChangeEvent {
|
|
11
|
+
angle: number;
|
|
12
|
+
direction: Direction;
|
|
13
|
+
power: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export enum Direction {
|
|
17
|
+
LEFT = "left",
|
|
18
|
+
TOP = "top",
|
|
19
|
+
BOTTOM = "bottom",
|
|
20
|
+
RIGHT = "right",
|
|
21
|
+
TOP_LEFT = "top_left",
|
|
22
|
+
TOP_RIGHT = "top_right",
|
|
23
|
+
BOTTOM_LEFT = "bottom_left",
|
|
24
|
+
BOTTOM_RIGHT = "bottom_right",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface JoystickSettings {
|
|
28
|
+
outer?: string;
|
|
29
|
+
inner?: string;
|
|
30
|
+
outerScale?: { x: number; y: number };
|
|
31
|
+
innerScale?: { x: number; y: number };
|
|
32
|
+
innerColor?: string;
|
|
33
|
+
outerColor?: string;
|
|
34
|
+
onChange?: (data: JoystickChangeEvent) => void;
|
|
35
|
+
onStart?: () => void;
|
|
36
|
+
onEnd?: () => void;
|
|
37
|
+
/** Controls instance to automatically apply joystick events to (e.g., JoystickControls or ControlsDirective) */
|
|
38
|
+
controls?: any;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function Joystick(opts: JoystickSettings = {}) {
|
|
42
|
+
const settings = Object.assign(
|
|
43
|
+
{
|
|
44
|
+
outerScale: { x: 1, y: 1 },
|
|
45
|
+
innerScale: { x: 1, y: 1 },
|
|
46
|
+
innerColor: "black",
|
|
47
|
+
outerColor: "black",
|
|
48
|
+
},
|
|
49
|
+
opts
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Unwrap controls if it's a signal
|
|
53
|
+
const getControls = () => {
|
|
54
|
+
if (isSignal(settings.controls)) {
|
|
55
|
+
return settings.controls();
|
|
56
|
+
}
|
|
57
|
+
return settings.controls;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
let outerRadius = 70;
|
|
61
|
+
let innerRadius = 10;
|
|
62
|
+
const innerAlphaStandby = 0.5;
|
|
63
|
+
|
|
64
|
+
let dragging = false;
|
|
65
|
+
let startPosition: PIXI.PointData | null = null;
|
|
66
|
+
let power = 0;
|
|
67
|
+
|
|
68
|
+
const innerPositionX = signal(0);
|
|
69
|
+
const innerPositionY = signal(0);
|
|
70
|
+
const innerAlpha = signal(innerAlphaStandby);
|
|
71
|
+
|
|
72
|
+
function getPower(centerPoint: PIXI.Point) {
|
|
73
|
+
const a = centerPoint.x - 0;
|
|
74
|
+
const b = centerPoint.y - 0;
|
|
75
|
+
return Math.min(1, Math.sqrt(a * a + b * b) / outerRadius);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getDirection(center: PIXI.Point) {
|
|
79
|
+
let rad = Math.atan2(center.y, center.x); // [-PI, PI]
|
|
80
|
+
if ((rad >= -Math.PI / 8 && rad < 0) || (rad >= 0 && rad < Math.PI / 8)) {
|
|
81
|
+
return Direction.RIGHT;
|
|
82
|
+
} else if (rad >= Math.PI / 8 && rad < (3 * Math.PI) / 8) {
|
|
83
|
+
return Direction.BOTTOM_RIGHT;
|
|
84
|
+
} else if (rad >= (3 * Math.PI) / 8 && rad < (5 * Math.PI) / 8) {
|
|
85
|
+
return Direction.BOTTOM;
|
|
86
|
+
} else if (rad >= (5 * Math.PI) / 8 && rad < (7 * Math.PI) / 8) {
|
|
87
|
+
return Direction.BOTTOM_LEFT;
|
|
88
|
+
} else if (
|
|
89
|
+
(rad >= (7 * Math.PI) / 8 && rad < Math.PI) ||
|
|
90
|
+
(rad >= -Math.PI && rad < (-7 * Math.PI) / 8)
|
|
91
|
+
) {
|
|
92
|
+
return Direction.LEFT;
|
|
93
|
+
} else if (rad >= (-7 * Math.PI) / 8 && rad < (-5 * Math.PI) / 8) {
|
|
94
|
+
return Direction.TOP_LEFT;
|
|
95
|
+
} else if (rad >= (-5 * Math.PI) / 8 && rad < (-3 * Math.PI) / 8) {
|
|
96
|
+
return Direction.TOP;
|
|
97
|
+
} else {
|
|
98
|
+
return Direction.TOP_RIGHT;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function handleDragStart(event: any) {
|
|
103
|
+
startPosition = event.getLocalPosition(this);
|
|
104
|
+
dragging = true;
|
|
105
|
+
innerAlpha.set(1);
|
|
106
|
+
settings.onStart?.();
|
|
107
|
+
|
|
108
|
+
// Notify controls if provided
|
|
109
|
+
const controls = getControls();
|
|
110
|
+
if (controls) {
|
|
111
|
+
// Check if it's JoystickControls instance
|
|
112
|
+
if (controls.handleJoystickStart) {
|
|
113
|
+
controls.handleJoystickStart();
|
|
114
|
+
}
|
|
115
|
+
// Check if it's ControlsDirective with joystick getter
|
|
116
|
+
else if (controls.joystick && controls.joystick.handleJoystickStart) {
|
|
117
|
+
controls.joystick.handleJoystickStart();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function handleDragEnd() {
|
|
123
|
+
if (!dragging) return;
|
|
124
|
+
innerPositionX.set(0);
|
|
125
|
+
innerPositionY.set(0);
|
|
126
|
+
dragging = false;
|
|
127
|
+
innerAlpha.set(innerAlphaStandby);
|
|
128
|
+
settings.onEnd?.();
|
|
129
|
+
|
|
130
|
+
// Notify controls if provided
|
|
131
|
+
const controls = getControls();
|
|
132
|
+
if (controls) {
|
|
133
|
+
// Check if it's JoystickControls instance
|
|
134
|
+
if (controls.handleJoystickEnd) {
|
|
135
|
+
controls.handleJoystickEnd();
|
|
136
|
+
}
|
|
137
|
+
// Check if it's ControlsDirective with joystick getter
|
|
138
|
+
else if (controls.joystick && controls.joystick.handleJoystickEnd) {
|
|
139
|
+
controls.joystick.handleJoystickEnd();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function handleDragMove(event: any) {
|
|
145
|
+
if (dragging == false) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let newPosition = event.getLocalPosition(this);
|
|
150
|
+
|
|
151
|
+
let sideX = newPosition.x - (startPosition?.x ?? 0);
|
|
152
|
+
let sideY = newPosition.y - (startPosition?.y ?? 0);
|
|
153
|
+
|
|
154
|
+
let centerPoint = new PIXI.Point(0, 0);
|
|
155
|
+
let angle = 0;
|
|
156
|
+
|
|
157
|
+
if (sideX == 0 && sideY == 0) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let calRadius = 0;
|
|
162
|
+
|
|
163
|
+
if (sideX * sideX + sideY * sideY >= outerRadius * outerRadius) {
|
|
164
|
+
calRadius = outerRadius;
|
|
165
|
+
} else {
|
|
166
|
+
calRadius = outerRadius - innerRadius;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* x: -1 <-> 1
|
|
171
|
+
* y: -1 <-> 1
|
|
172
|
+
* Y
|
|
173
|
+
* ^
|
|
174
|
+
* |
|
|
175
|
+
* 180 | 90
|
|
176
|
+
* ------------> X
|
|
177
|
+
* 270 | 360
|
|
178
|
+
* |
|
|
179
|
+
* |
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
let direction = Direction.LEFT;
|
|
183
|
+
|
|
184
|
+
if (sideX == 0) {
|
|
185
|
+
if (sideY > 0) {
|
|
186
|
+
centerPoint.set(0, sideY > outerRadius ? outerRadius : sideY);
|
|
187
|
+
angle = 270;
|
|
188
|
+
direction = Direction.BOTTOM;
|
|
189
|
+
} else {
|
|
190
|
+
centerPoint.set(
|
|
191
|
+
0,
|
|
192
|
+
-(Math.abs(sideY) > outerRadius ? outerRadius : Math.abs(sideY))
|
|
193
|
+
);
|
|
194
|
+
angle = 90;
|
|
195
|
+
direction = Direction.TOP;
|
|
196
|
+
}
|
|
197
|
+
innerPositionX.set(centerPoint.x);
|
|
198
|
+
innerPositionY.set(centerPoint.y);
|
|
199
|
+
power = getPower(centerPoint);
|
|
200
|
+
const changeEvent = { angle, direction, power };
|
|
201
|
+
settings.onChange?.(changeEvent);
|
|
202
|
+
|
|
203
|
+
// Notify controls if provided
|
|
204
|
+
const controls = getControls();
|
|
205
|
+
if (controls) {
|
|
206
|
+
// Check if it's JoystickControls instance
|
|
207
|
+
if (controls.handleJoystickChange) {
|
|
208
|
+
controls.handleJoystickChange(changeEvent);
|
|
209
|
+
}
|
|
210
|
+
// Check if it's ControlsDirective with joystick getter
|
|
211
|
+
else if (controls.joystick && controls.joystick.handleJoystickChange) {
|
|
212
|
+
controls.joystick.handleJoystickChange(changeEvent);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (sideY == 0) {
|
|
219
|
+
if (sideX > 0) {
|
|
220
|
+
centerPoint.set(
|
|
221
|
+
Math.abs(sideX) > outerRadius ? outerRadius : Math.abs(sideX),
|
|
222
|
+
0
|
|
223
|
+
);
|
|
224
|
+
angle = 0;
|
|
225
|
+
direction = Direction.RIGHT;
|
|
226
|
+
} else {
|
|
227
|
+
centerPoint.set(
|
|
228
|
+
-(Math.abs(sideX) > outerRadius ? outerRadius : Math.abs(sideX)),
|
|
229
|
+
0
|
|
230
|
+
);
|
|
231
|
+
angle = 180;
|
|
232
|
+
direction = Direction.LEFT;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
innerPositionX.set(centerPoint.x);
|
|
236
|
+
innerPositionY.set(centerPoint.y);
|
|
237
|
+
power = getPower(centerPoint);
|
|
238
|
+
const changeEvent = { angle, direction, power };
|
|
239
|
+
settings.onChange?.(changeEvent);
|
|
240
|
+
|
|
241
|
+
// Notify controls if provided
|
|
242
|
+
const controls = getControls();
|
|
243
|
+
if (controls) {
|
|
244
|
+
// Check if it's JoystickControls instance
|
|
245
|
+
if (controls.handleJoystickChange) {
|
|
246
|
+
controls.handleJoystickChange(changeEvent);
|
|
247
|
+
}
|
|
248
|
+
// Check if it's ControlsDirective with joystick getter
|
|
249
|
+
else if (controls.joystick && controls.joystick.handleJoystickChange) {
|
|
250
|
+
controls.joystick.handleJoystickChange(changeEvent);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let tanVal = Math.abs(sideY / sideX);
|
|
257
|
+
let radian = Math.atan(tanVal);
|
|
258
|
+
angle = (radian * 180) / Math.PI;
|
|
259
|
+
|
|
260
|
+
let centerX = 0;
|
|
261
|
+
let centerY = 0;
|
|
262
|
+
|
|
263
|
+
if (sideX * sideX + sideY * sideY >= outerRadius * outerRadius) {
|
|
264
|
+
centerX = outerRadius * Math.cos(radian);
|
|
265
|
+
centerY = outerRadius * Math.sin(radian);
|
|
266
|
+
} else {
|
|
267
|
+
centerX = Math.abs(sideX) > outerRadius ? outerRadius : Math.abs(sideX);
|
|
268
|
+
centerY = Math.abs(sideY) > outerRadius ? outerRadius : Math.abs(sideY);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (sideY < 0) {
|
|
272
|
+
centerY = -Math.abs(centerY);
|
|
273
|
+
}
|
|
274
|
+
if (sideX < 0) {
|
|
275
|
+
centerX = -Math.abs(centerX);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (sideX > 0 && sideY < 0) {
|
|
279
|
+
// < 90
|
|
280
|
+
} else if (sideX < 0 && sideY < 0) {
|
|
281
|
+
// 90 ~ 180
|
|
282
|
+
angle = 180 - angle;
|
|
283
|
+
} else if (sideX < 0 && sideY > 0) {
|
|
284
|
+
// 180 ~ 270
|
|
285
|
+
angle = angle + 180;
|
|
286
|
+
} else if (sideX > 0 && sideY > 0) {
|
|
287
|
+
// 270 ~ 369
|
|
288
|
+
angle = 360 - angle;
|
|
289
|
+
}
|
|
290
|
+
centerPoint.set(centerX, centerY);
|
|
291
|
+
power = getPower(centerPoint);
|
|
292
|
+
|
|
293
|
+
direction = getDirection(centerPoint);
|
|
294
|
+
innerPositionX.set(centerPoint.x);
|
|
295
|
+
innerPositionY.set(centerPoint.y);
|
|
296
|
+
const changeEvent = { angle, direction, power };
|
|
297
|
+
settings.onChange?.(changeEvent);
|
|
298
|
+
|
|
299
|
+
// Notify controls if provided
|
|
300
|
+
const controls = getControls();
|
|
301
|
+
if (controls) {
|
|
302
|
+
// Check if it's JoystickControls instance
|
|
303
|
+
if (controls.handleJoystickChange) {
|
|
304
|
+
controls.handleJoystickChange(changeEvent);
|
|
305
|
+
}
|
|
306
|
+
// Check if it's ControlsDirective with joystick getter
|
|
307
|
+
else if (controls.joystick && controls.joystick.handleJoystickChange) {
|
|
308
|
+
controls.joystick.handleJoystickChange(changeEvent);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let innerElement;
|
|
314
|
+
let outerElement;
|
|
315
|
+
|
|
316
|
+
if (!settings.outer) {
|
|
317
|
+
outerElement = h(Graphics, {
|
|
318
|
+
draw: (g) => {
|
|
319
|
+
g.circle(0, 0, outerRadius).fill(settings.outerColor);
|
|
320
|
+
},
|
|
321
|
+
alpha: 0.5,
|
|
322
|
+
});
|
|
323
|
+
} else {
|
|
324
|
+
outerElement = h(Sprite, {
|
|
325
|
+
image: settings.outer,
|
|
326
|
+
anchor: { x: 0.5, y: 0.5 },
|
|
327
|
+
scale: settings.outerScale,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const innerOptions: any = {
|
|
332
|
+
scale: settings.innerScale,
|
|
333
|
+
x: innerPositionX,
|
|
334
|
+
y: innerPositionY,
|
|
335
|
+
alpha: innerAlpha,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
if (!settings.inner) {
|
|
339
|
+
innerElement = h(Graphics, {
|
|
340
|
+
draw: (g) => {
|
|
341
|
+
g.circle(0, 0, innerRadius * 2.5).fill(settings.innerColor);
|
|
342
|
+
},
|
|
343
|
+
...innerOptions,
|
|
344
|
+
});
|
|
345
|
+
} else {
|
|
346
|
+
innerElement = settings.inner
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return h(
|
|
350
|
+
Container,
|
|
351
|
+
{
|
|
352
|
+
...opts,
|
|
353
|
+
pointerdown: handleDragStart,
|
|
354
|
+
pointerup: handleDragEnd,
|
|
355
|
+
pointerupoutside: handleDragEnd,
|
|
356
|
+
pointermove: handleDragMove,
|
|
357
|
+
},
|
|
358
|
+
outerElement,
|
|
359
|
+
innerElement,
|
|
360
|
+
);
|
|
361
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -13,4 +13,5 @@ export { NineSliceSprite } from './NineSliceSprite'
|
|
|
13
13
|
export { type ComponentInstance } from './DisplayObject'
|
|
14
14
|
export { DOMContainer } from './DOMContainer'
|
|
15
15
|
export { DOMElement } from './DOMElement'
|
|
16
|
-
export { Button, ButtonState, type ButtonProps, type ButtonStyle } from './Button'
|
|
16
|
+
export { Button, ButtonState, type ButtonProps, type ButtonStyle } from './Button'
|
|
17
|
+
export { Joystick, type JoystickSettings, type JoystickChangeEvent } from './Joystick'
|
|
@@ -3,14 +3,15 @@ import { Element } from "../engine/reactive";
|
|
|
3
3
|
import { ControlsBase, Controls } from "./ControlsBase";
|
|
4
4
|
import { KeyboardControls } from "./KeyboardControls";
|
|
5
5
|
import { GamepadControls, GamepadConfig } from "./GamepadControls";
|
|
6
|
+
import { JoystickControls, JoystickConfig } from "./JoystickControls";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* Controls directive that coordinates keyboard and
|
|
9
|
+
* Controls directive that coordinates keyboard, gamepad, and joystick input systems
|
|
9
10
|
*
|
|
10
|
-
* This directive automatically activates
|
|
11
|
+
* This directive automatically activates keyboard, gamepad, and joystick controls when available.
|
|
11
12
|
* The gamepad is automatically enabled if joypad.js is detected in the environment.
|
|
12
13
|
*
|
|
13
|
-
*
|
|
14
|
+
* All systems share the same control configuration and can work simultaneously.
|
|
14
15
|
*
|
|
15
16
|
* @example
|
|
16
17
|
* ```html
|
|
@@ -25,10 +26,11 @@ import { GamepadControls, GamepadConfig } from "./GamepadControls";
|
|
|
25
26
|
export class ControlsDirective extends Directive {
|
|
26
27
|
private keyboardControls: KeyboardControls | null = null;
|
|
27
28
|
private gamepadControls: GamepadControls | null = null;
|
|
29
|
+
private joystickControls: JoystickControls | null = null;
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Initialize the controls directive
|
|
31
|
-
* Sets up keyboard and
|
|
33
|
+
* Sets up keyboard, gamepad, and joystick controls if available
|
|
32
34
|
*/
|
|
33
35
|
onInit(element: Element) {
|
|
34
36
|
const value = element.props.controls?.value ?? element.props.controls;
|
|
@@ -47,6 +49,14 @@ export class ControlsDirective extends Directive {
|
|
|
47
49
|
this.gamepadControls.setInputs(value as Controls & { gamepad?: GamepadConfig });
|
|
48
50
|
this.gamepadControls.start();
|
|
49
51
|
}
|
|
52
|
+
|
|
53
|
+
// Initialize joystick controls if joystick config is present
|
|
54
|
+
const joystickConfig = (value as Controls & { joystick?: JoystickConfig }).joystick;
|
|
55
|
+
if (joystickConfig !== undefined && joystickConfig.enabled !== false) {
|
|
56
|
+
this.joystickControls = new JoystickControls();
|
|
57
|
+
this.joystickControls.setInputs(value as Controls & { joystick?: JoystickConfig });
|
|
58
|
+
this.joystickControls.start();
|
|
59
|
+
}
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
/**
|
|
@@ -84,6 +94,11 @@ export class ControlsDirective extends Directive {
|
|
|
84
94
|
this.gamepadControls.destroy();
|
|
85
95
|
this.gamepadControls = null;
|
|
86
96
|
}
|
|
97
|
+
|
|
98
|
+
if (this.joystickControls) {
|
|
99
|
+
this.joystickControls.destroy();
|
|
100
|
+
this.joystickControls = null;
|
|
101
|
+
}
|
|
87
102
|
}
|
|
88
103
|
|
|
89
104
|
/**
|
|
@@ -113,20 +128,24 @@ export class ControlsDirective extends Directive {
|
|
|
113
128
|
*
|
|
114
129
|
* @param controlName - Name of the control
|
|
115
130
|
* @param isDown - Whether the control is pressed (true) or released (false)
|
|
131
|
+
* @param payload - Optional payload to pass to keyDown/keyUp callbacks (e.g., { power: 0.8 })
|
|
116
132
|
* @returns Promise that resolves when the action is complete
|
|
117
133
|
*/
|
|
118
|
-
async applyControl(controlName: string | number, isDown?: boolean): Promise<void> {
|
|
134
|
+
async applyControl(controlName: string | number, isDown?: boolean, payload?: any): Promise<void> {
|
|
119
135
|
if (this.keyboardControls) {
|
|
120
136
|
await this.keyboardControls.applyControl(controlName, isDown);
|
|
121
137
|
}
|
|
122
138
|
if (this.gamepadControls) {
|
|
123
|
-
await this.gamepadControls.applyControl(controlName, isDown);
|
|
139
|
+
await this.gamepadControls.applyControl(controlName, isDown, payload);
|
|
140
|
+
}
|
|
141
|
+
if (this.joystickControls) {
|
|
142
|
+
await this.joystickControls.applyControl(controlName, isDown, payload);
|
|
124
143
|
}
|
|
125
144
|
}
|
|
126
145
|
|
|
127
146
|
/**
|
|
128
147
|
* Stop listening to inputs
|
|
129
|
-
* Stops
|
|
148
|
+
* Stops keyboard, gamepad, and joystick input processing
|
|
130
149
|
*/
|
|
131
150
|
stopInputs() {
|
|
132
151
|
if (this.keyboardControls) {
|
|
@@ -135,11 +154,14 @@ export class ControlsDirective extends Directive {
|
|
|
135
154
|
if (this.gamepadControls) {
|
|
136
155
|
this.gamepadControls.stopInputs();
|
|
137
156
|
}
|
|
157
|
+
if (this.joystickControls) {
|
|
158
|
+
this.joystickControls.stopInputs();
|
|
159
|
+
}
|
|
138
160
|
}
|
|
139
161
|
|
|
140
162
|
/**
|
|
141
163
|
* Resume listening to inputs
|
|
142
|
-
* Resumes
|
|
164
|
+
* Resumes keyboard, gamepad, and joystick input processing
|
|
143
165
|
*/
|
|
144
166
|
listenInputs() {
|
|
145
167
|
if (this.keyboardControls) {
|
|
@@ -148,6 +170,9 @@ export class ControlsDirective extends Directive {
|
|
|
148
170
|
if (this.gamepadControls) {
|
|
149
171
|
this.gamepadControls.listenInputs();
|
|
150
172
|
}
|
|
173
|
+
if (this.joystickControls) {
|
|
174
|
+
this.joystickControls.listenInputs();
|
|
175
|
+
}
|
|
151
176
|
}
|
|
152
177
|
|
|
153
178
|
/**
|
|
@@ -177,6 +202,15 @@ export class ControlsDirective extends Directive {
|
|
|
177
202
|
get gamepad(): GamepadControls | null {
|
|
178
203
|
return this.gamepadControls;
|
|
179
204
|
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get the joystick controls instance
|
|
208
|
+
*
|
|
209
|
+
* @returns JoystickControls instance or null
|
|
210
|
+
*/
|
|
211
|
+
get joystick(): JoystickControls | null {
|
|
212
|
+
return this.joystickControls;
|
|
213
|
+
}
|
|
180
214
|
}
|
|
181
215
|
|
|
182
216
|
registerDirective('controls', ControlsDirective);
|
|
@@ -112,6 +112,7 @@ export class GamepadControls extends ControlsBase {
|
|
|
112
112
|
private joypad: any = null;
|
|
113
113
|
private connectCallbacks: Array<() => void> = [];
|
|
114
114
|
private disconnectCallbacks: Array<() => void> = [];
|
|
115
|
+
private currentPower: number = 0;
|
|
115
116
|
|
|
116
117
|
/**
|
|
117
118
|
* Setup gamepad event listeners
|
|
@@ -191,6 +192,7 @@ export class GamepadControls extends ControlsBase {
|
|
|
191
192
|
this.gamepadMoving = false;
|
|
192
193
|
this.gamepadDirections = {};
|
|
193
194
|
this.gamepadAxisDate = 0;
|
|
195
|
+
this.currentPower = 0;
|
|
194
196
|
|
|
195
197
|
// Update gamepadConnected signal if provided
|
|
196
198
|
if (this.gamepadConfig.gamepadConnected) {
|
|
@@ -243,6 +245,21 @@ export class GamepadControls extends ControlsBase {
|
|
|
243
245
|
else if (direction === 'left') direction = axisMapping['left'] || 'left';
|
|
244
246
|
else if (direction === 'right') direction = axisMapping['right'] || 'right';
|
|
245
247
|
|
|
248
|
+
// Calculate power/intensity from axis values
|
|
249
|
+
// Get the first connected gamepad instance
|
|
250
|
+
if (this.joypad && this.joypad.instances) {
|
|
251
|
+
const gamepadInstances = Object.values(this.joypad.instances) as any[];
|
|
252
|
+
if (gamepadInstances.length > 0) {
|
|
253
|
+
const gamepad = gamepadInstances[0];
|
|
254
|
+
// Get axes values (axes 0-1 for left stick, 2-3 for right stick)
|
|
255
|
+
// We'll use the left stick by default (axes 0 and 1)
|
|
256
|
+
const axisX = gamepad.axes?.[0] || 0;
|
|
257
|
+
const axisY = gamepad.axes?.[1] || 0;
|
|
258
|
+
// Calculate power as magnitude of the vector
|
|
259
|
+
this.currentPower = Math.min(1, Math.sqrt(axisX * axisX + axisY * axisY));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
246
263
|
// Update active directions
|
|
247
264
|
this.gamepadDirections = {
|
|
248
265
|
[direction]: true
|
|
@@ -258,7 +275,7 @@ export class GamepadControls extends ControlsBase {
|
|
|
258
275
|
}
|
|
259
276
|
}
|
|
260
277
|
|
|
261
|
-
// Trigger movement
|
|
278
|
+
// Trigger movement with power
|
|
262
279
|
this.processGamepadMovement();
|
|
263
280
|
}
|
|
264
281
|
|
|
@@ -270,9 +287,20 @@ export class GamepadControls extends ControlsBase {
|
|
|
270
287
|
if (!this.gamepadMoving) return;
|
|
271
288
|
if (this.stop) return;
|
|
272
289
|
|
|
290
|
+
// Update current power from gamepad axes if available
|
|
291
|
+
if (this.joypad && this.joypad.instances) {
|
|
292
|
+
const gamepadInstances = Object.values(this.joypad.instances) as any[];
|
|
293
|
+
if (gamepadInstances.length > 0) {
|
|
294
|
+
const gamepad = gamepadInstances[0];
|
|
295
|
+
const axisX = gamepad.axes?.[0] || 0;
|
|
296
|
+
const axisY = gamepad.axes?.[1] || 0;
|
|
297
|
+
this.currentPower = Math.min(1, Math.sqrt(axisX * axisX + axisY * axisY));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
273
301
|
for (const direction in this.gamepadDirections) {
|
|
274
302
|
if (this.gamepadDirections[direction]) {
|
|
275
|
-
this.applyControl(direction, true).catch(() => {
|
|
303
|
+
this.applyControl(direction, true, { power: this.currentPower }).catch(() => {
|
|
276
304
|
// Ignore errors
|
|
277
305
|
});
|
|
278
306
|
}
|
|
@@ -363,9 +391,10 @@ export class GamepadControls extends ControlsBase {
|
|
|
363
391
|
*
|
|
364
392
|
* @param controlName - Name of the control
|
|
365
393
|
* @param isDown - Whether the control is pressed (true) or released (false)
|
|
394
|
+
* @param payload - Optional payload to pass to keyDown/keyUp callbacks (e.g., { power: 0.8 })
|
|
366
395
|
* @returns Promise that resolves when the action is complete
|
|
367
396
|
*/
|
|
368
|
-
async applyControl(controlName: string | number, isDown?: boolean): Promise<void> {
|
|
397
|
+
async applyControl(controlName: string | number, isDown?: boolean, payload?: any): Promise<void> {
|
|
369
398
|
const control = this._controlsOptions[controlName];
|
|
370
399
|
if (!control) return;
|
|
371
400
|
|
|
@@ -378,40 +407,40 @@ export class GamepadControls extends ControlsBase {
|
|
|
378
407
|
if (isDown === undefined) {
|
|
379
408
|
// Press and release (simulate button press)
|
|
380
409
|
if (boundKey.options.keyDown) {
|
|
381
|
-
let parameters = boundKey.parameters;
|
|
410
|
+
let parameters = payload ?? boundKey.parameters;
|
|
382
411
|
if (typeof parameters === "function") {
|
|
383
412
|
parameters = parameters();
|
|
384
413
|
}
|
|
385
|
-
boundKey.options.keyDown(boundKey);
|
|
414
|
+
boundKey.options.keyDown(boundKey, parameters);
|
|
386
415
|
}
|
|
387
416
|
// Release after a short delay (similar to keyboard)
|
|
388
417
|
return new Promise((resolve) => {
|
|
389
418
|
setTimeout(() => {
|
|
390
419
|
if (boundKey.options.keyUp) {
|
|
391
|
-
let parameters = boundKey.parameters;
|
|
420
|
+
let parameters = payload ?? boundKey.parameters;
|
|
392
421
|
if (typeof parameters === "function") {
|
|
393
422
|
parameters = parameters();
|
|
394
423
|
}
|
|
395
|
-
boundKey.options.keyUp(boundKey);
|
|
424
|
+
boundKey.options.keyUp(boundKey, parameters);
|
|
396
425
|
}
|
|
397
426
|
resolve();
|
|
398
427
|
}, 200);
|
|
399
428
|
});
|
|
400
429
|
} else if (isDown) {
|
|
401
430
|
if (boundKey.options.keyDown) {
|
|
402
|
-
let parameters = boundKey.parameters;
|
|
431
|
+
let parameters = payload ?? boundKey.parameters;
|
|
403
432
|
if (typeof parameters === "function") {
|
|
404
433
|
parameters = parameters();
|
|
405
434
|
}
|
|
406
|
-
boundKey.options.keyDown(boundKey);
|
|
435
|
+
boundKey.options.keyDown(boundKey, parameters);
|
|
407
436
|
}
|
|
408
437
|
} else {
|
|
409
438
|
if (boundKey.options.keyUp) {
|
|
410
|
-
let parameters = boundKey.parameters;
|
|
439
|
+
let parameters = payload ?? boundKey.parameters;
|
|
411
440
|
if (typeof parameters === "function") {
|
|
412
441
|
parameters = parameters();
|
|
413
442
|
}
|
|
414
|
-
boundKey.options.keyUp(boundKey);
|
|
443
|
+
boundKey.options.keyUp(boundKey, parameters);
|
|
415
444
|
}
|
|
416
445
|
}
|
|
417
446
|
break;
|