canvasengine 2.0.0-beta.34 → 2.0.0-beta.36

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.
Files changed (56) hide show
  1. package/dist/{DebugRenderer-BYa_lwD-.js → DebugRenderer-DDfZuvTR.js} +2 -2
  2. package/dist/{DebugRenderer-BYa_lwD-.js.map → DebugRenderer-DDfZuvTR.js.map} +1 -1
  3. package/dist/components/Container.d.ts +4 -0
  4. package/dist/components/Container.d.ts.map +1 -1
  5. package/dist/components/DOMContainer.d.ts +4 -0
  6. package/dist/components/DOMContainer.d.ts.map +1 -1
  7. package/dist/components/DisplayObject.d.ts +4 -0
  8. package/dist/components/DisplayObject.d.ts.map +1 -1
  9. package/dist/components/Mesh.d.ts +4 -0
  10. package/dist/components/Mesh.d.ts.map +1 -1
  11. package/dist/components/Sprite.d.ts +48 -0
  12. package/dist/components/Sprite.d.ts.map +1 -1
  13. package/dist/components/Viewport.d.ts +4 -0
  14. package/dist/components/Viewport.d.ts.map +1 -1
  15. package/dist/components/types/DisplayObject.d.ts +4 -0
  16. package/dist/components/types/DisplayObject.d.ts.map +1 -1
  17. package/dist/directives/Controls.d.ts +102 -0
  18. package/dist/directives/Controls.d.ts.map +1 -0
  19. package/dist/directives/ControlsBase.d.ts +198 -0
  20. package/dist/directives/ControlsBase.d.ts.map +1 -0
  21. package/dist/directives/Flash.d.ts +117 -0
  22. package/dist/directives/Flash.d.ts.map +1 -0
  23. package/dist/directives/GamepadControls.d.ts +223 -0
  24. package/dist/directives/GamepadControls.d.ts.map +1 -0
  25. package/dist/directives/KeyboardControls.d.ts +55 -366
  26. package/dist/directives/KeyboardControls.d.ts.map +1 -1
  27. package/dist/directives/Shake.d.ts +98 -0
  28. package/dist/directives/Shake.d.ts.map +1 -0
  29. package/dist/directives/index.d.ts +11 -1
  30. package/dist/directives/index.d.ts.map +1 -1
  31. package/dist/engine/trigger.d.ts +2 -3
  32. package/dist/engine/trigger.d.ts.map +1 -1
  33. package/dist/engine/utils.d.ts.map +1 -1
  34. package/dist/{index-BLbc2zG5.js → index--faZajmD.js} +4547 -3970
  35. package/dist/index--faZajmD.js.map +1 -0
  36. package/dist/index.d.ts +1 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.global.js +6 -6
  39. package/dist/index.global.js.map +1 -1
  40. package/dist/index.js +70 -57
  41. package/package.json +2 -1
  42. package/src/components/Container.ts +17 -0
  43. package/src/components/DisplayObject.ts +45 -6
  44. package/src/components/Sprite.ts +87 -3
  45. package/src/components/types/DisplayObject.ts +4 -0
  46. package/src/directives/Controls.ts +182 -0
  47. package/src/directives/ControlsBase.ts +266 -0
  48. package/src/directives/Flash.ts +409 -0
  49. package/src/directives/GamepadControls.ts +515 -0
  50. package/src/directives/KeyboardControls.ts +66 -426
  51. package/src/directives/Shake.ts +282 -0
  52. package/src/directives/index.ts +11 -6
  53. package/src/engine/trigger.ts +2 -2
  54. package/src/engine/utils.ts +4 -0
  55. package/src/index.ts +1 -1
  56. package/dist/index-BLbc2zG5.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,64 +1,77 @@
1
- import { h as a } from "./index-BLbc2zG5.js";
2
- import { A as r, B as n, w as o, C as c, m as l, l as p, D as S, v as m, a5 as u, a4 as g, a2 as b, n as d, G as C, c as j, M as T, N as E, O, P as f, a3 as v, R as w, o as h, p as D, S as P, q as k, r as x, T as y, b as A, V as H, t as M, a1 as V, a0 as B, _ as G, d as N, J as R, H as q, K as U, e as z, W as I, $ as J, f as K, g as L, x as Q, j as W, i as X, y as Y, k as Z, X as _, I as $, Q as F, L as aa, Z as sa, z as ea, s as ta, U as ia, Y as ra, a as na, u as oa } from "./index-BLbc2zG5.js";
1
+ import { h as a } from "./index--faZajmD.js";
2
+ import { A as r, X as o, Y as n, q as l, v as c, r as p, C as S, d as u, U as m, W as g, ai as d, f as b, D as C, ah as h, af as T, w as j, j as w, G as D, t as f, c as v, I as E, K as O, M as k, Q as y, O as P, P as V, ag as x, R as A, z as B, S as G, g as H, e as M, B as N, y as R, J as q, L as F, T as I, x as K, b as U, H as z, N as J, V as L, ae as Q, ad as W, ab as X, k as Y, a2 as Z, a0 as _, a3 as $, l as aa, a7 as sa, ac as ea, m as ta, n as ia, Z as ra, o as oa, i as na, _ as la, p as ca, a8 as pa, a1 as Sa, a5 as ua, a4 as ma, aa as ga, $ as da, s as ba, a6 as Ca, a9 as ha, a as Ta, u as ja } from "./index--faZajmD.js";
3
3
  const e = a.Howler;
4
4
  export {
5
5
  r as ArraySubject,
6
- n as Button,
7
- o as ButtonState,
8
- c as Canvas,
9
- l as Circle,
6
+ o as Button,
7
+ n as ButtonState,
8
+ l as Canvas,
9
+ c as Circle,
10
10
  p as Container,
11
- S as DOMContainer,
12
- m as DOMElement,
13
- u as DisplayObject,
14
- g as EVENTS,
15
- b as Easing,
16
- d as Ellipse,
17
- C as Graphics,
18
- j as Howl,
11
+ S as ControlsBase,
12
+ u as ControlsDirective,
13
+ m as DOMContainer,
14
+ g as DOMElement,
15
+ d as DisplayObject,
16
+ b as Drag,
17
+ C as Drop,
18
+ h as EVENTS,
19
+ T as Easing,
20
+ j as Ellipse,
21
+ w as Flash,
22
+ D as GamepadControls,
23
+ f as Graphics,
24
+ v as Howl,
19
25
  e as Howler,
20
- T as Mesh,
21
- E as NineSliceSprite,
22
- O as ObjectSubject,
23
- f as ParticlesEmitter,
24
- v as RadialGradient,
25
- w as Rect,
26
- h as Scene,
27
- D as Sprite,
28
- P as Svg,
29
- k as Text,
30
- x as TilingSprite,
31
- y as Triangle,
32
- A as Utils,
33
- H as Video,
34
- M as Viewport,
35
- V as animatedSequence,
36
- B as animatedSignal,
37
- G as bootstrapCanvas,
38
- N as computed,
39
- R as cond,
40
- q as createComponent,
41
- U as currentSubscriptionsTracker,
42
- z as effect,
43
- I as h,
44
- J as isAnimatedSignal,
45
- K as isArraySubject,
46
- L as isComputed,
47
- Q as isElement,
48
- W as isObjectSubject,
49
- X as isObservable,
50
- Y as isPrimitive,
51
- Z as isSignal,
52
- _ as isTrigger,
53
- $ as loop,
54
- F as mount,
55
- aa as mountTracker,
56
- sa as on,
57
- ea as registerComponent,
58
- ta as signal,
59
- ia as tick,
60
- ra as trigger,
61
- na as useDefineProps,
62
- oa as useProps
26
+ E as Input,
27
+ O as KeyboardControls,
28
+ k as Mesh,
29
+ y as NineSliceSprite,
30
+ P as ObjectSubject,
31
+ V as ParticlesEmitter,
32
+ x as RadialGradient,
33
+ A as Rect,
34
+ B as Scene,
35
+ G as Scheduler,
36
+ H as Shake,
37
+ M as Sound,
38
+ N as Sprite,
39
+ R as Svg,
40
+ q as Text,
41
+ F as TilingSprite,
42
+ I as Transition,
43
+ K as Triangle,
44
+ U as Utils,
45
+ z as Video,
46
+ J as Viewport,
47
+ L as ViewportFollow,
48
+ Q as animatedSequence,
49
+ W as animatedSignal,
50
+ X as bootstrapCanvas,
51
+ Y as computed,
52
+ Z as cond,
53
+ _ as createComponent,
54
+ $ as currentSubscriptionsTracker,
55
+ aa as effect,
56
+ sa as h,
57
+ ea as isAnimatedSignal,
58
+ ta as isArraySubject,
59
+ ia as isComputed,
60
+ ra as isElement,
61
+ oa as isObjectSubject,
62
+ na as isObservable,
63
+ la as isPrimitive,
64
+ ca as isSignal,
65
+ pa as isTrigger,
66
+ Sa as loop,
67
+ ua as mount,
68
+ ma as mountTracker,
69
+ ga as on,
70
+ da as registerComponent,
71
+ ba as signal,
72
+ Ca as tick,
73
+ ha as trigger,
74
+ Ta as useDefineProps,
75
+ ja as useProps
63
76
  };
64
77
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasengine",
3
- "version": "2.0.0-beta.34",
3
+ "version": "2.0.0-beta.36",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,6 +12,7 @@
12
12
  "@pixi/layout": "^3.0.2",
13
13
  "@signe/reactive": "^2.3.3",
14
14
  "howler": "^2.2.4",
15
+ "joypad.js": "^2.3.5",
15
16
  "pixi-filters": "^6.0.5",
16
17
  "pixi-viewport": "^6.0.3",
17
18
  "popmotion": "^11.0.5",
@@ -4,6 +4,7 @@ import { DisplayObject } from "./DisplayObject";
4
4
  import { ComponentFunction } from "../engine/signal";
5
5
  import { DisplayObjectProps } from "./types/DisplayObject";
6
6
  import { setObservablePoint } from "../engine/utils";
7
+ import { isPercent } from "../utils/functions";
7
8
 
8
9
  interface ContainerProps extends DisplayObjectProps {
9
10
  sortableChildren?: boolean;
@@ -34,6 +35,22 @@ export class CanvasContainer extends DisplayObject(PixiContainer) {
34
35
  componentInstance.addChild(child);
35
36
  });
36
37
  }
38
+
39
+ // Listen to layout events to update displayWidth and displayHeight with computed values
40
+ const isWidthPercentage = isPercent(props.width);
41
+ const isHeightPercentage = isPercent(props.height);
42
+
43
+ if (isWidthPercentage || isHeightPercentage) {
44
+ this.on('layout', (event) => {
45
+ const layoutBox = event.computedLayout;
46
+ if (isWidthPercentage && layoutBox.width !== undefined) {
47
+ this.displayWidth.set(layoutBox.width);
48
+ }
49
+ if (isHeightPercentage && layoutBox.height !== undefined) {
50
+ this.displayHeight.set(layoutBox.height);
51
+ }
52
+ });
53
+ }
37
54
  }
38
55
  }
39
56
 
@@ -118,6 +118,8 @@ export function DisplayObject(extendClass) {
118
118
  disableLayout: boolean = false;
119
119
  // Store registered event listeners for cleanup
120
120
  #registeredEvents: Map<string, Function> = new Map();
121
+ // Store computed layout box dimensions
122
+ #computedLayoutBox: { width?: number; height?: number } | null = null;
121
123
 
122
124
  get deltaRatio() {
123
125
  return this.#canvasContext?.scheduler?.tick.value.deltaRatio;
@@ -188,6 +190,19 @@ export function DisplayObject(extendClass) {
188
190
  }
189
191
  this.isMounted = true;
190
192
  this.onUpdate(props);
193
+
194
+ // Listen to layout events to store computed layout dimensions
195
+ const layoutHandler = (event: any) => {
196
+ if (event.computedLayout) {
197
+ this.#computedLayoutBox = {
198
+ width: event.computedLayout.width,
199
+ height: event.computedLayout.height,
200
+ };
201
+ }
202
+ };
203
+ this.on('layout', layoutHandler);
204
+ this.#registeredEvents.set('layout', layoutHandler);
205
+
191
206
  if (this.onAfterMount) {
192
207
  await this.onAfterMount();
193
208
  }
@@ -466,12 +481,36 @@ export function DisplayObject(extendClass) {
466
481
  }
467
482
  }
468
483
 
469
- getWidth() {
470
- return this.displayWidth();
471
- }
472
-
473
- getHeight() {
474
- return this.displayHeight();
484
+ getWidth(): number {
485
+ // If width is a percentage, use computed layout box
486
+ if (isPercent(this.fullProps.width)) {
487
+ if (this.#computedLayoutBox?.width !== undefined) {
488
+ return this.#computedLayoutBox.width;
489
+ }
490
+ // Fallback to native width if layout not yet computed
491
+ return typeof this.width === 'number' ? this.width : 0;
492
+ }
493
+ // For static values, use native PixiJS width or displayWidth signal
494
+ const staticWidth = typeof this.width === 'number' && this.width > 0
495
+ ? this.width
496
+ : (typeof this.displayWidth() === 'number' ? this.displayWidth() : 0);
497
+ return staticWidth;
498
+ }
499
+
500
+ getHeight(): number {
501
+ // If height is a percentage, use computed layout box
502
+ if (isPercent(this.fullProps.height)) {
503
+ if (this.#computedLayoutBox?.height !== undefined) {
504
+ return this.#computedLayoutBox.height;
505
+ }
506
+ // Fallback to native height if layout not yet computed
507
+ return typeof this.height === 'number' ? this.height : 0;
508
+ }
509
+ // For static values, use native PixiJS height or displayHeight signal
510
+ const staticHeight = typeof this.height === 'number' && this.height > 0
511
+ ? this.height
512
+ : (typeof this.displayHeight() === 'number' ? this.displayHeight() : 0);
513
+ return staticHeight;
475
514
  }
476
515
 
477
516
  // Min/Max constraints
@@ -86,10 +86,65 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
86
86
 
87
87
  private currentAnimationContainer: Container | null = null;
88
88
 
89
+ /**
90
+ * Auto-detects image dimensions by loading the image and reading its natural size
91
+ * This is used when width/height are not explicitly provided in the spritesheet definition
92
+ *
93
+ * @param imagePath - Path to the image file
94
+ * @returns Object containing the detected width and height of the image
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const { width, height } = await sprite.detectImageDimensions('path/to/image.png');
99
+ * // width: 256, height: 128
100
+ * ```
101
+ */
102
+ private async detectImageDimensions(imagePath: string): Promise<{ width: number; height: number }> {
103
+ if (!imagePath || typeof imagePath !== 'string' || imagePath.trim() === '') {
104
+ throw new Error(`Invalid image path provided to detectImageDimensions: ${imagePath}`);
105
+ }
106
+
107
+ const texture = await Assets.load(imagePath);
108
+ return {
109
+ width: texture.width,
110
+ height: texture.height,
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Creates textures from a spritesheet image by cutting it into frames
116
+ * Automatically detects image dimensions if width/height are not provided
117
+ *
118
+ * @param options - Texture options containing image path, dimensions, and frame configuration
119
+ * @returns A 2D array of textures organized by rows and columns
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * // With explicit dimensions
124
+ * const textures = await sprite.createTextures({
125
+ * image: 'path/to/image.png',
126
+ * width: 256,
127
+ * height: 128,
128
+ * framesWidth: 4,
129
+ * framesHeight: 2,
130
+ * spriteWidth: 64,
131
+ * spriteHeight: 64
132
+ * });
133
+ *
134
+ * // Without dimensions (automatically detected)
135
+ * const textures = await sprite.createTextures({
136
+ * image: 'path/to/image.png',
137
+ * framesWidth: 4,
138
+ * framesHeight: 2,
139
+ * spriteWidth: 64,
140
+ * spriteHeight: 64
141
+ * });
142
+ * ```
143
+ */
89
144
  private async createTextures(
90
145
  options: Required<TextureOptionsMerging>
91
146
  ): Promise<Texture[][]> {
92
- const { width, height, framesHeight, framesWidth, image, offset } = options;
147
+ let { width, height, framesHeight, framesWidth, image, offset } = options;
93
148
 
94
149
  if (!image || typeof image !== 'string' || image.trim() === '') {
95
150
  console.warn('Invalid image path provided to createTextures:', image);
@@ -97,6 +152,17 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
97
152
  }
98
153
 
99
154
  const texture = await Assets.load(image);
155
+
156
+ // Auto-detect width and height from the image if not provided
157
+ if (!width || width <= 0) {
158
+ width = texture.width;
159
+ options.width = width;
160
+ }
161
+ if (!height || height <= 0) {
162
+ height = texture.height;
163
+ options.height = height;
164
+ }
165
+
100
166
  const spriteWidth = options.spriteWidth;
101
167
  const spriteHeight = options.spriteHeight;
102
168
  const frames: Texture[][] = [];
@@ -155,12 +221,30 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
155
221
  } as any;
156
222
  const {
157
223
  rectWidth,
158
- width = 0,
224
+ width: widthOption = 0,
159
225
  framesWidth = 1,
160
226
  rectHeight,
161
- height = 0,
227
+ height: heightOption = 0,
162
228
  framesHeight = 1,
229
+ image,
163
230
  } = optionsTextures;
231
+
232
+ // Auto-detect width and height from the image if not provided
233
+ let width = widthOption;
234
+ let height = heightOption;
235
+
236
+ if (image && ((!width || width <= 0) || (!height || height <= 0))) {
237
+ const dimensions = await this.detectImageDimensions(image);
238
+ if (!width || width <= 0) {
239
+ width = dimensions.width;
240
+ optionsTextures.width = width;
241
+ }
242
+ if (!height || height <= 0) {
243
+ height = dimensions.height;
244
+ optionsTextures.height = height;
245
+ }
246
+ }
247
+
164
248
  optionsTextures.spriteWidth = rectWidth ? rectWidth : width / framesWidth;
165
249
  optionsTextures.spriteHeight = rectHeight
166
250
  ? rectHeight
@@ -2,6 +2,8 @@ import * as PIXI from "pixi.js";
2
2
  import { SignalOrPrimitive } from ".";
3
3
  import { DragProps } from "../../directives/Drag";
4
4
  import { ViewportFollowProps } from "../../directives/ViewportFollow";
5
+ import { ShakeProps } from "../../directives/Shake";
6
+ import { FlashProps } from "../../directives/Flash";
5
7
 
6
8
  export type FlexDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse';
7
9
  export type JustifyContent = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
@@ -64,6 +66,8 @@ export interface DisplayObjectProps {
64
66
  // Directives
65
67
  drag?: DragProps;
66
68
  viewportFollow?: ViewportFollowProps;
69
+ shake?: ShakeProps;
70
+ flash?: FlashProps;
67
71
 
68
72
  // Events
69
73
  click?: PIXI.FederatedEventHandler;
@@ -0,0 +1,182 @@
1
+ import { Directive, registerDirective } from "../engine/directive";
2
+ import { Element } from "../engine/reactive";
3
+ import { ControlsBase, Controls } from "./ControlsBase";
4
+ import { KeyboardControls } from "./KeyboardControls";
5
+ import { GamepadControls, GamepadConfig } from "./GamepadControls";
6
+
7
+ /**
8
+ * Controls directive that coordinates keyboard and gamepad input systems
9
+ *
10
+ * This directive automatically activates both keyboard and gamepad controls when available.
11
+ * The gamepad is automatically enabled if joypad.js is detected in the environment.
12
+ *
13
+ * Both systems share the same control configuration and can work simultaneously.
14
+ *
15
+ * @example
16
+ * ```html
17
+ * <Sprite
18
+ * image="path/to/image.png"
19
+ * controls={controlsConfig}
20
+ * x={x}
21
+ * y={y}
22
+ * />
23
+ * ```
24
+ */
25
+ export class ControlsDirective extends Directive {
26
+ private keyboardControls: KeyboardControls | null = null;
27
+ private gamepadControls: GamepadControls | null = null;
28
+
29
+ /**
30
+ * Initialize the controls directive
31
+ * Sets up keyboard and gamepad controls if available
32
+ */
33
+ onInit(element: Element) {
34
+ const value = element.props.controls?.value ?? element.props.controls;
35
+ if (!value) return;
36
+
37
+ // Initialize keyboard controls (always available)
38
+ this.keyboardControls = new KeyboardControls();
39
+ this.keyboardControls.setInputs(value as Controls);
40
+ this.keyboardControls.start();
41
+
42
+ // Initialize gamepad controls if gamepad config is present
43
+ // GamepadControls will handle joypad.js availability internally
44
+ const gamepadConfig = (value as Controls & { gamepad?: GamepadConfig }).gamepad;
45
+ if (gamepadConfig !== undefined && gamepadConfig.enabled !== false) {
46
+ this.gamepadControls = new GamepadControls();
47
+ this.gamepadControls.setInputs(value as Controls & { gamepad?: GamepadConfig });
48
+ this.gamepadControls.start();
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Mount hook (no specific action needed)
54
+ */
55
+ onMount(element: Element) {}
56
+
57
+ /**
58
+ * Update controls configuration
59
+ * Updates both keyboard and gamepad controls
60
+ */
61
+ onUpdate(props: any) {
62
+ const value = props.controls?.value ?? props.controls;
63
+ if (!value) return;
64
+
65
+ if (this.keyboardControls) {
66
+ this.keyboardControls.setInputs(value as Controls);
67
+ }
68
+
69
+ if (this.gamepadControls) {
70
+ this.gamepadControls.setInputs(value as Controls & { gamepad?: GamepadConfig });
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Cleanup and destroy all control systems
76
+ */
77
+ onDestroy(element: Element) {
78
+ if (this.keyboardControls) {
79
+ this.keyboardControls.destroy();
80
+ this.keyboardControls = null;
81
+ }
82
+
83
+ if (this.gamepadControls) {
84
+ this.gamepadControls.destroy();
85
+ this.gamepadControls = null;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Get a control by input name
91
+ * Delegates to keyboard controls (primary system)
92
+ *
93
+ * @param inputName - Name of the input/key
94
+ * @returns BoundKey if found, undefined otherwise
95
+ */
96
+ getControl(inputName: string) {
97
+ return this.keyboardControls?.getControl(inputName);
98
+ }
99
+
100
+ /**
101
+ * Get all bound controls
102
+ * Delegates to keyboard controls (primary system)
103
+ *
104
+ * @returns Object mapping input names to BoundKey objects
105
+ */
106
+ getControls() {
107
+ return this.keyboardControls?.getControls() || {};
108
+ }
109
+
110
+ /**
111
+ * Apply a control action programmatically
112
+ * Applies to both keyboard and gamepad if available
113
+ *
114
+ * @param controlName - Name of the control
115
+ * @param isDown - Whether the control is pressed (true) or released (false)
116
+ * @returns Promise that resolves when the action is complete
117
+ */
118
+ async applyControl(controlName: string | number, isDown?: boolean): Promise<void> {
119
+ if (this.keyboardControls) {
120
+ await this.keyboardControls.applyControl(controlName, isDown);
121
+ }
122
+ if (this.gamepadControls) {
123
+ await this.gamepadControls.applyControl(controlName, isDown);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Stop listening to inputs
129
+ * Stops both keyboard and gamepad input processing
130
+ */
131
+ stopInputs() {
132
+ if (this.keyboardControls) {
133
+ this.keyboardControls.stopInputs();
134
+ }
135
+ if (this.gamepadControls) {
136
+ this.gamepadControls.stopInputs();
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Resume listening to inputs
142
+ * Resumes both keyboard and gamepad input processing
143
+ */
144
+ listenInputs() {
145
+ if (this.keyboardControls) {
146
+ this.keyboardControls.listenInputs();
147
+ }
148
+ if (this.gamepadControls) {
149
+ this.gamepadControls.listenInputs();
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Get the current controls configuration
155
+ * Returns keyboard controls options (both systems share the same config)
156
+ *
157
+ * @returns The controls options object
158
+ */
159
+ get options(): Controls {
160
+ return this.keyboardControls?.options || {};
161
+ }
162
+
163
+ /**
164
+ * Get the keyboard controls instance
165
+ *
166
+ * @returns KeyboardControls instance or null
167
+ */
168
+ get keyboard(): KeyboardControls | null {
169
+ return this.keyboardControls;
170
+ }
171
+
172
+ /**
173
+ * Get the gamepad controls instance
174
+ *
175
+ * @returns GamepadControls instance or null
176
+ */
177
+ get gamepad(): GamepadControls | null {
178
+ return this.gamepadControls;
179
+ }
180
+ }
181
+
182
+ registerDirective('controls', ControlsDirective);