cubeforge 0.1.5 → 0.1.7
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/index.d.mts +150 -4
- package/dist/index.js +330 -71
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -6,7 +6,7 @@ export { Component, ECSWorld, Ease, EntityId, Plugin, ScriptUpdateFn, TransformC
|
|
|
6
6
|
import { InputManager, ActionBindings } from '@cubeforge/input';
|
|
7
7
|
export { ActionBindings, InputManager, InputMap, createInputMap } from '@cubeforge/input';
|
|
8
8
|
import { Canvas2DRenderer, RenderSystem } from '@cubeforge/renderer';
|
|
9
|
-
export { AnimationStateComponent, ParallaxLayerComponent, Particle, ParticlePoolComponent, SpriteComponent, SquashStretchComponent, createSprite } from '@cubeforge/renderer';
|
|
9
|
+
export { AnimationStateComponent, ParallaxLayerComponent, Particle, ParticlePoolComponent, SpriteComponent, SquashStretchComponent, TextComponent, createSprite } from '@cubeforge/renderer';
|
|
10
10
|
import { PhysicsSystem } from '@cubeforge/physics';
|
|
11
11
|
export { BoxColliderComponent, CircleColliderComponent, RaycastHit, RigidBodyComponent, overlapBox, raycast } from '@cubeforge/physics';
|
|
12
12
|
|
|
@@ -114,8 +114,25 @@ interface SpriteProps {
|
|
|
114
114
|
frameColumns?: number;
|
|
115
115
|
atlas?: SpriteAtlas;
|
|
116
116
|
frame?: string;
|
|
117
|
+
tileX?: boolean;
|
|
118
|
+
tileY?: boolean;
|
|
117
119
|
}
|
|
118
|
-
declare function Sprite({ width, height, color, src, offsetX, offsetY, zIndex, visible, flipX, anchorX, anchorY, frameIndex, frameWidth, frameHeight, frameColumns, atlas, frame, }: SpriteProps): null;
|
|
120
|
+
declare function Sprite({ width, height, color, src, offsetX, offsetY, zIndex, visible, flipX, anchorX, anchorY, frameIndex, frameWidth, frameHeight, frameColumns, atlas, frame, tileX, tileY, }: SpriteProps): null;
|
|
121
|
+
|
|
122
|
+
interface TextProps {
|
|
123
|
+
text: string;
|
|
124
|
+
fontSize?: number;
|
|
125
|
+
fontFamily?: string;
|
|
126
|
+
color?: string;
|
|
127
|
+
align?: CanvasTextAlign;
|
|
128
|
+
baseline?: CanvasTextBaseline;
|
|
129
|
+
zIndex?: number;
|
|
130
|
+
visible?: boolean;
|
|
131
|
+
maxWidth?: number;
|
|
132
|
+
offsetX?: number;
|
|
133
|
+
offsetY?: number;
|
|
134
|
+
}
|
|
135
|
+
declare function Text({ text, fontSize, fontFamily, color, align, baseline, zIndex, visible, maxWidth, offsetX, offsetY, }: TextProps): null;
|
|
119
136
|
|
|
120
137
|
interface RigidBodyProps {
|
|
121
138
|
mass?: number;
|
|
@@ -512,9 +529,28 @@ interface PlatformerControllerOptions {
|
|
|
512
529
|
coyoteTime?: number;
|
|
513
530
|
/** Seconds to buffer a jump input before landing (default 0.08) */
|
|
514
531
|
jumpBuffer?: number;
|
|
532
|
+
/**
|
|
533
|
+
* Minimum seconds between jumps — prevents multi-jump spam when holding the
|
|
534
|
+
* jump key with double-jump enabled (default 0.18)
|
|
535
|
+
*/
|
|
536
|
+
jumpCooldown?: number;
|
|
537
|
+
/**
|
|
538
|
+
* Override the default key bindings. Each action accepts a single key code
|
|
539
|
+
* or an array of key codes.
|
|
540
|
+
*
|
|
541
|
+
* Defaults:
|
|
542
|
+
* left: ['ArrowLeft', 'KeyA', 'a']
|
|
543
|
+
* right: ['ArrowRight', 'KeyD', 'd']
|
|
544
|
+
* jump: ['Space', 'ArrowUp', 'KeyW', 'w']
|
|
545
|
+
*/
|
|
546
|
+
bindings?: {
|
|
547
|
+
left?: string | string[];
|
|
548
|
+
right?: string | string[];
|
|
549
|
+
jump?: string | string[];
|
|
550
|
+
};
|
|
515
551
|
}
|
|
516
552
|
/**
|
|
517
|
-
* Attaches platformer movement (
|
|
553
|
+
* Attaches platformer movement (customisable keys + Space/Up to jump) to an entity.
|
|
518
554
|
* The entity must already have a RigidBody component.
|
|
519
555
|
*
|
|
520
556
|
* @example
|
|
@@ -555,6 +591,116 @@ interface TopDownMovementOptions {
|
|
|
555
591
|
*/
|
|
556
592
|
declare function useTopDownMovement(entityId: EntityId, opts?: TopDownMovementOptions): void;
|
|
557
593
|
|
|
594
|
+
interface SoundControls {
|
|
595
|
+
/** Start playing. If already playing and loop=false, restarts from the beginning. */
|
|
596
|
+
play(): void;
|
|
597
|
+
/** Stop the current playback. */
|
|
598
|
+
stop(): void;
|
|
599
|
+
/** Change the volume (0–1). */
|
|
600
|
+
setVolume(v: number): void;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Loads and plays an audio file via the Web Audio API.
|
|
604
|
+
*
|
|
605
|
+
* The AudioContext is created lazily — the first call to `play()` resumes it
|
|
606
|
+
* if the browser suspended it before a user gesture.
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* function JumpSfx() {
|
|
610
|
+
* const { play } = useSound('/jump.wav')
|
|
611
|
+
* // call play() on jump event
|
|
612
|
+
* }
|
|
613
|
+
*/
|
|
614
|
+
declare function useSound(src: string, opts?: {
|
|
615
|
+
volume?: number;
|
|
616
|
+
loop?: boolean;
|
|
617
|
+
}): SoundControls;
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* A lightweight game-loop timer for use inside Script update functions.
|
|
621
|
+
*
|
|
622
|
+
* @example
|
|
623
|
+
* // Create once outside the update function (e.g. in playerInit):
|
|
624
|
+
* const invincibleTimer = createTimer(2.0, () => { state.isInvincible = false })
|
|
625
|
+
*
|
|
626
|
+
* // Call update(dt) inside the Script update function each frame:
|
|
627
|
+
* function playerUpdate(id, world, input, dt) {
|
|
628
|
+
* invincibleTimer.update(dt)
|
|
629
|
+
* if (someHitCondition) {
|
|
630
|
+
* state.isInvincible = true
|
|
631
|
+
* invincibleTimer.restart()
|
|
632
|
+
* }
|
|
633
|
+
* }
|
|
634
|
+
*/
|
|
635
|
+
interface GameTimer {
|
|
636
|
+
/** Advance the timer by dt seconds. Calls onComplete when it reaches zero. */
|
|
637
|
+
update(dt: number): void;
|
|
638
|
+
/** Start (or resume) counting down. */
|
|
639
|
+
start(): void;
|
|
640
|
+
/** Pause counting without resetting. */
|
|
641
|
+
stop(): void;
|
|
642
|
+
/** Reset elapsed time to 0 and stop. Optionally change the duration. */
|
|
643
|
+
reset(newDuration?: number): void;
|
|
644
|
+
/** Reset elapsed time to 0 and immediately start. */
|
|
645
|
+
restart(): void;
|
|
646
|
+
/** Whether the timer is currently counting. */
|
|
647
|
+
readonly running: boolean;
|
|
648
|
+
/** Elapsed seconds since last reset/restart. */
|
|
649
|
+
readonly elapsed: number;
|
|
650
|
+
/** Remaining seconds (clamped to 0). */
|
|
651
|
+
readonly remaining: number;
|
|
652
|
+
/** Progress from 0 (just started) to 1 (complete). */
|
|
653
|
+
readonly progress: number;
|
|
654
|
+
}
|
|
655
|
+
declare function createTimer(duration: number, onComplete?: () => void, autoStart?: boolean): GameTimer;
|
|
656
|
+
|
|
657
|
+
interface GamepadState {
|
|
658
|
+
/** Whether a gamepad is connected at this player index. */
|
|
659
|
+
connected: boolean;
|
|
660
|
+
/**
|
|
661
|
+
* Normalised axis values (typically −1 to +1).
|
|
662
|
+
* Index 0 = left stick X, 1 = left stick Y, 2 = right stick X, 3 = right stick Y
|
|
663
|
+
* (varies by browser / controller).
|
|
664
|
+
*/
|
|
665
|
+
axes: readonly number[];
|
|
666
|
+
/**
|
|
667
|
+
* Button pressed states.
|
|
668
|
+
* Standard mapping: 0=A/Cross, 1=B/Circle, 2=X/Square, 3=Y/Triangle,
|
|
669
|
+
* 4=LB, 5=RB, 12=DPad-Up, 13=DPad-Down, 14=DPad-Left, 15=DPad-Right
|
|
670
|
+
*/
|
|
671
|
+
buttons: readonly boolean[];
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Polls the Gamepad API every frame and returns the current state.
|
|
675
|
+
*
|
|
676
|
+
* @param playerIndex - Which gamepad slot to read (0–3). Default 0.
|
|
677
|
+
*
|
|
678
|
+
* @example
|
|
679
|
+
* function MoveWithGamepad() {
|
|
680
|
+
* const gp = useGamepad()
|
|
681
|
+
* // gp.axes[0] = left stick horizontal
|
|
682
|
+
* // gp.buttons[0] = A button
|
|
683
|
+
* }
|
|
684
|
+
*/
|
|
685
|
+
declare function useGamepad(playerIndex?: number): GamepadState;
|
|
686
|
+
|
|
687
|
+
interface PauseControls {
|
|
688
|
+
paused: boolean;
|
|
689
|
+
pause(): void;
|
|
690
|
+
resume(): void;
|
|
691
|
+
toggle(): void;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Controls the game loop pause state from inside a `<Game>` tree.
|
|
695
|
+
*
|
|
696
|
+
* @example
|
|
697
|
+
* function PauseButton() {
|
|
698
|
+
* const { paused, toggle } = usePause()
|
|
699
|
+
* return <button onClick={toggle}>{paused ? 'Resume' : 'Pause'}</button>
|
|
700
|
+
* }
|
|
701
|
+
*/
|
|
702
|
+
declare function usePause(): PauseControls;
|
|
703
|
+
|
|
558
704
|
interface ContactOpts {
|
|
559
705
|
/** Only fire if the other entity has this tag */
|
|
560
706
|
tag?: string;
|
|
@@ -616,4 +762,4 @@ interface DevToolsHandle {
|
|
|
616
762
|
onFrame?: () => void;
|
|
617
763
|
}
|
|
618
764
|
|
|
619
|
-
export { Animation, type BoundInputMap, BoxCollider, Camera2D, type CameraControls, Checkpoint, CircleCollider, type DevToolsHandle, type EngineState, Entity, Game, type GameControls, MovingPlatform, ParallaxLayer, ParticleEmitter, type ParticlePreset, type PlatformerControllerOptions, RigidBody, ScreenFlash, type ScreenFlashHandle, Script, type SnapshotControls, Sprite, type SpriteAtlas, SquashStretch, type TiledLayer, type TiledObject, Tilemap, type TopDownMovementOptions, Transform, World, createAtlas, useCamera, useCircleEnter, useCircleExit, useCollisionEnter, useCollisionExit, useEntity, useEvent, useEvents, useGame, useInput, useInputMap, usePlatformerController, useSnapshot, useTopDownMovement, useTriggerEnter, useTriggerExit };
|
|
765
|
+
export { Animation, type BoundInputMap, BoxCollider, Camera2D, type CameraControls, Checkpoint, CircleCollider, type DevToolsHandle, type EngineState, Entity, Game, type GameControls, type GameTimer, type GamepadState, MovingPlatform, ParallaxLayer, ParticleEmitter, type ParticlePreset, type PauseControls, type PlatformerControllerOptions, RigidBody, ScreenFlash, type ScreenFlashHandle, Script, type SnapshotControls, type SoundControls, Sprite, type SpriteAtlas, SquashStretch, Text, type TiledLayer, type TiledObject, Tilemap, type TopDownMovementOptions, Transform, World, createAtlas, createTimer, useCamera, useCircleEnter, useCircleExit, useCollisionEnter, useCollisionExit, useEntity, useEvent, useEvents, useGame, useGamepad, useInput, useInputMap, usePause, usePlatformerController, useSnapshot, useSound, useTopDownMovement, useTriggerEnter, useTriggerExit };
|
package/dist/index.js
CHANGED
|
@@ -961,6 +961,14 @@ var RenderSystem = class {
|
|
|
961
961
|
} else if (sprite.frame) {
|
|
962
962
|
const { sx, sy, sw, sh } = sprite.frame;
|
|
963
963
|
ctx.drawImage(sprite.image, sx, sy, sw, sh, drawX, drawY, sprite.width, sprite.height);
|
|
964
|
+
} else if (sprite.tileX || sprite.tileY) {
|
|
965
|
+
const repeat = sprite.tileX && sprite.tileY ? "repeat" : sprite.tileX ? "repeat-x" : "repeat-y";
|
|
966
|
+
const pat = ctx.createPattern(sprite.image, repeat);
|
|
967
|
+
if (pat) {
|
|
968
|
+
pat.setTransform(new DOMMatrix().translate(drawX, drawY));
|
|
969
|
+
ctx.fillStyle = pat;
|
|
970
|
+
ctx.fillRect(drawX, drawY, sprite.width, sprite.height);
|
|
971
|
+
}
|
|
964
972
|
} else {
|
|
965
973
|
ctx.drawImage(sprite.image, drawX, drawY, sprite.width, sprite.height);
|
|
966
974
|
}
|
|
@@ -970,6 +978,26 @@ var RenderSystem = class {
|
|
|
970
978
|
}
|
|
971
979
|
ctx.restore();
|
|
972
980
|
}
|
|
981
|
+
const textEntities = world.query("Transform", "Text");
|
|
982
|
+
textEntities.sort((a, b) => {
|
|
983
|
+
const ta = world.getComponent(a, "Text");
|
|
984
|
+
const tb = world.getComponent(b, "Text");
|
|
985
|
+
return ta.zIndex - tb.zIndex;
|
|
986
|
+
});
|
|
987
|
+
for (const id of textEntities) {
|
|
988
|
+
const transform = world.getComponent(id, "Transform");
|
|
989
|
+
const text = world.getComponent(id, "Text");
|
|
990
|
+
if (!text.visible) continue;
|
|
991
|
+
ctx.save();
|
|
992
|
+
ctx.translate(transform.x + text.offsetX, transform.y + text.offsetY);
|
|
993
|
+
ctx.rotate(transform.rotation);
|
|
994
|
+
ctx.font = `${text.fontSize}px ${text.fontFamily}`;
|
|
995
|
+
ctx.fillStyle = text.color;
|
|
996
|
+
ctx.textAlign = text.align;
|
|
997
|
+
ctx.textBaseline = text.baseline;
|
|
998
|
+
ctx.fillText(text.text, 0, 0, text.maxWidth);
|
|
999
|
+
ctx.restore();
|
|
1000
|
+
}
|
|
973
1001
|
for (const id of world.query("Transform", "ParticlePool")) {
|
|
974
1002
|
const t = world.getComponent(id, "Transform");
|
|
975
1003
|
const pool = world.getComponent(id, "ParticlePool");
|
|
@@ -2201,7 +2229,9 @@ function Sprite({
|
|
|
2201
2229
|
frameHeight,
|
|
2202
2230
|
frameColumns,
|
|
2203
2231
|
atlas,
|
|
2204
|
-
frame
|
|
2232
|
+
frame,
|
|
2233
|
+
tileX,
|
|
2234
|
+
tileY
|
|
2205
2235
|
}) {
|
|
2206
2236
|
const resolvedFrameIndex = atlas && frame != null ? atlas[frame] ?? 0 : frameIndex;
|
|
2207
2237
|
const engine = useContext4(EngineContext);
|
|
@@ -2222,7 +2252,9 @@ function Sprite({
|
|
|
2222
2252
|
frameIndex: resolvedFrameIndex,
|
|
2223
2253
|
frameWidth,
|
|
2224
2254
|
frameHeight,
|
|
2225
|
-
frameColumns
|
|
2255
|
+
frameColumns,
|
|
2256
|
+
tileX,
|
|
2257
|
+
tileY
|
|
2226
2258
|
});
|
|
2227
2259
|
engine.ecs.addComponent(entityId, comp);
|
|
2228
2260
|
if (src) {
|
|
@@ -2248,8 +2280,54 @@ function Sprite({
|
|
|
2248
2280
|
return null;
|
|
2249
2281
|
}
|
|
2250
2282
|
|
|
2251
|
-
// src/components/
|
|
2283
|
+
// src/components/Text.tsx
|
|
2252
2284
|
import { useEffect as useEffect7, useContext as useContext5 } from "react";
|
|
2285
|
+
function Text({
|
|
2286
|
+
text,
|
|
2287
|
+
fontSize = 16,
|
|
2288
|
+
fontFamily = "monospace",
|
|
2289
|
+
color = "#ffffff",
|
|
2290
|
+
align = "center",
|
|
2291
|
+
baseline = "middle",
|
|
2292
|
+
zIndex = 10,
|
|
2293
|
+
visible = true,
|
|
2294
|
+
maxWidth,
|
|
2295
|
+
offsetX = 0,
|
|
2296
|
+
offsetY = 0
|
|
2297
|
+
}) {
|
|
2298
|
+
const engine = useContext5(EngineContext);
|
|
2299
|
+
const entityId = useContext5(EntityContext);
|
|
2300
|
+
useEffect7(() => {
|
|
2301
|
+
const comp = {
|
|
2302
|
+
type: "Text",
|
|
2303
|
+
text,
|
|
2304
|
+
fontSize,
|
|
2305
|
+
fontFamily,
|
|
2306
|
+
color,
|
|
2307
|
+
align,
|
|
2308
|
+
baseline,
|
|
2309
|
+
zIndex,
|
|
2310
|
+
visible,
|
|
2311
|
+
maxWidth,
|
|
2312
|
+
offsetX,
|
|
2313
|
+
offsetY
|
|
2314
|
+
};
|
|
2315
|
+
engine.ecs.addComponent(entityId, comp);
|
|
2316
|
+
return () => engine.ecs.removeComponent(entityId, "Text");
|
|
2317
|
+
}, []);
|
|
2318
|
+
useEffect7(() => {
|
|
2319
|
+
const comp = engine.ecs.getComponent(entityId, "Text");
|
|
2320
|
+
if (!comp) return;
|
|
2321
|
+
comp.text = text;
|
|
2322
|
+
comp.color = color;
|
|
2323
|
+
comp.visible = visible;
|
|
2324
|
+
comp.zIndex = zIndex;
|
|
2325
|
+
}, [text, color, visible, zIndex, engine, entityId]);
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
// src/components/RigidBody.tsx
|
|
2330
|
+
import { useEffect as useEffect8, useContext as useContext6 } from "react";
|
|
2253
2331
|
function RigidBody({
|
|
2254
2332
|
mass = 1,
|
|
2255
2333
|
gravityScale = 1,
|
|
@@ -2261,9 +2339,9 @@ function RigidBody({
|
|
|
2261
2339
|
lockX = false,
|
|
2262
2340
|
lockY = false
|
|
2263
2341
|
}) {
|
|
2264
|
-
const engine =
|
|
2265
|
-
const entityId =
|
|
2266
|
-
|
|
2342
|
+
const engine = useContext6(EngineContext);
|
|
2343
|
+
const entityId = useContext6(EntityContext);
|
|
2344
|
+
useEffect8(() => {
|
|
2267
2345
|
engine.ecs.addComponent(entityId, createRigidBody({ mass, gravityScale, isStatic, bounce, friction, vx, vy, lockX, lockY }));
|
|
2268
2346
|
return () => engine.ecs.removeComponent(entityId, "RigidBody");
|
|
2269
2347
|
}, []);
|
|
@@ -2271,7 +2349,7 @@ function RigidBody({
|
|
|
2271
2349
|
}
|
|
2272
2350
|
|
|
2273
2351
|
// src/components/BoxCollider.tsx
|
|
2274
|
-
import { useEffect as
|
|
2352
|
+
import { useEffect as useEffect9, useContext as useContext7 } from "react";
|
|
2275
2353
|
function BoxCollider({
|
|
2276
2354
|
width,
|
|
2277
2355
|
height,
|
|
@@ -2282,9 +2360,9 @@ function BoxCollider({
|
|
|
2282
2360
|
mask = "*",
|
|
2283
2361
|
oneWay = false
|
|
2284
2362
|
}) {
|
|
2285
|
-
const engine =
|
|
2286
|
-
const entityId =
|
|
2287
|
-
|
|
2363
|
+
const engine = useContext7(EngineContext);
|
|
2364
|
+
const entityId = useContext7(EntityContext);
|
|
2365
|
+
useEffect9(() => {
|
|
2288
2366
|
engine.ecs.addComponent(entityId, createBoxCollider(width, height, { offsetX, offsetY, isTrigger, layer, mask, oneWay }));
|
|
2289
2367
|
const checkId = setTimeout(() => {
|
|
2290
2368
|
if (engine.ecs.hasEntity(entityId) && !engine.ecs.hasComponent(entityId, "Transform")) {
|
|
@@ -2300,7 +2378,7 @@ function BoxCollider({
|
|
|
2300
2378
|
}
|
|
2301
2379
|
|
|
2302
2380
|
// src/components/CircleCollider.tsx
|
|
2303
|
-
import { useEffect as
|
|
2381
|
+
import { useEffect as useEffect10, useContext as useContext8 } from "react";
|
|
2304
2382
|
function CircleCollider({
|
|
2305
2383
|
radius,
|
|
2306
2384
|
offsetX = 0,
|
|
@@ -2309,9 +2387,9 @@ function CircleCollider({
|
|
|
2309
2387
|
layer = "default",
|
|
2310
2388
|
mask = "*"
|
|
2311
2389
|
}) {
|
|
2312
|
-
const engine =
|
|
2313
|
-
const entityId =
|
|
2314
|
-
|
|
2390
|
+
const engine = useContext8(EngineContext);
|
|
2391
|
+
const entityId = useContext8(EntityContext);
|
|
2392
|
+
useEffect10(() => {
|
|
2315
2393
|
engine.ecs.addComponent(entityId, createCircleCollider(radius, { offsetX, offsetY, isTrigger, layer, mask }));
|
|
2316
2394
|
return () => engine.ecs.removeComponent(entityId, "CircleCollider");
|
|
2317
2395
|
}, []);
|
|
@@ -2319,11 +2397,11 @@ function CircleCollider({
|
|
|
2319
2397
|
}
|
|
2320
2398
|
|
|
2321
2399
|
// src/components/Script.tsx
|
|
2322
|
-
import { useEffect as
|
|
2400
|
+
import { useEffect as useEffect11, useContext as useContext9 } from "react";
|
|
2323
2401
|
function Script({ init, update }) {
|
|
2324
|
-
const engine =
|
|
2325
|
-
const entityId =
|
|
2326
|
-
|
|
2402
|
+
const engine = useContext9(EngineContext);
|
|
2403
|
+
const entityId = useContext9(EntityContext);
|
|
2404
|
+
useEffect11(() => {
|
|
2327
2405
|
if (init) {
|
|
2328
2406
|
try {
|
|
2329
2407
|
init(entityId, engine.ecs);
|
|
@@ -2338,7 +2416,7 @@ function Script({ init, update }) {
|
|
|
2338
2416
|
}
|
|
2339
2417
|
|
|
2340
2418
|
// src/components/Camera2D.tsx
|
|
2341
|
-
import { useEffect as
|
|
2419
|
+
import { useEffect as useEffect12, useContext as useContext10 } from "react";
|
|
2342
2420
|
function Camera2D({
|
|
2343
2421
|
followEntity,
|
|
2344
2422
|
x = 0,
|
|
@@ -2351,8 +2429,8 @@ function Camera2D({
|
|
|
2351
2429
|
followOffsetX = 0,
|
|
2352
2430
|
followOffsetY = 0
|
|
2353
2431
|
}) {
|
|
2354
|
-
const engine =
|
|
2355
|
-
|
|
2432
|
+
const engine = useContext10(EngineContext);
|
|
2433
|
+
useEffect12(() => {
|
|
2356
2434
|
const entityId = engine.ecs.createEntity();
|
|
2357
2435
|
engine.ecs.addComponent(entityId, createCamera2D({
|
|
2358
2436
|
followEntityId: followEntity,
|
|
@@ -2368,7 +2446,7 @@ function Camera2D({
|
|
|
2368
2446
|
}));
|
|
2369
2447
|
return () => engine.ecs.destroyEntity(entityId);
|
|
2370
2448
|
}, []);
|
|
2371
|
-
|
|
2449
|
+
useEffect12(() => {
|
|
2372
2450
|
const camId = engine.ecs.queryOne("Camera2D");
|
|
2373
2451
|
if (camId === void 0) return;
|
|
2374
2452
|
const cam = engine.ecs.getComponent(camId, "Camera2D");
|
|
@@ -2385,11 +2463,11 @@ function Camera2D({
|
|
|
2385
2463
|
}
|
|
2386
2464
|
|
|
2387
2465
|
// src/components/Animation.tsx
|
|
2388
|
-
import { useEffect as
|
|
2466
|
+
import { useEffect as useEffect13, useContext as useContext11 } from "react";
|
|
2389
2467
|
function Animation({ frames, fps = 12, loop = true, playing = true, onComplete }) {
|
|
2390
|
-
const engine =
|
|
2391
|
-
const entityId =
|
|
2392
|
-
|
|
2468
|
+
const engine = useContext11(EngineContext);
|
|
2469
|
+
const entityId = useContext11(EntityContext);
|
|
2470
|
+
useEffect13(() => {
|
|
2393
2471
|
const state = {
|
|
2394
2472
|
type: "AnimationState",
|
|
2395
2473
|
frames,
|
|
@@ -2406,7 +2484,7 @@ function Animation({ frames, fps = 12, loop = true, playing = true, onComplete }
|
|
|
2406
2484
|
engine.ecs.removeComponent(entityId, "AnimationState");
|
|
2407
2485
|
};
|
|
2408
2486
|
}, []);
|
|
2409
|
-
|
|
2487
|
+
useEffect13(() => {
|
|
2410
2488
|
const anim = engine.ecs.getComponent(entityId, "AnimationState");
|
|
2411
2489
|
if (!anim) return;
|
|
2412
2490
|
const wasFramesChanged = anim.frames !== frames;
|
|
@@ -2425,11 +2503,11 @@ function Animation({ frames, fps = 12, loop = true, playing = true, onComplete }
|
|
|
2425
2503
|
}
|
|
2426
2504
|
|
|
2427
2505
|
// src/components/SquashStretch.tsx
|
|
2428
|
-
import { useEffect as
|
|
2506
|
+
import { useEffect as useEffect14, useContext as useContext12 } from "react";
|
|
2429
2507
|
function SquashStretch({ intensity = 0.2, recovery = 8 }) {
|
|
2430
|
-
const engine =
|
|
2431
|
-
const entityId =
|
|
2432
|
-
|
|
2508
|
+
const engine = useContext12(EngineContext);
|
|
2509
|
+
const entityId = useContext12(EntityContext);
|
|
2510
|
+
useEffect14(() => {
|
|
2433
2511
|
engine.ecs.addComponent(entityId, {
|
|
2434
2512
|
type: "SquashStretch",
|
|
2435
2513
|
intensity,
|
|
@@ -2443,7 +2521,7 @@ function SquashStretch({ intensity = 0.2, recovery = 8 }) {
|
|
|
2443
2521
|
}
|
|
2444
2522
|
|
|
2445
2523
|
// src/components/ParticleEmitter.tsx
|
|
2446
|
-
import { useEffect as
|
|
2524
|
+
import { useEffect as useEffect15, useContext as useContext13 } from "react";
|
|
2447
2525
|
|
|
2448
2526
|
// src/components/particlePresets.ts
|
|
2449
2527
|
var PARTICLE_PRESETS = {
|
|
@@ -2528,9 +2606,9 @@ function ParticleEmitter({
|
|
|
2528
2606
|
const resolvedColor = color ?? presetConfig.color ?? "#ffffff";
|
|
2529
2607
|
const resolvedGravity = gravity ?? presetConfig.gravity ?? 200;
|
|
2530
2608
|
const resolvedMaxParticles = maxParticles ?? presetConfig.maxParticles ?? 100;
|
|
2531
|
-
const engine =
|
|
2532
|
-
const entityId =
|
|
2533
|
-
|
|
2609
|
+
const engine = useContext13(EngineContext);
|
|
2610
|
+
const entityId = useContext13(EntityContext);
|
|
2611
|
+
useEffect15(() => {
|
|
2534
2612
|
engine.ecs.addComponent(entityId, {
|
|
2535
2613
|
type: "ParticlePool",
|
|
2536
2614
|
particles: [],
|
|
@@ -2548,7 +2626,7 @@ function ParticleEmitter({
|
|
|
2548
2626
|
});
|
|
2549
2627
|
return () => engine.ecs.removeComponent(entityId, "ParticlePool");
|
|
2550
2628
|
}, []);
|
|
2551
|
-
|
|
2629
|
+
useEffect15(() => {
|
|
2552
2630
|
const pool = engine.ecs.getComponent(entityId, "ParticlePool");
|
|
2553
2631
|
if (!pool) return;
|
|
2554
2632
|
pool.active = active;
|
|
@@ -2598,13 +2676,13 @@ function MovingPlatform({
|
|
|
2598
2676
|
import { useState as useState4 } from "react";
|
|
2599
2677
|
|
|
2600
2678
|
// src/hooks/useContact.ts
|
|
2601
|
-
import { useContext as
|
|
2679
|
+
import { useContext as useContext14, useEffect as useEffect16 } from "react";
|
|
2602
2680
|
function useContactEvent(eventName, handler, opts) {
|
|
2603
|
-
const engine =
|
|
2604
|
-
const entityId =
|
|
2681
|
+
const engine = useContext14(EngineContext);
|
|
2682
|
+
const entityId = useContext14(EntityContext);
|
|
2605
2683
|
if (!engine) throw new Error(`${eventName} hook must be used inside <Game>`);
|
|
2606
2684
|
if (entityId === null) throw new Error(`${eventName} hook must be used inside <Entity>`);
|
|
2607
|
-
|
|
2685
|
+
useEffect16(() => {
|
|
2608
2686
|
return engine.events.on(eventName, ({ a, b }) => {
|
|
2609
2687
|
const isA = a === entityId;
|
|
2610
2688
|
const isB = b === entityId;
|
|
@@ -2669,7 +2747,7 @@ function Checkpoint({
|
|
|
2669
2747
|
}
|
|
2670
2748
|
|
|
2671
2749
|
// src/components/Tilemap.tsx
|
|
2672
|
-
import { useEffect as
|
|
2750
|
+
import { useEffect as useEffect17, useState as useState5, useContext as useContext15 } from "react";
|
|
2673
2751
|
import { Fragment as Fragment3, jsx as jsx7 } from "react/jsx-runtime";
|
|
2674
2752
|
var animatedTiles = /* @__PURE__ */ new Map();
|
|
2675
2753
|
function getProperty(props, name) {
|
|
@@ -2693,9 +2771,9 @@ function Tilemap({
|
|
|
2693
2771
|
triggerLayer: triggerLayerName = "triggers",
|
|
2694
2772
|
onTileProperty
|
|
2695
2773
|
}) {
|
|
2696
|
-
const engine =
|
|
2774
|
+
const engine = useContext15(EngineContext);
|
|
2697
2775
|
const [spawnedNodes, setSpawnedNodes] = useState5([]);
|
|
2698
|
-
|
|
2776
|
+
useEffect17(() => {
|
|
2699
2777
|
if (!engine) return;
|
|
2700
2778
|
const createdEntities = [];
|
|
2701
2779
|
async function load() {
|
|
@@ -2878,7 +2956,7 @@ function Tilemap({
|
|
|
2878
2956
|
}
|
|
2879
2957
|
|
|
2880
2958
|
// src/components/ParallaxLayer.tsx
|
|
2881
|
-
import { useEffect as
|
|
2959
|
+
import { useEffect as useEffect18, useContext as useContext16 } from "react";
|
|
2882
2960
|
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2883
2961
|
function ParallaxLayerInner({
|
|
2884
2962
|
src,
|
|
@@ -2890,9 +2968,9 @@ function ParallaxLayerInner({
|
|
|
2890
2968
|
offsetX,
|
|
2891
2969
|
offsetY
|
|
2892
2970
|
}) {
|
|
2893
|
-
const engine =
|
|
2894
|
-
const entityId =
|
|
2895
|
-
|
|
2971
|
+
const engine = useContext16(EngineContext);
|
|
2972
|
+
const entityId = useContext16(EntityContext);
|
|
2973
|
+
useEffect18(() => {
|
|
2896
2974
|
engine.ecs.addComponent(entityId, {
|
|
2897
2975
|
type: "ParallaxLayer",
|
|
2898
2976
|
src,
|
|
@@ -2908,7 +2986,7 @@ function ParallaxLayerInner({
|
|
|
2908
2986
|
});
|
|
2909
2987
|
return () => engine.ecs.removeComponent(entityId, "ParallaxLayer");
|
|
2910
2988
|
}, []);
|
|
2911
|
-
|
|
2989
|
+
useEffect18(() => {
|
|
2912
2990
|
const layer = engine.ecs.getComponent(entityId, "ParallaxLayer");
|
|
2913
2991
|
if (!layer) return;
|
|
2914
2992
|
layer.src = src;
|
|
@@ -2990,9 +3068,9 @@ var ScreenFlash = forwardRef((_, ref) => {
|
|
|
2990
3068
|
ScreenFlash.displayName = "ScreenFlash";
|
|
2991
3069
|
|
|
2992
3070
|
// src/hooks/useGame.ts
|
|
2993
|
-
import { useContext as
|
|
3071
|
+
import { useContext as useContext17 } from "react";
|
|
2994
3072
|
function useGame() {
|
|
2995
|
-
const engine =
|
|
3073
|
+
const engine = useContext17(EngineContext);
|
|
2996
3074
|
if (!engine) throw new Error("useGame must be used inside <Game>");
|
|
2997
3075
|
return engine;
|
|
2998
3076
|
}
|
|
@@ -3043,17 +3121,17 @@ function useSnapshot() {
|
|
|
3043
3121
|
}
|
|
3044
3122
|
|
|
3045
3123
|
// src/hooks/useEntity.ts
|
|
3046
|
-
import { useContext as
|
|
3124
|
+
import { useContext as useContext18 } from "react";
|
|
3047
3125
|
function useEntity() {
|
|
3048
|
-
const id =
|
|
3126
|
+
const id = useContext18(EntityContext);
|
|
3049
3127
|
if (id === null) throw new Error("useEntity must be used inside <Entity>");
|
|
3050
3128
|
return id;
|
|
3051
3129
|
}
|
|
3052
3130
|
|
|
3053
3131
|
// src/hooks/useInput.ts
|
|
3054
|
-
import { useContext as
|
|
3132
|
+
import { useContext as useContext19 } from "react";
|
|
3055
3133
|
function useInput() {
|
|
3056
|
-
const engine =
|
|
3134
|
+
const engine = useContext19(EngineContext);
|
|
3057
3135
|
if (!engine) throw new Error("useInput must be used inside <Game>");
|
|
3058
3136
|
return engine.input;
|
|
3059
3137
|
}
|
|
@@ -3077,32 +3155,46 @@ function useInputMap(bindings) {
|
|
|
3077
3155
|
}
|
|
3078
3156
|
|
|
3079
3157
|
// src/hooks/useEvents.ts
|
|
3080
|
-
import { useContext as
|
|
3158
|
+
import { useContext as useContext20, useEffect as useEffect19 } from "react";
|
|
3081
3159
|
function useEvents() {
|
|
3082
|
-
const engine =
|
|
3160
|
+
const engine = useContext20(EngineContext);
|
|
3083
3161
|
if (!engine) throw new Error("useEvents must be used inside <Game>");
|
|
3084
3162
|
return engine.events;
|
|
3085
3163
|
}
|
|
3086
3164
|
function useEvent(event, handler) {
|
|
3087
3165
|
const events = useEvents();
|
|
3088
|
-
|
|
3166
|
+
useEffect19(() => {
|
|
3089
3167
|
return events.on(event, handler);
|
|
3090
3168
|
}, [events, event]);
|
|
3091
3169
|
}
|
|
3092
3170
|
|
|
3093
3171
|
// src/hooks/usePlatformerController.ts
|
|
3094
|
-
import { useContext as
|
|
3172
|
+
import { useContext as useContext21, useEffect as useEffect20 } from "react";
|
|
3173
|
+
function normalizeKeys(val, defaults) {
|
|
3174
|
+
if (!val) return defaults;
|
|
3175
|
+
return Array.isArray(val) ? val : [val];
|
|
3176
|
+
}
|
|
3095
3177
|
function usePlatformerController(entityId, opts = {}) {
|
|
3096
|
-
const engine =
|
|
3178
|
+
const engine = useContext21(EngineContext);
|
|
3097
3179
|
const {
|
|
3098
3180
|
speed = 200,
|
|
3099
3181
|
jumpForce = -500,
|
|
3100
3182
|
maxJumps = 1,
|
|
3101
3183
|
coyoteTime = 0.08,
|
|
3102
|
-
jumpBuffer = 0.08
|
|
3184
|
+
jumpBuffer = 0.08,
|
|
3185
|
+
jumpCooldown = 0.18,
|
|
3186
|
+
bindings
|
|
3103
3187
|
} = opts;
|
|
3104
|
-
|
|
3105
|
-
|
|
3188
|
+
const leftKeys = normalizeKeys(bindings?.left, ["ArrowLeft", "KeyA", "a"]);
|
|
3189
|
+
const rightKeys = normalizeKeys(bindings?.right, ["ArrowRight", "KeyD", "d"]);
|
|
3190
|
+
const jumpKeys = normalizeKeys(bindings?.jump, ["Space", "ArrowUp", "KeyW", "w"]);
|
|
3191
|
+
useEffect20(() => {
|
|
3192
|
+
const state = {
|
|
3193
|
+
coyoteTimer: 0,
|
|
3194
|
+
jumpBuffer: 0,
|
|
3195
|
+
jumpCooldown: 0,
|
|
3196
|
+
jumpsLeft: maxJumps
|
|
3197
|
+
};
|
|
3106
3198
|
const updateFn = (id, world, input, dt) => {
|
|
3107
3199
|
if (!world.hasEntity(id)) return;
|
|
3108
3200
|
const rb = world.getComponent(id, "RigidBody");
|
|
@@ -3111,11 +3203,12 @@ function usePlatformerController(entityId, opts = {}) {
|
|
|
3111
3203
|
state.coyoteTimer = coyoteTime;
|
|
3112
3204
|
state.jumpsLeft = maxJumps;
|
|
3113
3205
|
} else state.coyoteTimer = Math.max(0, state.coyoteTimer - dt);
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
const
|
|
3206
|
+
state.jumpCooldown = Math.max(0, state.jumpCooldown - dt);
|
|
3207
|
+
const jumpPressed = jumpKeys.some((k) => input.isPressed(k));
|
|
3208
|
+
if (jumpPressed && state.jumpCooldown === 0) state.jumpBuffer = jumpBuffer;
|
|
3209
|
+
else if (!jumpKeys.some((k) => input.isDown(k))) state.jumpBuffer = Math.max(0, state.jumpBuffer - dt);
|
|
3210
|
+
const left = leftKeys.some((k) => input.isDown(k));
|
|
3211
|
+
const right = rightKeys.some((k) => input.isDown(k));
|
|
3119
3212
|
if (left) rb.vx = -speed;
|
|
3120
3213
|
else if (right) rb.vx = speed;
|
|
3121
3214
|
else rb.vx *= rb.onGround ? 0.6 : 0.92;
|
|
@@ -3130,8 +3223,9 @@ function usePlatformerController(entityId, opts = {}) {
|
|
|
3130
3223
|
state.jumpsLeft = Math.max(0, state.jumpsLeft - 1);
|
|
3131
3224
|
state.coyoteTimer = 0;
|
|
3132
3225
|
state.jumpBuffer = 0;
|
|
3226
|
+
state.jumpCooldown = jumpCooldown;
|
|
3133
3227
|
}
|
|
3134
|
-
const jumpHeld =
|
|
3228
|
+
const jumpHeld = jumpKeys.some((k) => input.isDown(k));
|
|
3135
3229
|
if (!jumpHeld && rb.vy < -120) rb.vy += 800 * dt;
|
|
3136
3230
|
};
|
|
3137
3231
|
engine.ecs.addComponent(entityId, createScript(updateFn));
|
|
@@ -3140,11 +3234,11 @@ function usePlatformerController(entityId, opts = {}) {
|
|
|
3140
3234
|
}
|
|
3141
3235
|
|
|
3142
3236
|
// src/hooks/useTopDownMovement.ts
|
|
3143
|
-
import { useContext as
|
|
3237
|
+
import { useContext as useContext22, useEffect as useEffect21 } from "react";
|
|
3144
3238
|
function useTopDownMovement(entityId, opts = {}) {
|
|
3145
|
-
const engine =
|
|
3239
|
+
const engine = useContext22(EngineContext);
|
|
3146
3240
|
const { speed = 200, normalizeDiagonal = true } = opts;
|
|
3147
|
-
|
|
3241
|
+
useEffect21(() => {
|
|
3148
3242
|
const updateFn = (id, world, input) => {
|
|
3149
3243
|
if (!world.hasEntity(id)) return;
|
|
3150
3244
|
const rb = world.getComponent(id, "RigidBody");
|
|
@@ -3168,6 +3262,166 @@ function useTopDownMovement(entityId, opts = {}) {
|
|
|
3168
3262
|
}, []);
|
|
3169
3263
|
}
|
|
3170
3264
|
|
|
3265
|
+
// src/hooks/useSound.ts
|
|
3266
|
+
import { useEffect as useEffect22, useRef as useRef3 } from "react";
|
|
3267
|
+
var _audioCtx = null;
|
|
3268
|
+
function getAudioCtx() {
|
|
3269
|
+
if (!_audioCtx) _audioCtx = new AudioContext();
|
|
3270
|
+
return _audioCtx;
|
|
3271
|
+
}
|
|
3272
|
+
var bufferCache = /* @__PURE__ */ new Map();
|
|
3273
|
+
async function loadBuffer(src) {
|
|
3274
|
+
const cached = bufferCache.get(src);
|
|
3275
|
+
if (cached) return cached;
|
|
3276
|
+
const res = await fetch(src);
|
|
3277
|
+
const data = await res.arrayBuffer();
|
|
3278
|
+
const buf = await getAudioCtx().decodeAudioData(data);
|
|
3279
|
+
bufferCache.set(src, buf);
|
|
3280
|
+
return buf;
|
|
3281
|
+
}
|
|
3282
|
+
function useSound(src, opts = {}) {
|
|
3283
|
+
const bufferRef = useRef3(null);
|
|
3284
|
+
const sourceRef = useRef3(null);
|
|
3285
|
+
const gainRef = useRef3(null);
|
|
3286
|
+
const volRef = useRef3(opts.volume ?? 1);
|
|
3287
|
+
const loopRef = useRef3(opts.loop ?? false);
|
|
3288
|
+
useEffect22(() => {
|
|
3289
|
+
loadBuffer(src).then((buf) => {
|
|
3290
|
+
bufferRef.current = buf;
|
|
3291
|
+
}).catch(console.error);
|
|
3292
|
+
}, [src]);
|
|
3293
|
+
const play = () => {
|
|
3294
|
+
if (!bufferRef.current) return;
|
|
3295
|
+
const ctx = getAudioCtx();
|
|
3296
|
+
if (ctx.state === "suspended") ctx.resume();
|
|
3297
|
+
if (sourceRef.current) {
|
|
3298
|
+
try {
|
|
3299
|
+
sourceRef.current.stop();
|
|
3300
|
+
} catch {
|
|
3301
|
+
}
|
|
3302
|
+
sourceRef.current = null;
|
|
3303
|
+
}
|
|
3304
|
+
const gain = ctx.createGain();
|
|
3305
|
+
gain.gain.value = volRef.current;
|
|
3306
|
+
gain.connect(ctx.destination);
|
|
3307
|
+
gainRef.current = gain;
|
|
3308
|
+
const source = ctx.createBufferSource();
|
|
3309
|
+
source.buffer = bufferRef.current;
|
|
3310
|
+
source.loop = loopRef.current;
|
|
3311
|
+
source.connect(gain);
|
|
3312
|
+
source.start();
|
|
3313
|
+
source.onended = () => {
|
|
3314
|
+
sourceRef.current = null;
|
|
3315
|
+
};
|
|
3316
|
+
sourceRef.current = source;
|
|
3317
|
+
};
|
|
3318
|
+
const stop = () => {
|
|
3319
|
+
if (sourceRef.current) {
|
|
3320
|
+
try {
|
|
3321
|
+
sourceRef.current.stop();
|
|
3322
|
+
} catch {
|
|
3323
|
+
}
|
|
3324
|
+
sourceRef.current = null;
|
|
3325
|
+
}
|
|
3326
|
+
};
|
|
3327
|
+
const setVolume = (v) => {
|
|
3328
|
+
volRef.current = v;
|
|
3329
|
+
if (gainRef.current) gainRef.current.gain.value = v;
|
|
3330
|
+
};
|
|
3331
|
+
return { play, stop, setVolume };
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
// src/hooks/useTimer.ts
|
|
3335
|
+
function createTimer(duration, onComplete, autoStart = false) {
|
|
3336
|
+
let _duration = duration;
|
|
3337
|
+
let _elapsed = 0;
|
|
3338
|
+
let _running = autoStart;
|
|
3339
|
+
return {
|
|
3340
|
+
update(dt) {
|
|
3341
|
+
if (!_running) return;
|
|
3342
|
+
_elapsed += dt;
|
|
3343
|
+
if (_elapsed >= _duration) {
|
|
3344
|
+
_elapsed = _duration;
|
|
3345
|
+
_running = false;
|
|
3346
|
+
onComplete?.();
|
|
3347
|
+
}
|
|
3348
|
+
},
|
|
3349
|
+
start() {
|
|
3350
|
+
_running = true;
|
|
3351
|
+
},
|
|
3352
|
+
stop() {
|
|
3353
|
+
_running = false;
|
|
3354
|
+
},
|
|
3355
|
+
reset(d) {
|
|
3356
|
+
_elapsed = 0;
|
|
3357
|
+
_running = false;
|
|
3358
|
+
if (d !== void 0) _duration = d;
|
|
3359
|
+
},
|
|
3360
|
+
restart() {
|
|
3361
|
+
_elapsed = 0;
|
|
3362
|
+
_running = true;
|
|
3363
|
+
},
|
|
3364
|
+
get running() {
|
|
3365
|
+
return _running;
|
|
3366
|
+
},
|
|
3367
|
+
get elapsed() {
|
|
3368
|
+
return _elapsed;
|
|
3369
|
+
},
|
|
3370
|
+
get remaining() {
|
|
3371
|
+
return Math.max(0, _duration - _elapsed);
|
|
3372
|
+
},
|
|
3373
|
+
get progress() {
|
|
3374
|
+
return _duration > 0 ? Math.min(1, _elapsed / _duration) : 1;
|
|
3375
|
+
}
|
|
3376
|
+
};
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3379
|
+
// src/hooks/useGamepad.ts
|
|
3380
|
+
import { useEffect as useEffect23, useRef as useRef4, useState as useState6 } from "react";
|
|
3381
|
+
var EMPTY_STATE = { connected: false, axes: [], buttons: [] };
|
|
3382
|
+
function useGamepad(playerIndex = 0) {
|
|
3383
|
+
const [state, setState] = useState6(EMPTY_STATE);
|
|
3384
|
+
const rafRef = useRef4(0);
|
|
3385
|
+
useEffect23(() => {
|
|
3386
|
+
const poll = () => {
|
|
3387
|
+
const gp = navigator.getGamepads()[playerIndex];
|
|
3388
|
+
if (gp) {
|
|
3389
|
+
setState({
|
|
3390
|
+
connected: true,
|
|
3391
|
+
axes: Array.from(gp.axes),
|
|
3392
|
+
buttons: Array.from(gp.buttons).map((b) => b.pressed)
|
|
3393
|
+
});
|
|
3394
|
+
} else {
|
|
3395
|
+
setState((s) => s.connected ? EMPTY_STATE : s);
|
|
3396
|
+
}
|
|
3397
|
+
rafRef.current = requestAnimationFrame(poll);
|
|
3398
|
+
};
|
|
3399
|
+
rafRef.current = requestAnimationFrame(poll);
|
|
3400
|
+
return () => cancelAnimationFrame(rafRef.current);
|
|
3401
|
+
}, [playerIndex]);
|
|
3402
|
+
return state;
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
// src/hooks/usePause.ts
|
|
3406
|
+
import { useContext as useContext23, useState as useState7, useCallback as useCallback2 } from "react";
|
|
3407
|
+
function usePause() {
|
|
3408
|
+
const engine = useContext23(EngineContext);
|
|
3409
|
+
const [paused, setPaused] = useState7(false);
|
|
3410
|
+
const pause = useCallback2(() => {
|
|
3411
|
+
engine.loop.pause();
|
|
3412
|
+
setPaused(true);
|
|
3413
|
+
}, [engine]);
|
|
3414
|
+
const resume = useCallback2(() => {
|
|
3415
|
+
engine.loop.resume();
|
|
3416
|
+
setPaused(false);
|
|
3417
|
+
}, [engine]);
|
|
3418
|
+
const toggle = useCallback2(() => {
|
|
3419
|
+
if (engine.loop.isPaused) resume();
|
|
3420
|
+
else pause();
|
|
3421
|
+
}, [engine, pause, resume]);
|
|
3422
|
+
return { paused, pause, resume, toggle };
|
|
3423
|
+
}
|
|
3424
|
+
|
|
3171
3425
|
// src/components/spriteAtlas.ts
|
|
3172
3426
|
function createAtlas(names, _columns) {
|
|
3173
3427
|
const atlas = {};
|
|
@@ -3193,6 +3447,7 @@ export {
|
|
|
3193
3447
|
Script,
|
|
3194
3448
|
Sprite,
|
|
3195
3449
|
SquashStretch,
|
|
3450
|
+
Text,
|
|
3196
3451
|
Tilemap,
|
|
3197
3452
|
Transform,
|
|
3198
3453
|
World,
|
|
@@ -3200,6 +3455,7 @@ export {
|
|
|
3200
3455
|
createInputMap,
|
|
3201
3456
|
createSprite,
|
|
3202
3457
|
createTag,
|
|
3458
|
+
createTimer,
|
|
3203
3459
|
createTransform,
|
|
3204
3460
|
definePlugin,
|
|
3205
3461
|
findByTag,
|
|
@@ -3215,10 +3471,13 @@ export {
|
|
|
3215
3471
|
useEvent,
|
|
3216
3472
|
useEvents,
|
|
3217
3473
|
useGame,
|
|
3474
|
+
useGamepad,
|
|
3218
3475
|
useInput,
|
|
3219
3476
|
useInputMap,
|
|
3477
|
+
usePause,
|
|
3220
3478
|
usePlatformerController,
|
|
3221
3479
|
useSnapshot,
|
|
3480
|
+
useSound,
|
|
3222
3481
|
useTopDownMovement,
|
|
3223
3482
|
useTriggerEnter,
|
|
3224
3483
|
useTriggerExit
|