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.
- package/dist/{DebugRenderer-BYa_lwD-.js → DebugRenderer-DDfZuvTR.js} +2 -2
- package/dist/{DebugRenderer-BYa_lwD-.js.map → DebugRenderer-DDfZuvTR.js.map} +1 -1
- package/dist/components/Container.d.ts +4 -0
- package/dist/components/Container.d.ts.map +1 -1
- package/dist/components/DOMContainer.d.ts +4 -0
- package/dist/components/DOMContainer.d.ts.map +1 -1
- package/dist/components/DisplayObject.d.ts +4 -0
- package/dist/components/DisplayObject.d.ts.map +1 -1
- package/dist/components/Mesh.d.ts +4 -0
- package/dist/components/Mesh.d.ts.map +1 -1
- package/dist/components/Sprite.d.ts +48 -0
- package/dist/components/Sprite.d.ts.map +1 -1
- package/dist/components/Viewport.d.ts +4 -0
- package/dist/components/Viewport.d.ts.map +1 -1
- package/dist/components/types/DisplayObject.d.ts +4 -0
- package/dist/components/types/DisplayObject.d.ts.map +1 -1
- package/dist/directives/Controls.d.ts +102 -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/Flash.d.ts +117 -0
- package/dist/directives/Flash.d.ts.map +1 -0
- package/dist/directives/GamepadControls.d.ts +223 -0
- package/dist/directives/GamepadControls.d.ts.map +1 -0
- package/dist/directives/KeyboardControls.d.ts +55 -366
- package/dist/directives/KeyboardControls.d.ts.map +1 -1
- package/dist/directives/Shake.d.ts +98 -0
- package/dist/directives/Shake.d.ts.map +1 -0
- package/dist/directives/index.d.ts +11 -1
- package/dist/directives/index.d.ts.map +1 -1
- package/dist/engine/trigger.d.ts +2 -3
- package/dist/engine/trigger.d.ts.map +1 -1
- package/dist/engine/utils.d.ts.map +1 -1
- package/dist/{index-BLbc2zG5.js → index--faZajmD.js} +4547 -3970
- package/dist/index--faZajmD.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.global.js +6 -6
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +70 -57
- package/package.json +2 -1
- package/src/components/Container.ts +17 -0
- package/src/components/DisplayObject.ts +45 -6
- package/src/components/Sprite.ts +87 -3
- package/src/components/types/DisplayObject.ts +4 -0
- package/src/directives/Controls.ts +182 -0
- package/src/directives/ControlsBase.ts +266 -0
- package/src/directives/Flash.ts +409 -0
- package/src/directives/GamepadControls.ts +515 -0
- package/src/directives/KeyboardControls.ts +66 -426
- package/src/directives/Shake.ts +282 -0
- package/src/directives/index.ts +11 -6
- package/src/engine/trigger.ts +2 -2
- package/src/engine/utils.ts +4 -0
- package/src/index.ts +1 -1
- package/dist/index-BLbc2zG5.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,64 +1,77 @@
|
|
|
1
|
-
import { h as a } from "./index
|
|
2
|
-
import { A as r,
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
o as Button,
|
|
7
|
+
n as ButtonState,
|
|
8
|
+
l as Canvas,
|
|
9
|
+
c as Circle,
|
|
10
10
|
p as Container,
|
|
11
|
-
S as
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
g as
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
C as
|
|
18
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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.
|
|
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
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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
|
package/src/components/Sprite.ts
CHANGED
|
@@ -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
|
-
|
|
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);
|