ecspresso 0.11.0 → 0.12.0
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/README.md +200 -148
- package/dist/asset-manager.d.ts +1 -1
- package/dist/asset-types.d.ts +2 -2
- package/dist/command-buffer.d.ts +34 -24
- package/dist/ecspresso-builder.d.ts +100 -72
- package/dist/ecspresso.d.ts +257 -122
- package/dist/entity-manager.d.ts +57 -47
- package/dist/index.d.ts +5 -4
- package/dist/plugin.d.ts +61 -0
- package/dist/{bundles → plugins}/audio.d.ts +27 -47
- package/dist/{bundles → plugins}/bounds.d.ts +17 -25
- package/dist/{bundles → plugins}/camera.d.ts +8 -9
- package/dist/{bundles → plugins}/collision.d.ts +22 -26
- package/dist/plugins/coroutine.d.ts +126 -0
- package/dist/{bundles → plugins}/diagnostics.d.ts +5 -4
- package/dist/{bundles → plugins}/input.d.ts +9 -15
- package/dist/plugins/particles.d.ts +225 -0
- package/dist/{bundles → plugins}/physics2D.d.ts +27 -23
- package/dist/{bundles → plugins}/renderers/renderer2D.d.ts +40 -39
- package/dist/{bundles → plugins}/spatial-index.d.ts +11 -10
- package/dist/plugins/sprite-animation.d.ts +150 -0
- package/dist/{bundles → plugins}/state-machine.d.ts +50 -104
- package/dist/plugins/timers.d.ts +151 -0
- package/dist/{bundles → plugins}/transform.d.ts +18 -19
- package/dist/{bundles → plugins}/tween.d.ts +36 -71
- package/dist/resource-manager.d.ts +32 -7
- package/dist/screen-manager.d.ts +17 -11
- package/dist/screen-types.d.ts +5 -2
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +17 -17
- package/dist/src/plugins/audio.js +4 -0
- package/dist/src/plugins/audio.js.map +10 -0
- package/dist/src/plugins/bounds.js +4 -0
- package/dist/src/plugins/bounds.js.map +10 -0
- package/dist/src/plugins/camera.js +4 -0
- package/dist/src/plugins/camera.js.map +10 -0
- package/dist/src/plugins/collision.js +4 -0
- package/dist/src/plugins/collision.js.map +11 -0
- package/dist/src/plugins/coroutine.js +4 -0
- package/dist/src/plugins/coroutine.js.map +10 -0
- package/dist/src/plugins/diagnostics.js +5 -0
- package/dist/src/plugins/diagnostics.js.map +10 -0
- package/dist/src/plugins/input.js +4 -0
- package/dist/src/plugins/input.js.map +10 -0
- package/dist/src/plugins/particles.js +4 -0
- package/dist/src/plugins/particles.js.map +10 -0
- package/dist/src/plugins/physics2D.js +4 -0
- package/dist/src/plugins/physics2D.js.map +11 -0
- package/dist/src/plugins/renderers/renderer2D.js +4 -0
- package/dist/src/plugins/renderers/renderer2D.js.map +10 -0
- package/dist/src/plugins/spatial-index.js +4 -0
- package/dist/src/plugins/spatial-index.js.map +11 -0
- package/dist/src/plugins/sprite-animation.js +4 -0
- package/dist/src/plugins/sprite-animation.js.map +10 -0
- package/dist/src/plugins/state-machine.js +4 -0
- package/dist/src/plugins/state-machine.js.map +10 -0
- package/dist/src/plugins/timers.js +4 -0
- package/dist/src/plugins/timers.js.map +10 -0
- package/dist/src/plugins/transform.js +4 -0
- package/dist/src/plugins/transform.js.map +10 -0
- package/dist/src/plugins/tween.js +4 -0
- package/dist/src/plugins/tween.js.map +11 -0
- package/dist/system-builder.d.ts +66 -97
- package/dist/type-utils.d.ts +218 -27
- package/dist/types.d.ts +52 -24
- package/dist/utils/check-required-cycle.d.ts +1 -1
- package/dist/utils/narrowphase.d.ts +7 -7
- package/package.json +53 -45
- package/dist/bundle.d.ts +0 -173
- package/dist/bundles/timers.d.ts +0 -173
- package/dist/src/bundles/audio.js +0 -4
- package/dist/src/bundles/audio.js.map +0 -10
- package/dist/src/bundles/bounds.js +0 -4
- package/dist/src/bundles/bounds.js.map +0 -10
- package/dist/src/bundles/camera.js +0 -4
- package/dist/src/bundles/camera.js.map +0 -10
- package/dist/src/bundles/collision.js +0 -4
- package/dist/src/bundles/collision.js.map +0 -11
- package/dist/src/bundles/diagnostics.js +0 -5
- package/dist/src/bundles/diagnostics.js.map +0 -10
- package/dist/src/bundles/input.js +0 -4
- package/dist/src/bundles/input.js.map +0 -10
- package/dist/src/bundles/physics2D.js +0 -4
- package/dist/src/bundles/physics2D.js.map +0 -11
- package/dist/src/bundles/renderers/renderer2D.js +0 -4
- package/dist/src/bundles/renderers/renderer2D.js.map +0 -10
- package/dist/src/bundles/spatial-index.js +0 -4
- package/dist/src/bundles/spatial-index.js.map +0 -11
- package/dist/src/bundles/state-machine.js +0 -4
- package/dist/src/bundles/state-machine.js.map +0 -10
- package/dist/src/bundles/timers.js +0 -4
- package/dist/src/bundles/timers.js.map +0 -10
- package/dist/src/bundles/transform.js +0 -4
- package/dist/src/bundles/transform.js.map +0 -10
- package/dist/src/bundles/tween.js +0 -4
- package/dist/src/bundles/tween.js.map +0 -11
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Collision
|
|
2
|
+
* Collision Plugin for ECSpresso
|
|
3
3
|
*
|
|
4
4
|
* Provides layer-based collision detection with events.
|
|
5
5
|
* Uses worldTransform for position (world-space collision).
|
|
6
6
|
* Supports AABB and circle colliders.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import type {
|
|
10
|
-
import type {
|
|
8
|
+
import { type Plugin, type BasePluginOptions } from 'ecspresso';
|
|
9
|
+
import type { WorldConfigFrom } from '../type-utils';
|
|
10
|
+
import type { TransformWorldConfig } from './transform';
|
|
11
11
|
/**
|
|
12
12
|
* Axis-Aligned Bounding Box collider.
|
|
13
13
|
*/
|
|
@@ -42,13 +42,13 @@ export interface CollisionLayer<L extends string = never> {
|
|
|
42
42
|
collidesWith: readonly L[];
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
|
-
* Component types provided by the collision
|
|
46
|
-
* Included automatically via `.
|
|
45
|
+
* Component types provided by the collision plugin.
|
|
46
|
+
* Included automatically via `.withPlugin(createCollisionPlugin())`.
|
|
47
47
|
*
|
|
48
48
|
* @example
|
|
49
49
|
* ```typescript
|
|
50
50
|
* const ecs = ECSpresso.create()
|
|
51
|
-
* .
|
|
51
|
+
* .withPlugin(createCollisionPlugin())
|
|
52
52
|
* .withComponentTypes<{ sprite: Sprite; enemy: boolean }>()
|
|
53
53
|
* .build();
|
|
54
54
|
* ```
|
|
@@ -79,23 +79,17 @@ export interface CollisionEvent<L extends string = never> {
|
|
|
79
79
|
depth: number;
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* Event types provided by the collision
|
|
82
|
+
* Event types provided by the collision plugin.
|
|
83
83
|
*/
|
|
84
84
|
export interface CollisionEventTypes<L extends string = never> {
|
|
85
85
|
collision: CollisionEvent<L>;
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
|
-
* Configuration options for the collision
|
|
88
|
+
* Configuration options for the collision plugin.
|
|
89
89
|
*/
|
|
90
|
-
export interface
|
|
91
|
-
/** System group name (default: 'physics') */
|
|
92
|
-
systemGroup?: G;
|
|
93
|
-
/** Priority for collision system (default: 0) */
|
|
94
|
-
priority?: number;
|
|
90
|
+
export interface CollisionPluginOptions<G extends string = 'physics'> extends BasePluginOptions<G> {
|
|
95
91
|
/** Name of the collision event (default: 'collision') */
|
|
96
92
|
collisionEventName?: string;
|
|
97
|
-
/** Execution phase (default: 'postUpdate') */
|
|
98
|
-
phase?: SystemPhase;
|
|
99
93
|
}
|
|
100
94
|
/**
|
|
101
95
|
* Create an AABB collider component.
|
|
@@ -245,17 +239,19 @@ export type CollisionPairCallback<W = unknown> = (firstEntityId: number, secondE
|
|
|
245
239
|
* 'player:enemy': (playerId, enemyId, ecs) => { ... },
|
|
246
240
|
* });
|
|
247
241
|
*
|
|
248
|
-
* ecs.eventBus.subscribe('collision', (data) => handler(data, ecs));
|
|
242
|
+
* ecs.eventBus.subscribe('collision', (data) => handler({ data, ecs }));
|
|
249
243
|
* ```
|
|
250
244
|
*/
|
|
251
245
|
export declare function createCollisionPairHandler<W = unknown, L extends string = string>(pairs: {
|
|
252
246
|
[K in `${L}:${L}`]?: CollisionPairCallback<W>;
|
|
253
|
-
}): (
|
|
254
|
-
|
|
247
|
+
}): (ctx: {
|
|
248
|
+
data: CollisionEvent<L>;
|
|
249
|
+
ecs: W;
|
|
250
|
+
}) => void;
|
|
255
251
|
/**
|
|
256
|
-
* Create a collision
|
|
252
|
+
* Create a collision plugin for ECSpresso.
|
|
257
253
|
*
|
|
258
|
-
* This
|
|
254
|
+
* This plugin provides:
|
|
259
255
|
* - Collision detection between entities with colliders
|
|
260
256
|
* - AABB-AABB, circle-circle, and AABB-circle collision
|
|
261
257
|
* - Layer-based filtering for collision pairs
|
|
@@ -264,15 +260,15 @@ type CombinedComponentTypes<L extends string> = CollisionComponentTypes<L> & Tra
|
|
|
264
260
|
*
|
|
265
261
|
* Uses worldTransform for position (world-space collision detection).
|
|
266
262
|
* The `layers` parameter is required for type inference — at runtime the
|
|
267
|
-
*
|
|
263
|
+
* plugin does not consume it.
|
|
268
264
|
*
|
|
269
265
|
* @example
|
|
270
266
|
* ```typescript
|
|
271
267
|
* const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });
|
|
272
268
|
* const ecs = ECSpresso
|
|
273
269
|
* .create()
|
|
274
|
-
* .
|
|
275
|
-
* .
|
|
270
|
+
* .withPlugin(createTransformPlugin())
|
|
271
|
+
* .withPlugin(createCollisionPlugin({ layers }))
|
|
276
272
|
* .build();
|
|
277
273
|
*
|
|
278
274
|
* // Entity with collision
|
|
@@ -283,7 +279,7 @@ type CombinedComponentTypes<L extends string> = CollisionComponentTypes<L> & Tra
|
|
|
283
279
|
* });
|
|
284
280
|
* ```
|
|
285
281
|
*/
|
|
286
|
-
export declare function
|
|
282
|
+
export declare function createCollisionPlugin<L extends string, G extends string = 'physics'>(options: CollisionPluginOptions<G> & {
|
|
287
283
|
layers: LayerFactories<Record<L, readonly string[]>>;
|
|
288
|
-
}):
|
|
284
|
+
}): Plugin<WorldConfigFrom<CollisionComponentTypes<L>, CollisionEventTypes<L>>, TransformWorldConfig, 'collision-detection', G>;
|
|
289
285
|
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coroutine Plugin for ECSpresso
|
|
3
|
+
*
|
|
4
|
+
* ES6 generator-based coroutines for multi-step, frame-spanning scripted sequences.
|
|
5
|
+
* A `coroutine` component holds a live generator. A system ticks all generators each
|
|
6
|
+
* frame via `.next(dt)`. Helper generators (`waitSeconds`, `waitFrames`, `waitUntil`,
|
|
7
|
+
* `waitForEvent`, `parallel`, `race`) compose via `yield*`.
|
|
8
|
+
*/
|
|
9
|
+
import { type Plugin, type BasePluginOptions } from 'ecspresso';
|
|
10
|
+
import type { EventsOfWorld, AnyECSpresso } from 'ecspresso';
|
|
11
|
+
import type { WorldConfigFrom, EmptyConfig } from '../type-utils';
|
|
12
|
+
/**
|
|
13
|
+
* Yields void, returns void, receives deltaTime (number) via `.next(dt)`.
|
|
14
|
+
* First `.next(dt)` initializes the generator (runs to first yield, dt discarded per JS spec).
|
|
15
|
+
* Subsequent `.next(dt)` resume from yield with dt as the yield expression value.
|
|
16
|
+
*/
|
|
17
|
+
export type CoroutineGenerator = Generator<void, void, number>;
|
|
18
|
+
export interface CoroutineEventData {
|
|
19
|
+
entityId: number;
|
|
20
|
+
}
|
|
21
|
+
export interface CoroutineState {
|
|
22
|
+
generator: CoroutineGenerator;
|
|
23
|
+
onComplete?: (data: CoroutineEventData) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface CoroutineComponentTypes {
|
|
26
|
+
coroutine: CoroutineState;
|
|
27
|
+
}
|
|
28
|
+
export interface CoroutinePluginOptions<G extends string = 'coroutines'> extends BasePluginOptions<G> {
|
|
29
|
+
}
|
|
30
|
+
export interface CoroutineOptions {
|
|
31
|
+
onComplete?: (data: CoroutineEventData) => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create a coroutine component for spawning or adding to an entity.
|
|
35
|
+
*
|
|
36
|
+
* @param generator - The generator function to drive
|
|
37
|
+
* @param options - Optional configuration (onComplete event)
|
|
38
|
+
* @returns Component object suitable for spreading into spawn()
|
|
39
|
+
*/
|
|
40
|
+
export declare function createCoroutine(generator: CoroutineGenerator, options?: CoroutineOptions): Pick<CoroutineComponentTypes, 'coroutine'>;
|
|
41
|
+
/**
|
|
42
|
+
* Wait for a specified number of seconds. Accumulates dt until elapsed >= seconds.
|
|
43
|
+
* If seconds <= 0, returns immediately.
|
|
44
|
+
*/
|
|
45
|
+
export declare function waitSeconds(seconds: number): CoroutineGenerator;
|
|
46
|
+
/**
|
|
47
|
+
* Wait for a specified number of frames. Yields `frames` times.
|
|
48
|
+
* If frames <= 0, returns immediately.
|
|
49
|
+
*/
|
|
50
|
+
export declare function waitFrames(frames: number): CoroutineGenerator;
|
|
51
|
+
/**
|
|
52
|
+
* Wait until a predicate returns true. Yields each frame until predicate is satisfied.
|
|
53
|
+
* User closes over ecs if needed for state checks.
|
|
54
|
+
*/
|
|
55
|
+
export declare function waitUntil(predicate: () => boolean): CoroutineGenerator;
|
|
56
|
+
/**
|
|
57
|
+
* Run multiple coroutines in parallel. Completes when all finish.
|
|
58
|
+
* Initializes all sub-generators, ticks all each frame.
|
|
59
|
+
* Empty array = immediate return.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parallel(...coroutines: CoroutineGenerator[]): CoroutineGenerator;
|
|
62
|
+
/**
|
|
63
|
+
* Run multiple coroutines, completing when the first one finishes.
|
|
64
|
+
* Calls `.return()` on remaining generators (triggers finally blocks).
|
|
65
|
+
* Empty array = immediate return.
|
|
66
|
+
*/
|
|
67
|
+
export declare function race(...coroutines: CoroutineGenerator[]): CoroutineGenerator;
|
|
68
|
+
/**
|
|
69
|
+
* Wait until a matching event fires on the event bus.
|
|
70
|
+
* Subscribes via eventBus.subscribe, yields until event received, unsubscribes in finally block.
|
|
71
|
+
*
|
|
72
|
+
* @param eventBus - Object with subscribe method (typically ecs.eventBus)
|
|
73
|
+
* @param eventType - Event type name to listen for
|
|
74
|
+
* @param filter - Optional predicate to filter events
|
|
75
|
+
*/
|
|
76
|
+
export declare function waitForEvent<ET extends Record<string, any>, E extends keyof ET & string>(eventBus: {
|
|
77
|
+
subscribe(type: E, cb: (data: ET[E]) => void): () => void;
|
|
78
|
+
}, eventType: E, filter?: (data: ET[E]) => boolean): CoroutineGenerator;
|
|
79
|
+
/**
|
|
80
|
+
* Structural interface for ECS methods used by cancelCoroutine.
|
|
81
|
+
*/
|
|
82
|
+
export interface CoroutineWorld {
|
|
83
|
+
getComponent(entityId: number, componentName: string): unknown | undefined;
|
|
84
|
+
commands: {
|
|
85
|
+
removeComponent(entityId: number, componentName: string): void;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Cancel a running coroutine on an entity. Calls generator.return() (triggers finally blocks)
|
|
90
|
+
* and queues component removal.
|
|
91
|
+
*
|
|
92
|
+
* @returns true if the entity had a coroutine that was cancelled, false otherwise
|
|
93
|
+
*/
|
|
94
|
+
export declare function cancelCoroutine(ecs: CoroutineWorld, entityId: number): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Type-safe coroutine helpers that validate event names against a world's event types.
|
|
97
|
+
* Use `createCoroutineHelpers<typeof ecs>()` to get compile-time validation.
|
|
98
|
+
*/
|
|
99
|
+
export interface CoroutineHelpers<W extends AnyECSpresso> {
|
|
100
|
+
createCoroutine: (generator: CoroutineGenerator, options?: {
|
|
101
|
+
onComplete?: (data: CoroutineEventData) => void;
|
|
102
|
+
}) => Pick<CoroutineComponentTypes, 'coroutine'>;
|
|
103
|
+
waitForEvent: <E extends keyof EventsOfWorld<W> & string>(eventBus: {
|
|
104
|
+
subscribe(type: E, cb: (data: EventsOfWorld<W>[E]) => void): () => void;
|
|
105
|
+
}, eventType: E, filter?: (data: EventsOfWorld<W>[E]) => boolean) => CoroutineGenerator;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Create typed coroutine helpers that validate event names at compile time.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const { createCoroutine, waitForEvent } = createCoroutineHelpers<typeof ecs>();
|
|
113
|
+
* ecs.spawn({ ...createCoroutine(myGen(), { onComplete: (data) => console.log(data.entityId) }) });
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export declare function createCoroutineHelpers<W extends AnyECSpresso>(_world?: W): CoroutineHelpers<W>;
|
|
117
|
+
/**
|
|
118
|
+
* Create a coroutine plugin for ECSpresso.
|
|
119
|
+
*
|
|
120
|
+
* This plugin provides:
|
|
121
|
+
* - Coroutine system that ticks all generator-based coroutines each frame
|
|
122
|
+
* - Automatic cleanup via dispose callback (triggers generator finally blocks)
|
|
123
|
+
* - `onComplete` callback invocation
|
|
124
|
+
* - Component removal on completion
|
|
125
|
+
*/
|
|
126
|
+
export declare function createCoroutinePlugin<G extends string = 'coroutines'>(options?: CoroutinePluginOptions<G>): Plugin<WorldConfigFrom<CoroutineComponentTypes>, EmptyConfig, 'coroutine-update', G>;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Diagnostics
|
|
2
|
+
* Diagnostics Plugin for ECSpresso
|
|
3
3
|
*
|
|
4
4
|
* Runtime diagnostics: FPS, entity count, per-system timing, per-phase timing,
|
|
5
5
|
* and an optional DOM overlay for visual debugging.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { type Plugin } from 'ecspresso';
|
|
8
8
|
import type { SystemPhase } from 'ecspresso';
|
|
9
|
+
import type { WorldConfigFrom, EmptyConfig } from '../type-utils';
|
|
9
10
|
export interface DiagnosticsData {
|
|
10
11
|
fps: number;
|
|
11
12
|
entityCount: number;
|
|
@@ -16,7 +17,7 @@ export interface DiagnosticsData {
|
|
|
16
17
|
export interface DiagnosticsResourceTypes {
|
|
17
18
|
diagnostics: DiagnosticsData;
|
|
18
19
|
}
|
|
19
|
-
export interface
|
|
20
|
+
export interface DiagnosticsPluginOptions<G extends string = 'diagnostics'> {
|
|
20
21
|
/** System group name (default: 'diagnostics') */
|
|
21
22
|
systemGroup?: G;
|
|
22
23
|
/** Enable timing collection on initialize (default: true) */
|
|
@@ -34,7 +35,7 @@ export interface DiagnosticsOverlayOptions {
|
|
|
34
35
|
/** Maximum systems to show in overlay (default: 10) */
|
|
35
36
|
maxSystemsShown?: number;
|
|
36
37
|
}
|
|
37
|
-
export declare function
|
|
38
|
+
export declare function createDiagnosticsPlugin<G extends string = 'diagnostics'>(options?: DiagnosticsPluginOptions<G>): Plugin<WorldConfigFrom<{}, {}, DiagnosticsResourceTypes>, EmptyConfig, 'diagnostics-collect', G>;
|
|
38
39
|
/**
|
|
39
40
|
* Create a DOM overlay that displays diagnostics data.
|
|
40
41
|
* Returns a cleanup function that removes the element and clears the interval.
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Input
|
|
2
|
+
* Input Plugin for ECSpresso
|
|
3
3
|
*
|
|
4
4
|
* Provides frame-accurate keyboard, pointer (mouse + touch via PointerEvent),
|
|
5
|
-
* and action mapping input. Resource-only
|
|
5
|
+
* and action mapping input. Resource-only plugin — input is polled via the
|
|
6
6
|
* `inputState` resource. No ECS components or events.
|
|
7
7
|
*
|
|
8
8
|
* DOM events are accumulated between frames and snapshotted once per frame
|
|
9
9
|
* in the system's process step, so all systems see consistent state.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
12
|
-
import type {
|
|
11
|
+
import { type Plugin, type BasePluginOptions } from 'ecspresso';
|
|
12
|
+
import type { WorldConfigFrom, EmptyConfig } from '../type-utils';
|
|
13
13
|
export interface Vec2 {
|
|
14
14
|
x: number;
|
|
15
15
|
y: number;
|
|
@@ -68,13 +68,7 @@ export type ActionMap<A extends string = string> = Record<A, ActionBinding>;
|
|
|
68
68
|
export interface InputResourceTypes<A extends string = string> {
|
|
69
69
|
inputState: InputState<A>;
|
|
70
70
|
}
|
|
71
|
-
export interface
|
|
72
|
-
/** System group name (default: 'input') */
|
|
73
|
-
systemGroup?: G;
|
|
74
|
-
/** Priority for input system (default: 100) */
|
|
75
|
-
priority?: number;
|
|
76
|
-
/** Execution phase (default: 'preUpdate') */
|
|
77
|
-
phase?: SystemPhase;
|
|
71
|
+
export interface InputPluginOptions<A extends string = string, G extends string = 'input'> extends BasePluginOptions<G> {
|
|
78
72
|
/** Initial action mappings */
|
|
79
73
|
actions?: ActionMap<A>;
|
|
80
74
|
/** EventTarget to attach listeners to (default: globalThis). Pass a custom target for testability. */
|
|
@@ -88,9 +82,9 @@ export interface InputBundleOptions<A extends string = string, G extends string
|
|
|
88
82
|
*/
|
|
89
83
|
export declare function createActionBinding(binding: ActionBinding): ActionBinding;
|
|
90
84
|
/**
|
|
91
|
-
* Create an input
|
|
85
|
+
* Create an input plugin for ECSpresso.
|
|
92
86
|
*
|
|
93
|
-
* This
|
|
87
|
+
* This plugin provides:
|
|
94
88
|
* - Frame-accurate keyboard state (isDown, justPressed, justReleased)
|
|
95
89
|
* - Pointer position/delta and button state (mouse + touch via PointerEvent)
|
|
96
90
|
* - Named action mapping with runtime remapping
|
|
@@ -99,7 +93,7 @@ export declare function createActionBinding(binding: ActionBinding): ActionBindi
|
|
|
99
93
|
* @example
|
|
100
94
|
* ```typescript
|
|
101
95
|
* const ecs = ECSpresso.create()
|
|
102
|
-
* .
|
|
96
|
+
* .withPlugin(createInputPlugin({
|
|
103
97
|
* actions: {
|
|
104
98
|
* jump: { keys: [' ', 'ArrowUp'] },
|
|
105
99
|
* shoot: { keys: ['z'], buttons: [0] },
|
|
@@ -113,5 +107,5 @@ export declare function createActionBinding(binding: ActionBinding): ActionBindi
|
|
|
113
107
|
* if (input.keyboard.isDown('ArrowRight')) { ... }
|
|
114
108
|
* ```
|
|
115
109
|
*/
|
|
116
|
-
export declare function
|
|
110
|
+
export declare function createInputPlugin<A extends string = string, G extends string = 'input'>(options?: InputPluginOptions<A, G>): Plugin<WorldConfigFrom<{}, {}, InputResourceTypes<A>>, EmptyConfig, 'input-state', G>;
|
|
117
111
|
export {};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Particle System Plugin for ECSpresso
|
|
3
|
+
*
|
|
4
|
+
* High-performance particle system where particles live outside the ECS in
|
|
5
|
+
* pre-allocated pools. Renders via PixiJS v8's ParticleContainer + Particle API.
|
|
6
|
+
* Renderer2D is a required dependency.
|
|
7
|
+
*
|
|
8
|
+
* Follows the established plugin pattern: immutable shared config
|
|
9
|
+
* (ParticleEffectConfig) + mutable per-entity state (ParticleEmitter) component,
|
|
10
|
+
* side-storage Map for PixiJS objects, kit pattern for typed helpers.
|
|
11
|
+
*/
|
|
12
|
+
import { type Plugin, type BasePluginOptions } from 'ecspresso';
|
|
13
|
+
import type { BaseWorld } from 'ecspresso';
|
|
14
|
+
import type { WorldConfigFrom } from '../type-utils';
|
|
15
|
+
import type { TransformComponentTypes } from 'ecspresso/plugins/transform';
|
|
16
|
+
/** BaseWorld narrowed to particle components for typed access in helpers. */
|
|
17
|
+
type ParticleWorld = BaseWorld<ParticleComponentTypes>;
|
|
18
|
+
/** Fixed value or random range [min, max] */
|
|
19
|
+
export type ParticleValue = number | readonly [number, number];
|
|
20
|
+
/** Emission geometry */
|
|
21
|
+
export type EmissionShape = 'point' | 'circle';
|
|
22
|
+
/** Blend modes for particle rendering */
|
|
23
|
+
export type ParticleBlendMode = 'normal' | 'add' | 'multiply' | 'screen';
|
|
24
|
+
/**
|
|
25
|
+
* User-facing config input for defining a particle effect.
|
|
26
|
+
* All properties optional except maxParticles and texture.
|
|
27
|
+
*/
|
|
28
|
+
export interface ParticleEffectInput {
|
|
29
|
+
/** Pool size — maximum simultaneous particles */
|
|
30
|
+
maxParticles: number;
|
|
31
|
+
/** PixiJS Texture for particles */
|
|
32
|
+
texture: unknown;
|
|
33
|
+
/** Particles per second (0 = burst-only, default: 10) */
|
|
34
|
+
spawnRate?: number;
|
|
35
|
+
/** Particles per burst (default: 0) */
|
|
36
|
+
burstCount?: number;
|
|
37
|
+
/** Emitter lifetime in seconds (-1 = infinite, default: -1) */
|
|
38
|
+
duration?: number;
|
|
39
|
+
/** Per-particle lifetime in seconds (default: 1) */
|
|
40
|
+
lifetime?: ParticleValue;
|
|
41
|
+
/** Initial speed in pixels/second (default: 100) */
|
|
42
|
+
speed?: ParticleValue;
|
|
43
|
+
/** Emission direction in radians (default: [0, 2*PI]) */
|
|
44
|
+
angle?: ParticleValue;
|
|
45
|
+
/** Spawn geometry (default: 'point') */
|
|
46
|
+
emissionShape?: EmissionShape;
|
|
47
|
+
/** Radius for 'circle' shape (default: 0) */
|
|
48
|
+
emissionRadius?: number;
|
|
49
|
+
/** Acceleration in pixels/second^2 (default: {x: 0, y: 0}) */
|
|
50
|
+
gravity?: {
|
|
51
|
+
readonly x: number;
|
|
52
|
+
readonly y: number;
|
|
53
|
+
};
|
|
54
|
+
/** Initial scale (default: 1) */
|
|
55
|
+
startSize?: ParticleValue;
|
|
56
|
+
/** Final scale (default: same as startSize) */
|
|
57
|
+
endSize?: ParticleValue;
|
|
58
|
+
/** Initial opacity (default: 1) */
|
|
59
|
+
startAlpha?: ParticleValue;
|
|
60
|
+
/** Final opacity (default: 0) */
|
|
61
|
+
endAlpha?: ParticleValue;
|
|
62
|
+
/** Initial hex color (default: 0xffffff) */
|
|
63
|
+
startTint?: number;
|
|
64
|
+
/** Final hex color (default: same as startTint) */
|
|
65
|
+
endTint?: number;
|
|
66
|
+
/** Initial rotation in radians (default: 0) */
|
|
67
|
+
startRotation?: ParticleValue;
|
|
68
|
+
/** Rotation velocity in rad/s (default: 0) */
|
|
69
|
+
rotationSpeed?: ParticleValue;
|
|
70
|
+
/** Blend mode (default: 'normal') */
|
|
71
|
+
blendMode?: ParticleBlendMode;
|
|
72
|
+
/** Particles in world coordinates (default: true) */
|
|
73
|
+
worldSpace?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Frozen, fully-resolved particle effect config.
|
|
77
|
+
* Output of defineParticleEffect.
|
|
78
|
+
*/
|
|
79
|
+
export interface ParticleEffectConfig {
|
|
80
|
+
readonly maxParticles: number;
|
|
81
|
+
readonly texture: unknown;
|
|
82
|
+
readonly spawnRate: number;
|
|
83
|
+
readonly burstCount: number;
|
|
84
|
+
readonly duration: number;
|
|
85
|
+
readonly lifetime: ParticleValue;
|
|
86
|
+
readonly speed: ParticleValue;
|
|
87
|
+
readonly angle: ParticleValue;
|
|
88
|
+
readonly emissionShape: EmissionShape;
|
|
89
|
+
readonly emissionRadius: number;
|
|
90
|
+
readonly gravity: {
|
|
91
|
+
readonly x: number;
|
|
92
|
+
readonly y: number;
|
|
93
|
+
};
|
|
94
|
+
readonly startSize: ParticleValue;
|
|
95
|
+
readonly endSize: ParticleValue;
|
|
96
|
+
readonly startAlpha: ParticleValue;
|
|
97
|
+
readonly endAlpha: ParticleValue;
|
|
98
|
+
readonly startTint: number;
|
|
99
|
+
readonly endTint: number;
|
|
100
|
+
readonly startRotation: ParticleValue;
|
|
101
|
+
readonly rotationSpeed: ParticleValue;
|
|
102
|
+
readonly blendMode: ParticleBlendMode;
|
|
103
|
+
readonly worldSpace: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Mutable per-particle state. Pre-allocated, never GC'd.
|
|
107
|
+
*/
|
|
108
|
+
export interface ParticleState {
|
|
109
|
+
active: boolean;
|
|
110
|
+
x: number;
|
|
111
|
+
y: number;
|
|
112
|
+
vx: number;
|
|
113
|
+
vy: number;
|
|
114
|
+
life: number;
|
|
115
|
+
maxLife: number;
|
|
116
|
+
size: number;
|
|
117
|
+
startSize: number;
|
|
118
|
+
endSize: number;
|
|
119
|
+
alpha: number;
|
|
120
|
+
startAlpha: number;
|
|
121
|
+
endAlpha: number;
|
|
122
|
+
tint: number;
|
|
123
|
+
rotation: number;
|
|
124
|
+
rotationSpeed: number;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Per-entity emitter state stored as an ECS component.
|
|
128
|
+
*/
|
|
129
|
+
export interface ParticleEmitter {
|
|
130
|
+
readonly config: ParticleEffectConfig;
|
|
131
|
+
activeCount: number;
|
|
132
|
+
spawnAccumulator: number;
|
|
133
|
+
elapsed: number;
|
|
134
|
+
playing: boolean;
|
|
135
|
+
pendingBurst: number;
|
|
136
|
+
finished: boolean;
|
|
137
|
+
onComplete?: (data: ParticleEmitterEventData) => void;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Component types provided by the particle plugin.
|
|
141
|
+
*/
|
|
142
|
+
export interface ParticleComponentTypes {
|
|
143
|
+
particleEmitter: ParticleEmitter;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Data published when an emitter completes.
|
|
147
|
+
*/
|
|
148
|
+
export interface ParticleEmitterEventData {
|
|
149
|
+
entityId: number;
|
|
150
|
+
}
|
|
151
|
+
export interface ParticlePluginOptions<G extends string = 'particles'> extends BasePluginOptions<G> {
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Sample a ParticleValue: returns fixed value or random within [min, max].
|
|
155
|
+
*/
|
|
156
|
+
export declare function sampleRange(value: ParticleValue): number;
|
|
157
|
+
/**
|
|
158
|
+
* Linear interpolation between two hex colors (RGB channels).
|
|
159
|
+
*/
|
|
160
|
+
export declare function lerpTint(start: number, end: number, t: number): number;
|
|
161
|
+
/**
|
|
162
|
+
* Define a particle effect config with defaults applied and frozen.
|
|
163
|
+
*/
|
|
164
|
+
export declare function defineParticleEffect(input: ParticleEffectInput): ParticleEffectConfig;
|
|
165
|
+
/**
|
|
166
|
+
* Create a particleEmitter component suitable for spreading into spawn().
|
|
167
|
+
*/
|
|
168
|
+
export declare function createParticleEmitter(config: ParticleEffectConfig, options?: {
|
|
169
|
+
playing?: boolean;
|
|
170
|
+
onComplete?: (data: ParticleEmitterEventData) => void;
|
|
171
|
+
}): Pick<ParticleComponentTypes, 'particleEmitter'>;
|
|
172
|
+
/**
|
|
173
|
+
* Queue a burst of particles on an emitter.
|
|
174
|
+
* Returns false if entity has no particleEmitter component.
|
|
175
|
+
*/
|
|
176
|
+
export declare function burstParticles(ecs: ParticleWorld, entityId: number, count?: number): boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Stop an emitter from spawning new particles.
|
|
179
|
+
* Existing particles continue their lifecycle.
|
|
180
|
+
*/
|
|
181
|
+
export declare function stopEmitter(ecs: ParticleWorld, entityId: number): boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Resume a stopped emitter.
|
|
184
|
+
*/
|
|
185
|
+
export declare function resumeEmitter(ecs: ParticleWorld, entityId: number): boolean;
|
|
186
|
+
/**
|
|
187
|
+
* Runtime data stored outside the ECS, keyed by entity ID.
|
|
188
|
+
*/
|
|
189
|
+
export interface EmitterRuntimeData {
|
|
190
|
+
particles: ParticleState[];
|
|
191
|
+
pixiContainer: unknown;
|
|
192
|
+
pixiParticles: unknown[];
|
|
193
|
+
}
|
|
194
|
+
export declare const particlePresets: {
|
|
195
|
+
readonly explosion: (texture: unknown, overrides?: Partial<ParticleEffectInput>) => ParticleEffectConfig;
|
|
196
|
+
readonly smoke: (texture: unknown, overrides?: Partial<ParticleEffectInput>) => ParticleEffectConfig;
|
|
197
|
+
readonly fire: (texture: unknown, overrides?: Partial<ParticleEffectInput>) => ParticleEffectConfig;
|
|
198
|
+
readonly sparkle: (texture: unknown, overrides?: Partial<ParticleEffectInput>) => ParticleEffectConfig;
|
|
199
|
+
readonly trail: (texture: unknown, overrides?: Partial<ParticleEffectInput>) => ParticleEffectConfig;
|
|
200
|
+
};
|
|
201
|
+
type ParticleLabels = 'particle-update' | 'particle-render-sync';
|
|
202
|
+
type ParticleRequires = WorldConfigFrom<TransformComponentTypes & {
|
|
203
|
+
renderLayer: string;
|
|
204
|
+
}>;
|
|
205
|
+
/**
|
|
206
|
+
* Create a particle system plugin for ECSpresso.
|
|
207
|
+
*
|
|
208
|
+
* Provides:
|
|
209
|
+
* - Pre-allocated particle pools outside the entity system
|
|
210
|
+
* - Continuous and burst emission modes
|
|
211
|
+
* - Velocity, gravity, lifetime, interpolation (size, alpha, tint, rotation)
|
|
212
|
+
* - World-space and local-space particle emission
|
|
213
|
+
* - PixiJS ParticleContainer rendering (via renderer2D dependency)
|
|
214
|
+
* - Presets for common effects (explosion, smoke, fire, sparkle, trail)
|
|
215
|
+
*
|
|
216
|
+
* Renderer2D is a required dependency.
|
|
217
|
+
*/
|
|
218
|
+
export declare function createParticlePlugin<G extends string = 'particles'>(options?: ParticlePluginOptions<G>): Plugin<WorldConfigFrom<ParticleComponentTypes>, ParticleRequires, ParticleLabels, G, never, 'particle-emitters'>;
|
|
219
|
+
/**
|
|
220
|
+
* Get the runtime data for an emitter entity.
|
|
221
|
+
* Useful for tests and advanced usage.
|
|
222
|
+
* @internal Exported for testing only.
|
|
223
|
+
*/
|
|
224
|
+
export declare function getEmitterData(emitterDataMap: Map<number, EmitterRuntimeData>, entityId: number): EmitterRuntimeData | undefined;
|
|
225
|
+
export {};
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Physics 2D
|
|
2
|
+
* Physics 2D Plugin for ECSpresso
|
|
3
3
|
*
|
|
4
4
|
* Provides ECS-native arcade physics: gravity, forces, drag, semi-implicit Euler
|
|
5
5
|
* integration, and impulse-based collision response with friction.
|
|
6
6
|
*
|
|
7
|
-
* Reuses collider types from the collision
|
|
7
|
+
* Reuses collider types from the collision plugin for shape definitions.
|
|
8
8
|
* Has its own collision detection in fixedUpdate for physics response;
|
|
9
|
-
* the existing collision
|
|
9
|
+
* the existing collision plugin can still run in postUpdate for game logic events.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { type Plugin } from 'ecspresso';
|
|
12
12
|
import type { SystemPhase } from 'ecspresso';
|
|
13
|
-
import type {
|
|
13
|
+
import type { WorldConfigFrom } from '../type-utils';
|
|
14
|
+
import type { TransformComponentTypes, TransformWorldConfig } from './transform';
|
|
14
15
|
import type { CollisionComponentTypes, LayerFactories } from './collision';
|
|
15
16
|
import type { Vector2D } from 'ecspresso';
|
|
16
17
|
/**
|
|
@@ -37,13 +38,20 @@ export interface RigidBody {
|
|
|
37
38
|
gravityScale: number;
|
|
38
39
|
}
|
|
39
40
|
/**
|
|
40
|
-
* Component types provided by the physics
|
|
41
|
+
* Component types directly provided by the physics plugin.
|
|
41
42
|
*/
|
|
42
|
-
export interface
|
|
43
|
+
export interface Physics2DOwnComponentTypes {
|
|
43
44
|
rigidBody: RigidBody;
|
|
44
45
|
velocity: Vector2D;
|
|
45
46
|
force: Vector2D;
|
|
46
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Full component types available when using the physics plugin
|
|
50
|
+
* (own components + transform + collision dependencies).
|
|
51
|
+
* Convenience alias for consumer code.
|
|
52
|
+
*/
|
|
53
|
+
export interface Physics2DComponentTypes<L extends string = never> extends TransformComponentTypes, CollisionComponentTypes<L>, Physics2DOwnComponentTypes {
|
|
54
|
+
}
|
|
47
55
|
/**
|
|
48
56
|
* Physics configuration resource.
|
|
49
57
|
*/
|
|
@@ -67,7 +75,7 @@ export interface Physics2DCollisionEvent {
|
|
|
67
75
|
export interface Physics2DEventTypes {
|
|
68
76
|
physicsCollision: Physics2DCollisionEvent;
|
|
69
77
|
}
|
|
70
|
-
export interface
|
|
78
|
+
export interface Physics2DPluginOptions<G extends string = 'physics2D', CG extends string = never> {
|
|
71
79
|
/** World gravity vector (default: {x: 0, y: 0}) */
|
|
72
80
|
gravity?: Vector2D;
|
|
73
81
|
/** System group name (default: 'physics2D') */
|
|
@@ -108,29 +116,23 @@ export declare function createForce(x: number, y: number): {
|
|
|
108
116
|
* Accumulate a force onto an entity's force component.
|
|
109
117
|
*/
|
|
110
118
|
export declare function applyForce(ecs: {
|
|
111
|
-
|
|
112
|
-
getComponent(id: number, name: 'force'): Vector2D | undefined;
|
|
113
|
-
};
|
|
119
|
+
getComponent(id: number, name: 'force'): Vector2D | undefined;
|
|
114
120
|
}, entityId: number, fx: number, fy: number): void;
|
|
115
121
|
/**
|
|
116
122
|
* Apply an instantaneous impulse: velocity += impulse / mass.
|
|
117
123
|
*/
|
|
118
124
|
export declare function applyImpulse(ecs: {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
getComponent(id: number, name: 'rigidBody'): RigidBody | undefined;
|
|
122
|
-
};
|
|
125
|
+
getComponent(id: number, name: 'velocity'): Vector2D | undefined;
|
|
126
|
+
getComponent(id: number, name: 'rigidBody'): RigidBody | undefined;
|
|
123
127
|
}, entityId: number, ix: number, iy: number): void;
|
|
124
128
|
/**
|
|
125
129
|
* Directly set an entity's velocity.
|
|
126
130
|
*/
|
|
127
131
|
export declare function setVelocity(ecs: {
|
|
128
|
-
|
|
129
|
-
getComponent(id: number, name: 'velocity'): Vector2D | undefined;
|
|
130
|
-
};
|
|
132
|
+
getComponent(id: number, name: 'velocity'): Vector2D | undefined;
|
|
131
133
|
}, entityId: number, vx: number, vy: number): void;
|
|
132
134
|
/**
|
|
133
|
-
* Create a 2D physics
|
|
135
|
+
* Create a 2D physics plugin for ECSpresso.
|
|
134
136
|
*
|
|
135
137
|
* Provides:
|
|
136
138
|
* - Semi-implicit Euler integration (gravity, forces, drag → velocity → position)
|
|
@@ -140,8 +142,8 @@ export declare function setVelocity(ecs: {
|
|
|
140
142
|
* @example
|
|
141
143
|
* ```typescript
|
|
142
144
|
* const ecs = ECSpresso.create()
|
|
143
|
-
* .
|
|
144
|
-
* .
|
|
145
|
+
* .withPlugin(createTransformPlugin())
|
|
146
|
+
* .withPlugin(createPhysics2DPlugin({ gravity: { x: 0, y: 980 } }))
|
|
145
147
|
* .withFixedTimestep(1/60)
|
|
146
148
|
* .build();
|
|
147
149
|
*
|
|
@@ -154,6 +156,8 @@ export declare function setVelocity(ecs: {
|
|
|
154
156
|
* });
|
|
155
157
|
* ```
|
|
156
158
|
*/
|
|
157
|
-
|
|
159
|
+
type Physics2DProvides<L extends string = never> = Physics2DOwnComponentTypes & CollisionComponentTypes<L>;
|
|
160
|
+
export declare function createPhysics2DPlugin<L extends string = never, G extends string = 'physics2D', CG extends string = never>(options?: Physics2DPluginOptions<G, CG> & {
|
|
158
161
|
layers?: LayerFactories<Record<L, readonly string[]>>;
|
|
159
|
-
}):
|
|
162
|
+
}): Plugin<WorldConfigFrom<Physics2DProvides<L>, Physics2DEventTypes, Physics2DResourceTypes>, TransformWorldConfig, 'physics2D-integration' | 'physics2D-collision', G | CG>;
|
|
163
|
+
export {};
|