ecspresso 0.10.2 → 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 +256 -148
- package/dist/asset-manager.d.ts +16 -16
- package/dist/asset-types.d.ts +18 -16
- package/dist/command-buffer.d.ts +30 -20
- package/dist/ecspresso-builder.d.ts +193 -0
- package/dist/ecspresso.d.ts +323 -209
- package/dist/entity-manager.d.ts +76 -30
- package/dist/event-bus.d.ts +6 -1
- package/dist/index.d.ts +6 -13
- package/dist/plugin.d.ts +61 -0
- package/dist/plugins/audio.d.ts +273 -0
- package/dist/{bundles/utils → plugins}/bounds.d.ts +20 -26
- package/dist/plugins/camera.d.ts +88 -0
- package/dist/plugins/collision.d.ts +285 -0
- package/dist/plugins/coroutine.d.ts +126 -0
- package/dist/plugins/diagnostics.d.ts +49 -0
- package/dist/{bundles/utils → plugins}/input.d.ts +22 -29
- package/dist/plugins/particles.d.ts +225 -0
- package/dist/plugins/physics2D.d.ts +163 -0
- package/dist/plugins/renderers/renderer2D.d.ts +262 -0
- package/dist/plugins/spatial-index.d.ts +58 -0
- package/dist/plugins/sprite-animation.d.ts +150 -0
- package/dist/plugins/state-machine.d.ts +244 -0
- package/dist/plugins/timers.d.ts +151 -0
- package/dist/{bundles/utils → plugins}/transform.d.ts +21 -22
- package/dist/plugins/tween.d.ts +162 -0
- package/dist/reactive-query-manager.d.ts +14 -3
- package/dist/resource-manager.d.ts +64 -23
- package/dist/screen-manager.d.ts +21 -15
- package/dist/screen-types.d.ts +15 -11
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +25 -0
- 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 +75 -112
- package/dist/type-utils.d.ts +247 -7
- package/dist/types.d.ts +58 -39
- package/dist/utils/check-required-cycle.d.ts +12 -0
- package/dist/utils/easing.d.ts +71 -0
- package/dist/utils/math.d.ts +67 -0
- package/dist/utils/narrowphase.d.ts +63 -0
- package/dist/utils/spatial-hash.d.ts +53 -0
- package/package.json +65 -27
- package/dist/bundle.d.ts +0 -123
- package/dist/bundles/renderers/renderer2D.d.ts +0 -220
- package/dist/bundles/renderers/renderer2D.js +0 -4
- package/dist/bundles/renderers/renderer2D.js.map +0 -10
- package/dist/bundles/utils/bounds.js +0 -4
- package/dist/bundles/utils/bounds.js.map +0 -10
- package/dist/bundles/utils/collision.d.ts +0 -204
- package/dist/bundles/utils/collision.js +0 -4
- package/dist/bundles/utils/collision.js.map +0 -10
- package/dist/bundles/utils/input.js +0 -4
- package/dist/bundles/utils/input.js.map +0 -10
- package/dist/bundles/utils/movement.d.ts +0 -86
- package/dist/bundles/utils/movement.js +0 -4
- package/dist/bundles/utils/movement.js.map +0 -10
- package/dist/bundles/utils/timers.d.ts +0 -172
- package/dist/bundles/utils/timers.js +0 -4
- package/dist/bundles/utils/timers.js.map +0 -10
- package/dist/bundles/utils/transform.js +0 -4
- package/dist/bundles/utils/transform.js.map +0 -10
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -22
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bounds
|
|
2
|
+
* Bounds Plugin for ECSpresso
|
|
3
3
|
*
|
|
4
4
|
* Provides screen bounds enforcement for entities with transforms.
|
|
5
5
|
* Reads worldTransform for position checking; modifies localTransform for corrections.
|
|
6
6
|
* Supports destroy, clamp, and wrap behaviors.
|
|
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
|
* Component that marks an entity for destruction when outside bounds.
|
|
13
13
|
*/
|
|
@@ -30,14 +30,16 @@ export interface WrapAtBounds {
|
|
|
30
30
|
padding?: number;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* Component types provided by the bounds
|
|
34
|
-
*
|
|
33
|
+
* Component types provided by the bounds plugin.
|
|
34
|
+
* Included automatically via `.withPlugin(createBoundsPlugin())`.
|
|
35
35
|
*
|
|
36
36
|
* @example
|
|
37
37
|
* ```typescript
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* }
|
|
38
|
+
* const ecs = ECSpresso.create()
|
|
39
|
+
* .withPlugin(createTransformPlugin())
|
|
40
|
+
* .withPlugin(createBoundsPlugin({ width: 800, height: 600 }))
|
|
41
|
+
* .withComponentTypes<{ sprite: Sprite }>()
|
|
42
|
+
* .build();
|
|
41
43
|
* ```
|
|
42
44
|
*/
|
|
43
45
|
export interface BoundsComponentTypes {
|
|
@@ -59,7 +61,7 @@ export interface BoundsRect {
|
|
|
59
61
|
height: number;
|
|
60
62
|
}
|
|
61
63
|
/**
|
|
62
|
-
* Resource types provided by the bounds
|
|
64
|
+
* Resource types provided by the bounds plugin.
|
|
63
65
|
*/
|
|
64
66
|
export interface BoundsResourceTypes {
|
|
65
67
|
bounds: BoundsRect;
|
|
@@ -74,25 +76,19 @@ export interface EntityOutOfBoundsEvent {
|
|
|
74
76
|
exitEdge: 'top' | 'bottom' | 'left' | 'right';
|
|
75
77
|
}
|
|
76
78
|
/**
|
|
77
|
-
* Event types provided by the bounds
|
|
79
|
+
* Event types provided by the bounds plugin.
|
|
78
80
|
*/
|
|
79
81
|
export interface BoundsEventTypes {
|
|
80
82
|
entityOutOfBounds: EntityOutOfBoundsEvent;
|
|
81
83
|
}
|
|
82
84
|
/**
|
|
83
|
-
* Configuration options for the bounds
|
|
85
|
+
* Configuration options for the bounds plugin.
|
|
84
86
|
*/
|
|
85
|
-
export interface
|
|
86
|
-
/** System group name (default: 'physics') */
|
|
87
|
-
systemGroup?: string;
|
|
88
|
-
/** Priority for bounds systems (default: 50) */
|
|
89
|
-
priority?: number;
|
|
87
|
+
export interface BoundsPluginOptions<G extends string = 'physics'> extends BasePluginOptions<G> {
|
|
90
88
|
/** Resource key for bounds rectangle (default: 'bounds') */
|
|
91
89
|
boundsResourceKey?: string;
|
|
92
90
|
/** Whether to auto-remove entities when out of bounds (default: true) */
|
|
93
91
|
autoRemove?: boolean;
|
|
94
|
-
/** Execution phase (default: 'postUpdate') */
|
|
95
|
-
phase?: SystemPhase;
|
|
96
92
|
}
|
|
97
93
|
/**
|
|
98
94
|
* Create a bounds rectangle resource.
|
|
@@ -156,11 +152,10 @@ export declare function createClampToBounds(margin?: number): Pick<BoundsCompone
|
|
|
156
152
|
* ```
|
|
157
153
|
*/
|
|
158
154
|
export declare function createWrapAtBounds(padding?: number): Pick<BoundsComponentTypes, 'wrapAtBounds'>;
|
|
159
|
-
type CombinedComponentTypes = BoundsComponentTypes & TransformComponentTypes;
|
|
160
155
|
/**
|
|
161
|
-
* Create a bounds
|
|
156
|
+
* Create a bounds plugin for ECSpresso.
|
|
162
157
|
*
|
|
163
|
-
* This
|
|
158
|
+
* This plugin provides:
|
|
164
159
|
* - Destroy out of bounds system - removes entities that exit bounds
|
|
165
160
|
* - Clamp to bounds system - constrains entities within bounds
|
|
166
161
|
* - Wrap at bounds system - wraps entities to opposite edge
|
|
@@ -174,8 +169,8 @@ type CombinedComponentTypes = BoundsComponentTypes & TransformComponentTypes;
|
|
|
174
169
|
* const ecs = ECSpresso
|
|
175
170
|
* .create<Components, Events, Resources>()
|
|
176
171
|
* .withResource('bounds', createBounds(800, 600))
|
|
177
|
-
* .
|
|
178
|
-
* .
|
|
172
|
+
* .withPlugin(createTransformPlugin())
|
|
173
|
+
* .withPlugin(createBoundsPlugin())
|
|
179
174
|
* .build();
|
|
180
175
|
*
|
|
181
176
|
* // Entity that gets destroyed when leaving screen
|
|
@@ -185,5 +180,4 @@ type CombinedComponentTypes = BoundsComponentTypes & TransformComponentTypes;
|
|
|
185
180
|
* });
|
|
186
181
|
* ```
|
|
187
182
|
*/
|
|
188
|
-
export declare function
|
|
189
|
-
export {};
|
|
183
|
+
export declare function createBoundsPlugin<ResourceTypes extends BoundsResourceTypes = BoundsResourceTypes, G extends string = 'physics'>(options?: BoundsPluginOptions<G>): Plugin<WorldConfigFrom<BoundsComponentTypes, BoundsEventTypes, ResourceTypes>, TransformWorldConfig, 'bounds-destroy' | 'bounds-clamp' | 'bounds-wrap', G>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Camera / Viewport Plugin for ECSpresso
|
|
3
|
+
*
|
|
4
|
+
* Provides a camera entity with world/screen coordinate conversion, smooth follow,
|
|
5
|
+
* trauma-based shake, bounds clamping, and logical viewport dimensions.
|
|
6
|
+
*
|
|
7
|
+
* This plugin is renderer-agnostic. PixiJS or other renderer integration (applying
|
|
8
|
+
* cameraState to a container/stage transform) is the consumer's responsibility.
|
|
9
|
+
*
|
|
10
|
+
* Camera uses its own x/y/zoom/rotation rather than localTransform/worldTransform.
|
|
11
|
+
* It reads the target entity's worldTransform for follow, but doesn't participate
|
|
12
|
+
* in the transform hierarchy itself.
|
|
13
|
+
*/
|
|
14
|
+
import { type Plugin } from 'ecspresso';
|
|
15
|
+
import type { SystemPhase } from 'ecspresso';
|
|
16
|
+
import type ECSpresso from 'ecspresso';
|
|
17
|
+
import type { WorldConfigFrom } from '../type-utils';
|
|
18
|
+
import type { TransformWorldConfig } from './transform';
|
|
19
|
+
export interface Camera {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
zoom: number;
|
|
23
|
+
rotation: number;
|
|
24
|
+
}
|
|
25
|
+
export interface CameraFollow {
|
|
26
|
+
target: number;
|
|
27
|
+
smoothing: number;
|
|
28
|
+
deadzoneX: number;
|
|
29
|
+
deadzoneY: number;
|
|
30
|
+
offsetX: number;
|
|
31
|
+
offsetY: number;
|
|
32
|
+
}
|
|
33
|
+
export interface CameraShake {
|
|
34
|
+
trauma: number;
|
|
35
|
+
traumaDecay: number;
|
|
36
|
+
maxOffsetX: number;
|
|
37
|
+
maxOffsetY: number;
|
|
38
|
+
maxRotation: number;
|
|
39
|
+
}
|
|
40
|
+
export interface CameraBounds {
|
|
41
|
+
minX: number;
|
|
42
|
+
minY: number;
|
|
43
|
+
maxX: number;
|
|
44
|
+
maxY: number;
|
|
45
|
+
}
|
|
46
|
+
export interface CameraComponentTypes {
|
|
47
|
+
camera: Camera;
|
|
48
|
+
cameraFollow: CameraFollow;
|
|
49
|
+
cameraShake: CameraShake;
|
|
50
|
+
cameraBounds: CameraBounds;
|
|
51
|
+
}
|
|
52
|
+
export interface CameraState {
|
|
53
|
+
x: number;
|
|
54
|
+
y: number;
|
|
55
|
+
zoom: number;
|
|
56
|
+
rotation: number;
|
|
57
|
+
shakeOffsetX: number;
|
|
58
|
+
shakeOffsetY: number;
|
|
59
|
+
shakeRotation: number;
|
|
60
|
+
viewportWidth: number;
|
|
61
|
+
viewportHeight: number;
|
|
62
|
+
}
|
|
63
|
+
export interface CameraResourceTypes {
|
|
64
|
+
cameraState: CameraState;
|
|
65
|
+
}
|
|
66
|
+
export interface CameraPluginOptions<G extends string = 'camera'> {
|
|
67
|
+
viewportWidth?: number;
|
|
68
|
+
viewportHeight?: number;
|
|
69
|
+
systemGroup?: G;
|
|
70
|
+
phase?: SystemPhase;
|
|
71
|
+
randomFn?: () => number;
|
|
72
|
+
}
|
|
73
|
+
export declare const DEFAULT_CAMERA: Readonly<Camera>;
|
|
74
|
+
export declare const DEFAULT_CAMERA_STATE: Readonly<CameraState>;
|
|
75
|
+
export declare function createCamera(x?: number, y?: number, zoom?: number, rotation?: number): Pick<CameraComponentTypes, 'camera'>;
|
|
76
|
+
export declare function createCameraFollow(target: number, options?: Partial<Omit<CameraFollow, 'target'>>): Pick<CameraComponentTypes, 'cameraFollow'>;
|
|
77
|
+
export declare function createCameraShake(options?: Partial<CameraShake>): Pick<CameraComponentTypes, 'cameraShake'>;
|
|
78
|
+
export declare function createCameraBounds(minX: number, minY: number, maxX: number, maxY: number): Pick<CameraComponentTypes, 'cameraBounds'>;
|
|
79
|
+
export declare function addTrauma<Cfg extends WorldConfigFrom<CameraComponentTypes, {}, CameraResourceTypes>>(ecs: ECSpresso<Cfg>, entityId: number, amount: number): void;
|
|
80
|
+
export declare function worldToScreen(worldX: number, worldY: number, state: CameraState): {
|
|
81
|
+
x: number;
|
|
82
|
+
y: number;
|
|
83
|
+
};
|
|
84
|
+
export declare function screenToWorld(screenX: number, screenY: number, state: CameraState): {
|
|
85
|
+
x: number;
|
|
86
|
+
y: number;
|
|
87
|
+
};
|
|
88
|
+
export declare function createCameraPlugin<G extends string = 'camera'>(options?: CameraPluginOptions<G>): Plugin<WorldConfigFrom<CameraComponentTypes, {}, CameraResourceTypes>, TransformWorldConfig, 'camera-follow' | 'camera-shake-update' | 'camera-bounds' | 'camera-state-sync', G>;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collision Plugin for ECSpresso
|
|
3
|
+
*
|
|
4
|
+
* Provides layer-based collision detection with events.
|
|
5
|
+
* Uses worldTransform for position (world-space collision).
|
|
6
|
+
* Supports AABB and circle colliders.
|
|
7
|
+
*/
|
|
8
|
+
import { type Plugin, type BasePluginOptions } from 'ecspresso';
|
|
9
|
+
import type { WorldConfigFrom } from '../type-utils';
|
|
10
|
+
import type { TransformWorldConfig } from './transform';
|
|
11
|
+
/**
|
|
12
|
+
* Axis-Aligned Bounding Box collider.
|
|
13
|
+
*/
|
|
14
|
+
export interface AABBCollider {
|
|
15
|
+
/** Width of the bounding box */
|
|
16
|
+
width: number;
|
|
17
|
+
/** Height of the bounding box */
|
|
18
|
+
height: number;
|
|
19
|
+
/** X offset from entity position (default: 0) */
|
|
20
|
+
offsetX?: number;
|
|
21
|
+
/** Y offset from entity position (default: 0) */
|
|
22
|
+
offsetY?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Circle collider.
|
|
26
|
+
*/
|
|
27
|
+
export interface CircleCollider {
|
|
28
|
+
/** Radius of the circle */
|
|
29
|
+
radius: number;
|
|
30
|
+
/** X offset from entity position (default: 0) */
|
|
31
|
+
offsetX?: number;
|
|
32
|
+
/** Y offset from entity position (default: 0) */
|
|
33
|
+
offsetY?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Collision layer configuration.
|
|
37
|
+
*/
|
|
38
|
+
export interface CollisionLayer<L extends string = never> {
|
|
39
|
+
/** The layer this entity belongs to */
|
|
40
|
+
layer: L;
|
|
41
|
+
/** Layers this entity can collide with */
|
|
42
|
+
collidesWith: readonly L[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Component types provided by the collision plugin.
|
|
46
|
+
* Included automatically via `.withPlugin(createCollisionPlugin())`.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const ecs = ECSpresso.create()
|
|
51
|
+
* .withPlugin(createCollisionPlugin())
|
|
52
|
+
* .withComponentTypes<{ sprite: Sprite; enemy: boolean }>()
|
|
53
|
+
* .build();
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export interface CollisionComponentTypes<L extends string = never> {
|
|
57
|
+
aabbCollider: AABBCollider;
|
|
58
|
+
circleCollider: CircleCollider;
|
|
59
|
+
collisionLayer: CollisionLayer<L>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Event fired when two entities collide.
|
|
63
|
+
*/
|
|
64
|
+
export interface CollisionEvent<L extends string = never> {
|
|
65
|
+
/** First entity in the collision */
|
|
66
|
+
entityA: number;
|
|
67
|
+
/** Second entity in the collision */
|
|
68
|
+
entityB: number;
|
|
69
|
+
/** Layer of the first entity */
|
|
70
|
+
layerA: L;
|
|
71
|
+
/** Layer of the second entity */
|
|
72
|
+
layerB: L;
|
|
73
|
+
/** Contact normal pointing from entityA toward entityB */
|
|
74
|
+
normal: {
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
};
|
|
78
|
+
/** Penetration depth (positive = overlapping) */
|
|
79
|
+
depth: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Event types provided by the collision plugin.
|
|
83
|
+
*/
|
|
84
|
+
export interface CollisionEventTypes<L extends string = never> {
|
|
85
|
+
collision: CollisionEvent<L>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Configuration options for the collision plugin.
|
|
89
|
+
*/
|
|
90
|
+
export interface CollisionPluginOptions<G extends string = 'physics'> extends BasePluginOptions<G> {
|
|
91
|
+
/** Name of the collision event (default: 'collision') */
|
|
92
|
+
collisionEventName?: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Create an AABB collider component.
|
|
96
|
+
*
|
|
97
|
+
* @param width Width of the bounding box
|
|
98
|
+
* @param height Height of the bounding box
|
|
99
|
+
* @param offsetX X offset from entity position
|
|
100
|
+
* @param offsetY Y offset from entity position
|
|
101
|
+
* @returns Component object suitable for spreading into spawn()
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* ecs.spawn({
|
|
106
|
+
* ...createTransform(100, 200),
|
|
107
|
+
* ...createAABBCollider(50, 30),
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export declare function createAABBCollider(width: number, height: number, offsetX?: number, offsetY?: number): {
|
|
112
|
+
aabbCollider: AABBCollider;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Create a circle collider component.
|
|
116
|
+
*
|
|
117
|
+
* @param radius Radius of the circle
|
|
118
|
+
* @param offsetX X offset from entity position
|
|
119
|
+
* @param offsetY Y offset from entity position
|
|
120
|
+
* @returns Component object suitable for spreading into spawn()
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* ecs.spawn({
|
|
125
|
+
* ...createTransform(100, 200),
|
|
126
|
+
* ...createCircleCollider(25),
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export declare function createCircleCollider(radius: number, offsetX?: number, offsetY?: number): {
|
|
131
|
+
circleCollider: CircleCollider;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Create a collision layer component.
|
|
135
|
+
*
|
|
136
|
+
* @param layer The layer this entity belongs to
|
|
137
|
+
* @param collidesWith Layers this entity can collide with
|
|
138
|
+
* @returns Component object suitable for spreading into spawn()
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* ecs.spawn({
|
|
143
|
+
* ...createTransform(100, 200),
|
|
144
|
+
* ...createAABBCollider(50, 30),
|
|
145
|
+
* ...createCollisionLayer('player', ['enemy', 'obstacle']),
|
|
146
|
+
* });
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export declare function createCollisionLayer<L extends string>(layer: L, collidesWith: readonly L[]): Pick<CollisionComponentTypes<L>, 'collisionLayer'>;
|
|
150
|
+
/**
|
|
151
|
+
* Layer factory result from defineCollisionLayers.
|
|
152
|
+
*/
|
|
153
|
+
export type LayerFactories<T extends Record<string, readonly string[]>> = {
|
|
154
|
+
[K in keyof T]: () => Pick<CollisionComponentTypes<Extract<keyof T, string>>, 'collisionLayer'>;
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Extract layer names from a `defineCollisionLayers` result for use with
|
|
158
|
+
* `createCollisionPairHandler`'s `L` type parameter.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });
|
|
163
|
+
* type Layer = LayersOf<typeof layers>;
|
|
164
|
+
* const handler = createCollisionPairHandler<ECS, Layer>({
|
|
165
|
+
* 'player:enemy': (playerId, enemyId, ecs) => { ... },
|
|
166
|
+
* });
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export type LayersOf<T> = Extract<keyof T, string>;
|
|
170
|
+
/**
|
|
171
|
+
* Define collision layer relationships and get factory functions.
|
|
172
|
+
*
|
|
173
|
+
* @param rules Object mapping layer names to arrays of layers they collide with
|
|
174
|
+
* @returns Object with factory functions for each layer
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* const layers = defineCollisionLayers({
|
|
179
|
+
* player: ['enemy', 'enemyProjectile'],
|
|
180
|
+
* playerProjectile: ['enemy'],
|
|
181
|
+
* enemy: ['playerProjectile'],
|
|
182
|
+
* enemyProjectile: ['player'],
|
|
183
|
+
* });
|
|
184
|
+
*
|
|
185
|
+
* // Usage
|
|
186
|
+
* ecs.spawn({
|
|
187
|
+
* ...createTransform(100, 200),
|
|
188
|
+
* ...createAABBCollider(50, 30),
|
|
189
|
+
* ...layers.player(),
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
/**
|
|
194
|
+
* Validates that all `collidesWith` values reference actual layer keys.
|
|
195
|
+
* Catches typos at compile time.
|
|
196
|
+
*/
|
|
197
|
+
type ValidateCollidesWith<T> = {
|
|
198
|
+
[K in keyof T]: T[K] extends readonly (infer V)[] ? [V] extends [Extract<keyof T, string>] ? T[K] : readonly Extract<keyof T, string>[] : never;
|
|
199
|
+
};
|
|
200
|
+
export declare function defineCollisionLayers<const T extends Record<string, readonly string[]>>(rules: T & ValidateCollidesWith<T>): LayerFactories<T>;
|
|
201
|
+
/**
|
|
202
|
+
* Callback for a collision pair handler.
|
|
203
|
+
*
|
|
204
|
+
* @param firstEntityId Entity belonging to the first layer in the pair key
|
|
205
|
+
* @param secondEntityId Entity belonging to the second layer in the pair key
|
|
206
|
+
* @param ecs The ECS world instance (passed through from the subscriber)
|
|
207
|
+
*/
|
|
208
|
+
export type CollisionPairCallback<W = unknown> = (firstEntityId: number, secondEntityId: number, ecs: W) => void;
|
|
209
|
+
/**
|
|
210
|
+
* Create a collision pair handler that routes collision events to
|
|
211
|
+
* layer-pair-specific callbacks.
|
|
212
|
+
*
|
|
213
|
+
* Registering `"a:b"` automatically handles both `(layerA=a, layerB=b)` and
|
|
214
|
+
* `(layerA=b, layerB=a)`. Entity arguments are swapped to match the declared
|
|
215
|
+
* key order. If both `"a:b"` and `"b:a"` are explicitly registered, each gets
|
|
216
|
+
* its own handler with no implicit reverse.
|
|
217
|
+
*
|
|
218
|
+
* @typeParam W - The ECS world type (e.g. `ECSpresso<C, E, R>`). Defaults to `unknown`.
|
|
219
|
+
* @typeParam L - Union of valid layer names. Defaults to `string`.
|
|
220
|
+
* Provide specific layer names for compile-time key validation:
|
|
221
|
+
* `createCollisionPairHandler<ECS, keyof typeof layers>({...})`
|
|
222
|
+
*
|
|
223
|
+
* @param pairs Object mapping `"layerA:layerB"` keys to callbacks
|
|
224
|
+
* @returns A dispatch function to call with collision event data and ECS instance
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* // Basic usage:
|
|
229
|
+
* const handler = createCollisionPairHandler<ECS>({
|
|
230
|
+
* 'playerProjectile:enemy': (projectileId, enemyId, ecs) => {
|
|
231
|
+
* ecs.commands.removeEntity(projectileId);
|
|
232
|
+
* },
|
|
233
|
+
* });
|
|
234
|
+
*
|
|
235
|
+
* // With layer name validation:
|
|
236
|
+
* const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });
|
|
237
|
+
* type Layer = LayersOf<typeof layers>;
|
|
238
|
+
* const handler = createCollisionPairHandler<ECS, Layer>({
|
|
239
|
+
* 'player:enemy': (playerId, enemyId, ecs) => { ... },
|
|
240
|
+
* });
|
|
241
|
+
*
|
|
242
|
+
* ecs.eventBus.subscribe('collision', (data) => handler({ data, ecs }));
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
export declare function createCollisionPairHandler<W = unknown, L extends string = string>(pairs: {
|
|
246
|
+
[K in `${L}:${L}`]?: CollisionPairCallback<W>;
|
|
247
|
+
}): (ctx: {
|
|
248
|
+
data: CollisionEvent<L>;
|
|
249
|
+
ecs: W;
|
|
250
|
+
}) => void;
|
|
251
|
+
/**
|
|
252
|
+
* Create a collision plugin for ECSpresso.
|
|
253
|
+
*
|
|
254
|
+
* This plugin provides:
|
|
255
|
+
* - Collision detection between entities with colliders
|
|
256
|
+
* - AABB-AABB, circle-circle, and AABB-circle collision
|
|
257
|
+
* - Layer-based filtering for collision pairs
|
|
258
|
+
* - Deduplication of A-B / B-A collisions
|
|
259
|
+
* - Automatic broadphase acceleration when spatialIndex resource is present
|
|
260
|
+
*
|
|
261
|
+
* Uses worldTransform for position (world-space collision detection).
|
|
262
|
+
* The `layers` parameter is required for type inference — at runtime the
|
|
263
|
+
* plugin does not consume it.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* const layers = defineCollisionLayers({ player: ['enemy'], enemy: ['player'] });
|
|
268
|
+
* const ecs = ECSpresso
|
|
269
|
+
* .create()
|
|
270
|
+
* .withPlugin(createTransformPlugin())
|
|
271
|
+
* .withPlugin(createCollisionPlugin({ layers }))
|
|
272
|
+
* .build();
|
|
273
|
+
*
|
|
274
|
+
* // Entity with collision
|
|
275
|
+
* ecs.spawn({
|
|
276
|
+
* ...createTransform(100, 200),
|
|
277
|
+
* ...createAABBCollider(50, 30),
|
|
278
|
+
* ...layers.player(),
|
|
279
|
+
* });
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
export declare function createCollisionPlugin<L extends string, G extends string = 'physics'>(options: CollisionPluginOptions<G> & {
|
|
283
|
+
layers: LayerFactories<Record<L, readonly string[]>>;
|
|
284
|
+
}): Plugin<WorldConfigFrom<CollisionComponentTypes<L>, CollisionEventTypes<L>>, TransformWorldConfig, 'collision-detection', G>;
|
|
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>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostics Plugin for ECSpresso
|
|
3
|
+
*
|
|
4
|
+
* Runtime diagnostics: FPS, entity count, per-system timing, per-phase timing,
|
|
5
|
+
* and an optional DOM overlay for visual debugging.
|
|
6
|
+
*/
|
|
7
|
+
import { type Plugin } from 'ecspresso';
|
|
8
|
+
import type { SystemPhase } from 'ecspresso';
|
|
9
|
+
import type { WorldConfigFrom, EmptyConfig } from '../type-utils';
|
|
10
|
+
export interface DiagnosticsData {
|
|
11
|
+
fps: number;
|
|
12
|
+
entityCount: number;
|
|
13
|
+
systemTimings: ReadonlyMap<string, number>;
|
|
14
|
+
phaseTimings: Readonly<Record<SystemPhase, number>>;
|
|
15
|
+
averageFrameTime: number;
|
|
16
|
+
}
|
|
17
|
+
export interface DiagnosticsResourceTypes {
|
|
18
|
+
diagnostics: DiagnosticsData;
|
|
19
|
+
}
|
|
20
|
+
export interface DiagnosticsPluginOptions<G extends string = 'diagnostics'> {
|
|
21
|
+
/** System group name (default: 'diagnostics') */
|
|
22
|
+
systemGroup?: G;
|
|
23
|
+
/** Enable timing collection on initialize (default: true) */
|
|
24
|
+
enableTimingOnInit?: boolean;
|
|
25
|
+
/** Number of frames to sample for FPS average (default: 60) */
|
|
26
|
+
fpsSampleCount?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface DiagnosticsOverlayOptions {
|
|
29
|
+
/** Corner position (default: 'top-left') */
|
|
30
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
31
|
+
/** Milliseconds between DOM updates (default: 200) */
|
|
32
|
+
updateInterval?: number;
|
|
33
|
+
/** Show per-system timings (default: true) */
|
|
34
|
+
showSystemTimings?: boolean;
|
|
35
|
+
/** Maximum systems to show in overlay (default: 10) */
|
|
36
|
+
maxSystemsShown?: number;
|
|
37
|
+
}
|
|
38
|
+
export declare function createDiagnosticsPlugin<G extends string = 'diagnostics'>(options?: DiagnosticsPluginOptions<G>): Plugin<WorldConfigFrom<{}, {}, DiagnosticsResourceTypes>, EmptyConfig, 'diagnostics-collect', G>;
|
|
39
|
+
/**
|
|
40
|
+
* Create a DOM overlay that displays diagnostics data.
|
|
41
|
+
* Returns a cleanup function that removes the element and clears the interval.
|
|
42
|
+
*
|
|
43
|
+
* @param ecs An ECSpresso instance with the diagnostics resource
|
|
44
|
+
* @param options Overlay configuration
|
|
45
|
+
* @returns Cleanup function
|
|
46
|
+
*/
|
|
47
|
+
export declare function createDiagnosticsOverlay<R extends DiagnosticsResourceTypes>(ecs: {
|
|
48
|
+
getResource<K extends keyof R>(key: K): R[K];
|
|
49
|
+
}, options?: DiagnosticsOverlayOptions): () => void;
|