canvasengine 2.0.0-beta.4 → 2.0.0-beta.40
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-DgECR3yZ.js +172 -0
- package/dist/DebugRenderer-DgECR3yZ.js.map +1 -0
- package/dist/components/Button.d.ts +183 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Canvas.d.ts +18 -0
- package/dist/components/Canvas.d.ts.map +1 -0
- package/dist/components/DOMElement.d.ts +44 -0
- package/dist/components/DOMElement.d.ts.map +1 -0
- package/dist/components/Graphic.d.ts +65 -0
- package/dist/components/Graphic.d.ts.map +1 -0
- package/dist/components/Joystick.d.ts +36 -0
- package/dist/components/Joystick.d.ts.map +1 -0
- package/dist/components/NineSliceSprite.d.ts +17 -0
- package/dist/components/NineSliceSprite.d.ts.map +1 -0
- package/dist/components/ParticleEmitter.d.ts +5 -0
- package/dist/components/ParticleEmitter.d.ts.map +1 -0
- package/dist/components/Scene.d.ts +2 -0
- package/dist/components/Scene.d.ts.map +1 -0
- package/dist/components/Text.d.ts +26 -0
- package/dist/components/Text.d.ts.map +1 -0
- package/dist/components/TilingSprite.d.ts +18 -0
- package/dist/components/TilingSprite.d.ts.map +1 -0
- package/dist/components/Video.d.ts +15 -0
- package/dist/components/Video.d.ts.map +1 -0
- package/dist/components/index.d.ts +18 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/types/DisplayObject.d.ts +110 -0
- package/dist/components/types/DisplayObject.d.ts.map +1 -0
- package/dist/components/types/MouseEvent.d.ts +4 -0
- package/dist/components/types/MouseEvent.d.ts.map +1 -0
- package/dist/components/types/Spritesheet.d.ts +248 -0
- package/dist/components/types/Spritesheet.d.ts.map +1 -0
- package/dist/components/types/index.d.ts +5 -0
- package/dist/components/types/index.d.ts.map +1 -0
- package/dist/directives/Controls.d.ts +113 -0
- package/dist/directives/Controls.d.ts.map +1 -0
- package/dist/directives/ControlsBase.d.ts +198 -0
- package/dist/directives/ControlsBase.d.ts.map +1 -0
- package/dist/directives/Drag.d.ts +70 -0
- package/dist/directives/Drag.d.ts.map +1 -0
- package/dist/directives/Flash.d.ts +117 -0
- package/dist/directives/Flash.d.ts.map +1 -0
- package/dist/directives/GamepadControls.d.ts +225 -0
- package/dist/directives/GamepadControls.d.ts.map +1 -0
- package/dist/directives/JoystickControls.d.ts +172 -0
- package/dist/directives/JoystickControls.d.ts.map +1 -0
- package/dist/directives/KeyboardControls.d.ts +219 -0
- package/dist/directives/KeyboardControls.d.ts.map +1 -0
- package/dist/directives/Scheduler.d.ts +36 -0
- package/dist/directives/Scheduler.d.ts.map +1 -0
- package/dist/directives/Shake.d.ts +98 -0
- package/dist/directives/Shake.d.ts.map +1 -0
- package/dist/directives/Sound.d.ts +26 -0
- package/dist/directives/Sound.d.ts.map +1 -0
- package/dist/directives/Transition.d.ts +11 -0
- package/dist/directives/Transition.d.ts.map +1 -0
- package/dist/directives/ViewportCull.d.ts +12 -0
- package/dist/directives/ViewportCull.d.ts.map +1 -0
- package/dist/directives/ViewportFollow.d.ts +19 -0
- package/dist/directives/ViewportFollow.d.ts.map +1 -0
- package/dist/directives/index.d.ts +13 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/engine/animation.d.ts +73 -0
- package/dist/engine/animation.d.ts.map +1 -0
- package/dist/engine/bootstrap.d.ts +16 -0
- package/dist/engine/bootstrap.d.ts.map +1 -0
- package/dist/engine/directive.d.ts +14 -0
- package/dist/engine/directive.d.ts.map +1 -0
- package/dist/engine/reactive.d.ts +105 -0
- package/dist/engine/reactive.d.ts.map +1 -0
- package/dist/engine/signal.d.ts +72 -0
- package/dist/engine/signal.d.ts.map +1 -0
- package/dist/engine/trigger.d.ts +50 -0
- package/dist/engine/trigger.d.ts.map +1 -0
- package/dist/engine/utils.d.ts +90 -0
- package/dist/engine/utils.d.ts.map +1 -0
- package/dist/hooks/addContext.d.ts +2 -0
- package/dist/hooks/addContext.d.ts.map +1 -0
- package/dist/hooks/useProps.d.ts +42 -0
- package/dist/hooks/useProps.d.ts.map +1 -0
- package/dist/hooks/useRef.d.ts +5 -0
- package/dist/hooks/useRef.d.ts.map +1 -0
- package/dist/index-gb763Hyx.js +12560 -0
- package/dist/index-gb763Hyx.js.map +1 -0
- package/dist/index.d.ts +15 -1083
- package/dist/index.d.ts.map +1 -0
- package/dist/index.global.js +29 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +81 -3041
- package/dist/index.js.map +1 -1
- package/dist/utils/Ease.d.ts +17 -0
- package/dist/utils/Ease.d.ts.map +1 -0
- package/dist/utils/GlobalAssetLoader.d.ts +141 -0
- package/dist/utils/GlobalAssetLoader.d.ts.map +1 -0
- package/dist/utils/RadialGradient.d.ts +58 -0
- package/dist/utils/RadialGradient.d.ts.map +1 -0
- package/dist/utils/functions.d.ts +2 -0
- package/dist/utils/functions.d.ts.map +1 -0
- package/package.json +13 -7
- package/src/components/Button.ts +396 -0
- package/src/components/Canvas.ts +61 -45
- package/src/components/Container.ts +21 -2
- package/src/components/DOMContainer.ts +123 -0
- package/src/components/DOMElement.ts +421 -0
- package/src/components/DisplayObject.ts +350 -197
- package/src/components/Graphic.ts +200 -34
- package/src/components/Joystick.ts +361 -0
- package/src/components/Mesh.ts +222 -0
- package/src/components/NineSliceSprite.ts +4 -1
- package/src/components/ParticleEmitter.ts +12 -8
- package/src/components/Sprite.ts +306 -30
- package/src/components/Text.ts +125 -18
- package/src/components/Video.ts +110 -0
- package/src/components/Viewport.ts +59 -43
- package/src/components/index.ts +8 -2
- package/src/components/types/DisplayObject.ts +34 -0
- package/src/components/types/Spritesheet.ts +0 -118
- package/src/directives/Controls.ts +254 -0
- package/src/directives/ControlsBase.ts +266 -0
- package/src/directives/Drag.ts +357 -52
- package/src/directives/Flash.ts +409 -0
- package/src/directives/GamepadControls.ts +537 -0
- package/src/directives/JoystickControls.ts +396 -0
- package/src/directives/KeyboardControls.ts +66 -424
- package/src/directives/Shake.ts +282 -0
- package/src/directives/Sound.ts +94 -31
- package/src/directives/ViewportFollow.ts +35 -7
- package/src/directives/index.ts +12 -6
- package/src/engine/animation.ts +175 -21
- package/src/engine/bootstrap.ts +23 -3
- package/src/engine/directive.ts +2 -2
- package/src/engine/reactive.ts +780 -177
- package/src/engine/signal.ts +35 -4
- package/src/engine/trigger.ts +21 -4
- package/src/engine/utils.ts +19 -3
- package/src/hooks/useProps.ts +1 -1
- package/src/index.ts +4 -2
- package/src/utils/GlobalAssetLoader.ts +257 -0
- package/src/utils/functions.ts +7 -0
- package/testing/index.ts +12 -0
- package/tsconfig.json +17 -0
- package/vite.config.ts +39 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { Directive, registerDirective } from "../engine/directive";
|
|
2
|
+
import { Element, isElementFrozen } from "../engine/reactive";
|
|
3
|
+
import { ControlsBase, Controls } from "./ControlsBase";
|
|
4
|
+
import { KeyboardControls } from "./KeyboardControls";
|
|
5
|
+
import { GamepadControls, GamepadConfig } from "./GamepadControls";
|
|
6
|
+
import { JoystickControls, JoystickConfig } from "./JoystickControls";
|
|
7
|
+
import { Signal, isSignal } from "@signe/reactive";
|
|
8
|
+
import { Subscription } from "rxjs";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Controls directive that coordinates keyboard, gamepad, and joystick input systems
|
|
12
|
+
*
|
|
13
|
+
* This directive automatically activates keyboard, gamepad, and joystick controls when available.
|
|
14
|
+
* The gamepad is automatically enabled if joypad.js is detected in the environment.
|
|
15
|
+
*
|
|
16
|
+
* All systems share the same control configuration and can work simultaneously.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```html
|
|
20
|
+
* <Sprite
|
|
21
|
+
* image="path/to/image.png"
|
|
22
|
+
* controls={controlsConfig}
|
|
23
|
+
* x={x}
|
|
24
|
+
* y={y}
|
|
25
|
+
* />
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class ControlsDirective extends Directive {
|
|
29
|
+
private keyboardControls: KeyboardControls | null = null;
|
|
30
|
+
private gamepadControls: GamepadControls | null = null;
|
|
31
|
+
private joystickControls: JoystickControls | null = null;
|
|
32
|
+
private freezeSubscription: Subscription | null = null;
|
|
33
|
+
private element: Element | null = null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Initialize the controls directive
|
|
37
|
+
* Sets up keyboard, gamepad, and joystick controls if available
|
|
38
|
+
*/
|
|
39
|
+
onInit(element: Element) {
|
|
40
|
+
this.element = element;
|
|
41
|
+
const value = element.props.controls?.value ?? element.props.controls;
|
|
42
|
+
if (!value) return;
|
|
43
|
+
|
|
44
|
+
// Initialize keyboard controls (always available)
|
|
45
|
+
this.keyboardControls = new KeyboardControls();
|
|
46
|
+
this.keyboardControls.setInputs(value as Controls);
|
|
47
|
+
this.keyboardControls.start();
|
|
48
|
+
|
|
49
|
+
// Initialize gamepad controls if gamepad config is present
|
|
50
|
+
// GamepadControls will handle joypad.js availability internally
|
|
51
|
+
const gamepadConfig = (value as Controls & { gamepad?: GamepadConfig }).gamepad;
|
|
52
|
+
if (gamepadConfig !== undefined && gamepadConfig.enabled !== false) {
|
|
53
|
+
this.gamepadControls = new GamepadControls();
|
|
54
|
+
this.gamepadControls.setInputs(value as Controls & { gamepad?: GamepadConfig });
|
|
55
|
+
this.gamepadControls.start();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Initialize joystick controls if joystick config is present
|
|
59
|
+
const joystickConfig = (value as Controls & { joystick?: JoystickConfig }).joystick;
|
|
60
|
+
if (joystickConfig !== undefined && joystickConfig.enabled !== false) {
|
|
61
|
+
this.joystickControls = new JoystickControls();
|
|
62
|
+
this.joystickControls.setInputs(value as Controls & { joystick?: JoystickConfig });
|
|
63
|
+
this.joystickControls.start();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check initial freeze state
|
|
67
|
+
if (isElementFrozen(element)) {
|
|
68
|
+
this.stopInputs();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Subscribe to freeze prop if it's a signal
|
|
72
|
+
const freezeProp = element.propObservables?.freeze ?? element.props?.freeze;
|
|
73
|
+
if (isSignal(freezeProp)) {
|
|
74
|
+
this.freezeSubscription = (freezeProp as Signal<boolean>).observable.subscribe((isFrozen) => {
|
|
75
|
+
if (isFrozen) {
|
|
76
|
+
this.stopInputs();
|
|
77
|
+
} else {
|
|
78
|
+
this.listenInputs();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Mount hook (no specific action needed)
|
|
86
|
+
*/
|
|
87
|
+
onMount(element: Element) {}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Update controls configuration
|
|
91
|
+
* Updates both keyboard and gamepad controls
|
|
92
|
+
*/
|
|
93
|
+
onUpdate(props: any, element: Element) {
|
|
94
|
+
const value = props.controls?.value ?? props.controls;
|
|
95
|
+
if (value) {
|
|
96
|
+
if (this.keyboardControls) {
|
|
97
|
+
this.keyboardControls.setInputs(value as Controls);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (this.gamepadControls) {
|
|
101
|
+
this.gamepadControls.setInputs(value as Controls & { gamepad?: GamepadConfig });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Handle freeze prop update
|
|
106
|
+
if (props.freeze !== undefined && this.element) {
|
|
107
|
+
if (isElementFrozen(this.element)) {
|
|
108
|
+
this.stopInputs();
|
|
109
|
+
} else {
|
|
110
|
+
this.listenInputs();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Cleanup and destroy all control systems
|
|
117
|
+
*/
|
|
118
|
+
onDestroy(element: Element) {
|
|
119
|
+
if (this.freezeSubscription) {
|
|
120
|
+
this.freezeSubscription.unsubscribe();
|
|
121
|
+
this.freezeSubscription = null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (this.keyboardControls) {
|
|
125
|
+
this.keyboardControls.destroy();
|
|
126
|
+
this.keyboardControls = null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (this.gamepadControls) {
|
|
130
|
+
this.gamepadControls.destroy();
|
|
131
|
+
this.gamepadControls = null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (this.joystickControls) {
|
|
135
|
+
this.joystickControls.destroy();
|
|
136
|
+
this.joystickControls = null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.element = null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get a control by input name
|
|
144
|
+
* Delegates to keyboard controls (primary system)
|
|
145
|
+
*
|
|
146
|
+
* @param inputName - Name of the input/key
|
|
147
|
+
* @returns BoundKey if found, undefined otherwise
|
|
148
|
+
*/
|
|
149
|
+
getControl(inputName: string) {
|
|
150
|
+
return this.keyboardControls?.getControl(inputName);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get all bound controls
|
|
155
|
+
* Delegates to keyboard controls (primary system)
|
|
156
|
+
*
|
|
157
|
+
* @returns Object mapping input names to BoundKey objects
|
|
158
|
+
*/
|
|
159
|
+
getControls() {
|
|
160
|
+
return this.keyboardControls?.getControls() || {};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Apply a control action programmatically
|
|
165
|
+
* Applies to both keyboard and gamepad if available
|
|
166
|
+
*
|
|
167
|
+
* @param controlName - Name of the control
|
|
168
|
+
* @param isDown - Whether the control is pressed (true) or released (false)
|
|
169
|
+
* @param payload - Optional payload to pass to keyDown/keyUp callbacks (e.g., { power: 0.8 })
|
|
170
|
+
* @returns Promise that resolves when the action is complete
|
|
171
|
+
*/
|
|
172
|
+
async applyControl(controlName: string | number, isDown?: boolean, payload?: any): Promise<void> {
|
|
173
|
+
if (this.keyboardControls) {
|
|
174
|
+
await this.keyboardControls.applyControl(controlName, isDown);
|
|
175
|
+
}
|
|
176
|
+
if (this.gamepadControls) {
|
|
177
|
+
await this.gamepadControls.applyControl(controlName, isDown, payload);
|
|
178
|
+
}
|
|
179
|
+
if (this.joystickControls) {
|
|
180
|
+
await this.joystickControls.applyControl(controlName, isDown, payload);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Stop listening to inputs
|
|
186
|
+
* Stops keyboard, gamepad, and joystick input processing
|
|
187
|
+
*/
|
|
188
|
+
stopInputs() {
|
|
189
|
+
if (this.keyboardControls) {
|
|
190
|
+
this.keyboardControls.stopInputs();
|
|
191
|
+
}
|
|
192
|
+
if (this.gamepadControls) {
|
|
193
|
+
this.gamepadControls.stopInputs();
|
|
194
|
+
}
|
|
195
|
+
if (this.joystickControls) {
|
|
196
|
+
this.joystickControls.stopInputs();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Resume listening to inputs
|
|
202
|
+
* Resumes keyboard, gamepad, and joystick input processing
|
|
203
|
+
*/
|
|
204
|
+
listenInputs() {
|
|
205
|
+
if (this.keyboardControls) {
|
|
206
|
+
this.keyboardControls.listenInputs();
|
|
207
|
+
}
|
|
208
|
+
if (this.gamepadControls) {
|
|
209
|
+
this.gamepadControls.listenInputs();
|
|
210
|
+
}
|
|
211
|
+
if (this.joystickControls) {
|
|
212
|
+
this.joystickControls.listenInputs();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get the current controls configuration
|
|
218
|
+
* Returns keyboard controls options (both systems share the same config)
|
|
219
|
+
*
|
|
220
|
+
* @returns The controls options object
|
|
221
|
+
*/
|
|
222
|
+
get options(): Controls {
|
|
223
|
+
return this.keyboardControls?.options || {};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get the keyboard controls instance
|
|
228
|
+
*
|
|
229
|
+
* @returns KeyboardControls instance or null
|
|
230
|
+
*/
|
|
231
|
+
get keyboard(): KeyboardControls | null {
|
|
232
|
+
return this.keyboardControls;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get the gamepad controls instance
|
|
237
|
+
*
|
|
238
|
+
* @returns GamepadControls instance or null
|
|
239
|
+
*/
|
|
240
|
+
get gamepad(): GamepadControls | null {
|
|
241
|
+
return this.gamepadControls;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get the joystick controls instance
|
|
246
|
+
*
|
|
247
|
+
* @returns JoystickControls instance or null
|
|
248
|
+
*/
|
|
249
|
+
get joystick(): JoystickControls | null {
|
|
250
|
+
return this.joystickControls;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
registerDirective('controls', ControlsDirective);
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { fps2ms } from "../engine/utils";
|
|
2
|
+
|
|
3
|
+
export interface ControlOptions {
|
|
4
|
+
repeat?: boolean;
|
|
5
|
+
bind: string | string[];
|
|
6
|
+
keyUp?: Function;
|
|
7
|
+
keyDown?: Function;
|
|
8
|
+
delay?: number | {
|
|
9
|
+
duration: number;
|
|
10
|
+
otherControls?: (string)[];
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface Controls {
|
|
15
|
+
[controlName: string]: ControlOptions;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type BoundKey = { actionName: string, options: ControlOptions, parameters?: any };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Abstract base class for control systems (keyboard, gamepad, etc.)
|
|
22
|
+
*
|
|
23
|
+
* This class provides common functionality shared across all control implementations:
|
|
24
|
+
* - Input binding and management
|
|
25
|
+
* - Control configuration
|
|
26
|
+
* - Input state management
|
|
27
|
+
* - Common methods for querying and triggering controls
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* class MyControls extends ControlsBase {
|
|
32
|
+
* protected setupListeners() {
|
|
33
|
+
* // Setup specific input listeners
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* protected cleanup() {
|
|
37
|
+
* // Cleanup specific resources
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* protected preStep() {
|
|
41
|
+
* // Process inputs each frame
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export abstract class ControlsBase {
|
|
47
|
+
protected boundKeys: {
|
|
48
|
+
[keyName: string]: BoundKey
|
|
49
|
+
} = {}
|
|
50
|
+
protected stop: boolean = false
|
|
51
|
+
protected _controlsOptions: Controls = {}
|
|
52
|
+
protected interval: any
|
|
53
|
+
protected serverFps: number = 60
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Setup input listeners specific to this control implementation
|
|
57
|
+
* Must be implemented by subclasses
|
|
58
|
+
*/
|
|
59
|
+
protected abstract setupListeners(): void;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Cleanup resources specific to this control implementation
|
|
63
|
+
* Must be implemented by subclasses
|
|
64
|
+
*/
|
|
65
|
+
protected abstract cleanup(): void;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Process inputs each step/frame
|
|
69
|
+
* Must be implemented by subclasses
|
|
70
|
+
*/
|
|
71
|
+
protected abstract preStep(): void;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Start the control processing loop
|
|
75
|
+
* Initializes listeners and starts the interval
|
|
76
|
+
*/
|
|
77
|
+
start() {
|
|
78
|
+
this.setupListeners();
|
|
79
|
+
this.interval = setInterval(() => {
|
|
80
|
+
this.preStep()
|
|
81
|
+
}, fps2ms(this.serverFps ?? 60))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Stop the control processing and cleanup resources
|
|
86
|
+
*/
|
|
87
|
+
destroy() {
|
|
88
|
+
if (this.interval) {
|
|
89
|
+
clearInterval(this.interval)
|
|
90
|
+
}
|
|
91
|
+
this.cleanup();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Bind a key/input to a control action
|
|
96
|
+
*
|
|
97
|
+
* @param keys - Single key or array of keys to bind
|
|
98
|
+
* @param actionName - Name of the control action
|
|
99
|
+
* @param options - Control options (repeat, keyDown, keyUp, etc.)
|
|
100
|
+
* @param parameters - Optional parameters to pass to the control callbacks
|
|
101
|
+
*/
|
|
102
|
+
protected bindKey(keys: string | string[], actionName: string, options: ControlOptions, parameters?: object) {
|
|
103
|
+
if (!Array.isArray(keys)) keys = [keys]
|
|
104
|
+
const keyOptions = Object.assign({
|
|
105
|
+
repeat: false
|
|
106
|
+
}, options);
|
|
107
|
+
keys.forEach(keyName => {
|
|
108
|
+
this.boundKeys[keyName] = { actionName, options: keyOptions, parameters }
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Apply an input action for a bound key
|
|
114
|
+
* Can be overridden by subclasses for custom behavior
|
|
115
|
+
*
|
|
116
|
+
* @param keyName - Name of the key/input to process
|
|
117
|
+
*/
|
|
118
|
+
protected applyInput(keyName: string) {
|
|
119
|
+
const boundKey = this.boundKeys[keyName];
|
|
120
|
+
if (!boundKey) return;
|
|
121
|
+
|
|
122
|
+
const { repeat, keyDown } = boundKey.options;
|
|
123
|
+
// Default implementation - subclasses may override for state tracking
|
|
124
|
+
if (keyDown) {
|
|
125
|
+
let parameters = boundKey.parameters;
|
|
126
|
+
if (typeof parameters === "function") {
|
|
127
|
+
parameters = parameters();
|
|
128
|
+
}
|
|
129
|
+
keyDown(boundKey);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get a control by input name
|
|
135
|
+
*
|
|
136
|
+
* @param inputName - Name of the input/key
|
|
137
|
+
* @returns BoundKey if found, undefined otherwise
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const control = controls.getControl('up');
|
|
141
|
+
* if (control) {
|
|
142
|
+
* console.log(control.actionName); // 'up'
|
|
143
|
+
* }
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
getControl(inputName: string): BoundKey | undefined {
|
|
147
|
+
return this.boundKeys[inputName]
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get all bound controls
|
|
152
|
+
*
|
|
153
|
+
* @returns Object mapping input names to BoundKey objects
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* const allControls = controls.getControls();
|
|
157
|
+
* console.log(Object.keys(allControls)); // ['up', 'down', 'left', 'right', ...]
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
getControls(): { [key: string]: BoundKey } {
|
|
161
|
+
return this.boundKeys
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Apply a control action programmatically
|
|
166
|
+
*
|
|
167
|
+
* Must be implemented by subclasses to provide input-specific behavior
|
|
168
|
+
*
|
|
169
|
+
* @param controlName - Name or identifier of the control
|
|
170
|
+
* @param isDown - Whether the control is pressed down (true) or released (false)
|
|
171
|
+
* @returns Promise that resolves when the control action is complete
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* // Press a control
|
|
175
|
+
* await controls.applyControl('action', true);
|
|
176
|
+
*
|
|
177
|
+
* // Release a control
|
|
178
|
+
* await controls.applyControl('action', false);
|
|
179
|
+
*
|
|
180
|
+
* // Press and release (default)
|
|
181
|
+
* await controls.applyControl('action');
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
abstract applyControl(controlName: string | number, isDown?: boolean): Promise<void>;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Stop listening to inputs
|
|
188
|
+
* Input events will be ignored until listenInputs() is called
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```ts
|
|
192
|
+
* controls.stopInputs();
|
|
193
|
+
* // ... later
|
|
194
|
+
* controls.listenInputs();
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
stopInputs() {
|
|
198
|
+
this.stop = true
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Resume listening to inputs after stopInputs() was called
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* controls.stopInputs();
|
|
207
|
+
* // ... later
|
|
208
|
+
* controls.listenInputs();
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
listenInputs() {
|
|
212
|
+
this.stop = false
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Configure controls with input mappings
|
|
217
|
+
*
|
|
218
|
+
* This method sets up the binding between input keys/buttons and control actions.
|
|
219
|
+
* It clears existing bindings and creates new ones based on the provided configuration.
|
|
220
|
+
*
|
|
221
|
+
* @param inputs - Control configuration object
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* controls.setInputs({
|
|
225
|
+
* up: {
|
|
226
|
+
* repeat: true,
|
|
227
|
+
* bind: 'up',
|
|
228
|
+
* keyDown() {
|
|
229
|
+
* console.log('Up pressed');
|
|
230
|
+
* }
|
|
231
|
+
* },
|
|
232
|
+
* action: {
|
|
233
|
+
* bind: ['space', 'enter'],
|
|
234
|
+
* keyDown() {
|
|
235
|
+
* console.log('Action triggered');
|
|
236
|
+
* }
|
|
237
|
+
* }
|
|
238
|
+
* });
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
setInputs(inputs: Controls) {
|
|
242
|
+
if (!inputs) return
|
|
243
|
+
this.boundKeys = {}
|
|
244
|
+
for (let control in inputs) {
|
|
245
|
+
const option = inputs[control]
|
|
246
|
+
const { bind } = option
|
|
247
|
+
let inputsKey: any = bind
|
|
248
|
+
if (!Array.isArray(inputsKey)) {
|
|
249
|
+
inputsKey = [bind]
|
|
250
|
+
}
|
|
251
|
+
for (let input of inputsKey) {
|
|
252
|
+
this.bindKey(input, control, option)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
this._controlsOptions = inputs
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get the current controls configuration
|
|
260
|
+
*
|
|
261
|
+
* @returns The controls options object
|
|
262
|
+
*/
|
|
263
|
+
get options(): Controls {
|
|
264
|
+
return this._controlsOptions
|
|
265
|
+
}
|
|
266
|
+
}
|