ecspresso 0.6.0 → 0.7.1
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 +187 -0
- package/dist/bundle.d.ts +2 -21
- package/dist/bundles/renderers/pixi.d.ts +13 -29
- package/dist/bundles/renderers/pixi.js +2 -2
- package/dist/bundles/renderers/pixi.js.map +6 -5
- package/dist/bundles/utils/bounds.d.ts +186 -0
- package/dist/bundles/utils/collision.d.ts +201 -0
- package/dist/bundles/utils/movement.d.ts +83 -0
- package/dist/bundles/utils/timers.d.ts +67 -11
- package/dist/bundles/utils/timers.js +2 -2
- package/dist/bundles/utils/timers.js.map +4 -4
- package/dist/bundles/utils/transform.d.ts +148 -0
- package/dist/command-buffer.d.ts +90 -0
- package/dist/ecspresso.d.ts +15 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +7 -6
- package/dist/type-utils.d.ts +9 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,6 +18,8 @@ A type-safe, modular, and extensible Entity Component System (ECS) framework for
|
|
|
18
18
|
- **Reactive Queries**: Enter/exit callbacks when entities match or unmatch queries
|
|
19
19
|
- **System Groups**: Enable/disable groups of systems at runtime
|
|
20
20
|
- **Component Lifecycle**: Callbacks for component add/remove with unsubscribe support
|
|
21
|
+
- **Command Buffer**: Deferred structural changes for safe entity/component operations during systems
|
|
22
|
+
- **Timer Bundle**: ECS-native timers with event-based completion notifications
|
|
21
23
|
|
|
22
24
|
## Installation
|
|
23
25
|
|
|
@@ -967,6 +969,191 @@ const removed = world.removeReactiveQuery('enemies'); // returns true if existed
|
|
|
967
969
|
|
|
968
970
|
**Note:** Component replacement (calling `addComponent` with a component that already exists) does NOT trigger enter/exit callbacks since the entity's query match status doesn't change.
|
|
969
971
|
|
|
972
|
+
## Command Buffer
|
|
973
|
+
|
|
974
|
+
The command buffer allows you to queue structural changes (entity creation, removal, component changes) that execute at the end of the update cycle. This prevents issues when modifying entities during system iteration.
|
|
975
|
+
|
|
976
|
+
```typescript
|
|
977
|
+
// Queue commands during system execution
|
|
978
|
+
world.addSystem('combat')
|
|
979
|
+
.addQuery('enemies', { with: ['enemy', 'health'] })
|
|
980
|
+
.setProcess((queries, dt, ecs) => {
|
|
981
|
+
for (const entity of queries.enemies) {
|
|
982
|
+
if (entity.components.health.value <= 0) {
|
|
983
|
+
// Queue removal - doesn't execute immediately
|
|
984
|
+
ecs.commands.removeEntity(entity.id);
|
|
985
|
+
|
|
986
|
+
// Queue spawning an explosion
|
|
987
|
+
ecs.commands.spawn({
|
|
988
|
+
position: entity.components.position,
|
|
989
|
+
explosion: true,
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
// Commands execute automatically at end of update()
|
|
994
|
+
})
|
|
995
|
+
.build();
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
### Available Commands
|
|
999
|
+
|
|
1000
|
+
```typescript
|
|
1001
|
+
// Entity operations
|
|
1002
|
+
ecs.commands.spawn({ position: { x: 0, y: 0 } });
|
|
1003
|
+
ecs.commands.spawnChild(parentId, { position: { x: 10, y: 0 } });
|
|
1004
|
+
ecs.commands.removeEntity(entityId);
|
|
1005
|
+
ecs.commands.removeEntity(entityId, { cascade: false }); // Orphan children
|
|
1006
|
+
|
|
1007
|
+
// Component operations
|
|
1008
|
+
ecs.commands.addComponent(entityId, 'velocity', { x: 5, y: 0 });
|
|
1009
|
+
ecs.commands.addComponents(entityId, { velocity: { x: 5, y: 0 }, health: { value: 100 } });
|
|
1010
|
+
ecs.commands.removeComponent(entityId, 'velocity');
|
|
1011
|
+
|
|
1012
|
+
// Hierarchy operations
|
|
1013
|
+
ecs.commands.setParent(childId, parentId);
|
|
1014
|
+
ecs.commands.removeParent(childId);
|
|
1015
|
+
|
|
1016
|
+
// Utility
|
|
1017
|
+
ecs.commands.length; // Number of queued commands
|
|
1018
|
+
ecs.commands.clear(); // Discard all queued commands
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
Commands execute in FIFO order. If a command fails (e.g., entity doesn't exist), it logs a warning and continues with remaining commands.
|
|
1022
|
+
|
|
1023
|
+
## Built-in Bundles
|
|
1024
|
+
|
|
1025
|
+
ECSpresso provides optional utility bundles for common game development needs:
|
|
1026
|
+
|
|
1027
|
+
| Bundle | Import | Description |
|
|
1028
|
+
|--------|--------|-------------|
|
|
1029
|
+
| **Transform** | `ecspresso/bundles/utils/transform` | Hierarchical transform propagation (local/world transforms) |
|
|
1030
|
+
| **Movement** | `ecspresso/bundles/utils/movement` | Velocity-based movement integration |
|
|
1031
|
+
| **Bounds** | `ecspresso/bundles/utils/bounds` | Screen bounds enforcement (destroy, clamp, wrap) |
|
|
1032
|
+
| **Collision** | `ecspresso/bundles/utils/collision` | Layer-based AABB/circle collision detection with events |
|
|
1033
|
+
| **Timers** | `ecspresso/bundles/utils/timers` | ECS-native timers with event-based completion |
|
|
1034
|
+
| **PixiJS Renderer** | `ecspresso/bundles/renderers/pixi` | Automated PixiJS scene graph wiring |
|
|
1035
|
+
|
|
1036
|
+
## Timer Bundle
|
|
1037
|
+
|
|
1038
|
+
The timer bundle provides ECS-native timers that follow the "data, not callbacks" philosophy. Timers are components processed each frame, with optional event-based completion notifications.
|
|
1039
|
+
|
|
1040
|
+
```typescript
|
|
1041
|
+
import {
|
|
1042
|
+
createTimerBundle,
|
|
1043
|
+
createTimer,
|
|
1044
|
+
createRepeatingTimer,
|
|
1045
|
+
type TimerComponentTypes,
|
|
1046
|
+
type TimerEventData
|
|
1047
|
+
} from 'ecspresso/bundles/utils/timers';
|
|
1048
|
+
|
|
1049
|
+
// Define events that use TimerEventData payload
|
|
1050
|
+
interface Events {
|
|
1051
|
+
hideMessage: TimerEventData;
|
|
1052
|
+
spawnWave: TimerEventData;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// Extend components with timer support
|
|
1056
|
+
interface Components extends TimerComponentTypes<Events> {
|
|
1057
|
+
position: { x: number; y: number };
|
|
1058
|
+
messageDisplay: true;
|
|
1059
|
+
spawner: true;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// Create world with timer bundle
|
|
1063
|
+
const world = ECSpresso
|
|
1064
|
+
.create<Components, Events, Resources>()
|
|
1065
|
+
.withBundle(createTimerBundle<Events>())
|
|
1066
|
+
.build();
|
|
1067
|
+
|
|
1068
|
+
// One-shot timer without event (poll justFinished)
|
|
1069
|
+
world.spawn({
|
|
1070
|
+
...createTimer<Events>(2.0),
|
|
1071
|
+
messageDisplay: true,
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
// One-shot timer with completion event
|
|
1075
|
+
world.spawn({
|
|
1076
|
+
...createTimer<Events>(1.5, { onComplete: 'hideMessage' }),
|
|
1077
|
+
messageDisplay: true,
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
// Repeating timer with event
|
|
1081
|
+
world.spawn({
|
|
1082
|
+
...createRepeatingTimer<Events>(5.0, { onComplete: 'spawnWave' }),
|
|
1083
|
+
spawner: true,
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
// Subscribe to timer events
|
|
1087
|
+
world.on('hideMessage', (data) => {
|
|
1088
|
+
console.log(`Timer on entity ${data.entityId} completed after ${data.elapsed}s`);
|
|
1089
|
+
});
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
### Timer Event Data
|
|
1093
|
+
|
|
1094
|
+
Events used with timer `onComplete` must have `TimerEventData` as their payload type:
|
|
1095
|
+
|
|
1096
|
+
```typescript
|
|
1097
|
+
interface TimerEventData {
|
|
1098
|
+
entityId: number; // Entity the timer belongs to
|
|
1099
|
+
duration: number; // Timer's configured duration
|
|
1100
|
+
elapsed: number; // Actual elapsed time (may exceed duration slightly)
|
|
1101
|
+
}
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
### Polling vs Events
|
|
1105
|
+
|
|
1106
|
+
You can use timers in two ways:
|
|
1107
|
+
|
|
1108
|
+
```typescript
|
|
1109
|
+
// 1. Polling with justFinished flag
|
|
1110
|
+
world.addSystem('timerPolling')
|
|
1111
|
+
.addQuery('timers', { with: ['timer', 'messageDisplay'] })
|
|
1112
|
+
.setProcess((queries) => {
|
|
1113
|
+
for (const entity of queries.timers) {
|
|
1114
|
+
if (entity.components.timer.justFinished) {
|
|
1115
|
+
// Timer just completed this frame
|
|
1116
|
+
hideMessage(entity.id);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
})
|
|
1120
|
+
.build();
|
|
1121
|
+
|
|
1122
|
+
// 2. Event-based (decoupled)
|
|
1123
|
+
world.addSystem('timerEvents')
|
|
1124
|
+
.setEventHandlers({
|
|
1125
|
+
hideMessage: {
|
|
1126
|
+
handler: (data, ecs) => {
|
|
1127
|
+
// React to timer completion
|
|
1128
|
+
ecs.commands.removeEntity(data.entityId);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
})
|
|
1132
|
+
.build();
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
### Timer Properties
|
|
1136
|
+
|
|
1137
|
+
```typescript
|
|
1138
|
+
interface Timer {
|
|
1139
|
+
elapsed: number; // Time accumulated (seconds)
|
|
1140
|
+
duration: number; // Target duration (seconds)
|
|
1141
|
+
repeat: boolean; // Whether timer repeats
|
|
1142
|
+
active: boolean; // Whether timer is running
|
|
1143
|
+
justFinished: boolean; // True for one frame after completion
|
|
1144
|
+
onComplete?: string; // Event name to publish (optional)
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// Control timer at runtime
|
|
1148
|
+
const entity = world.spawn({ ...createTimer<Events>(5.0), myTimer: true });
|
|
1149
|
+
const timer = entity.components.timer;
|
|
1150
|
+
|
|
1151
|
+
timer.active = false; // Pause
|
|
1152
|
+
timer.active = true; // Resume
|
|
1153
|
+
timer.elapsed = 0; // Reset
|
|
1154
|
+
timer.duration = 10; // Change duration
|
|
1155
|
+
```
|
|
1156
|
+
|
|
970
1157
|
## Error Handling
|
|
971
1158
|
|
|
972
1159
|
ECSpresso provides clear, contextual error messages for common issues:
|
package/dist/bundle.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { SystemBuilderWithBundle } from './system-builder';
|
|
|
2
2
|
import type ECSpresso from './ecspresso';
|
|
3
3
|
import type { AssetDefinition } from './asset-types';
|
|
4
4
|
import type { ScreenDefinition } from './screen-types';
|
|
5
|
+
import type { BundlesAreCompatible } from './type-utils';
|
|
5
6
|
/**
|
|
6
7
|
* Bundle class that encapsulates a set of components, resources, events, and systems
|
|
7
8
|
* that can be merged into a ECSpresso instance
|
|
@@ -115,28 +116,8 @@ export default class Bundle<ComponentTypes extends Record<string, any> = {}, Eve
|
|
|
115
116
|
*/
|
|
116
117
|
hasResource<K extends keyof ResourceTypes>(key: K): boolean;
|
|
117
118
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Utility type to check if two types are exactly the same
|
|
120
|
-
*/
|
|
121
|
-
type Exactly<T, U> = T extends U ? U extends T ? true : false : false;
|
|
122
|
-
/**
|
|
123
|
-
* Simplified type constraint for bundle compatibility
|
|
124
|
-
* Ensures that overlapping keys have exactly the same types
|
|
125
|
-
*/
|
|
126
|
-
type CompatibleBundles<C1 extends Record<string, any>, C2 extends Record<string, any>, E1 extends Record<string, any>, E2 extends Record<string, any>, R1 extends Record<string, any>, R2 extends Record<string, any>, A1 extends Record<string, unknown> = {}, A2 extends Record<string, unknown> = {}, S1 extends Record<string, ScreenDefinition<any, any>> = {}, S2 extends Record<string, ScreenDefinition<any, any>> = {}> = {
|
|
127
|
-
[K in keyof C1 & keyof C2]: Exactly<C1[K], C2[K]> extends true ? C1[K] : never;
|
|
128
|
-
} & {
|
|
129
|
-
[K in keyof E1 & keyof E2]: Exactly<E1[K], E2[K]> extends true ? E1[K] : never;
|
|
130
|
-
} & {
|
|
131
|
-
[K in keyof R1 & keyof R2]: Exactly<R1[K], R2[K]> extends true ? R1[K] : never;
|
|
132
|
-
} & {
|
|
133
|
-
[K in keyof A1 & keyof A2]: Exactly<A1[K], A2[K]> extends true ? A1[K] : never;
|
|
134
|
-
} & {
|
|
135
|
-
[K in keyof S1 & keyof S2]: Exactly<S1[K], S2[K]> extends true ? S1[K] : never;
|
|
136
|
-
};
|
|
137
119
|
/**
|
|
138
120
|
* Function that merges multiple bundles into a single bundle
|
|
139
121
|
*/
|
|
140
|
-
export declare function mergeBundles<C1 extends Record<string, any>, E1 extends Record<string, any>, R1 extends Record<string, any>, A1 extends Record<string, unknown>, S1 extends Record<string, ScreenDefinition<any, any>>, C2 extends Record<string, any>, E2 extends Record<string, any>, R2 extends Record<string, any>, A2 extends Record<string, unknown>, S2 extends Record<string, ScreenDefinition<any, any>>>(id: string, bundle1: Bundle<C1, E1, R1, A1, S1>, bundle2:
|
|
122
|
+
export declare function mergeBundles<C1 extends Record<string, any>, E1 extends Record<string, any>, R1 extends Record<string, any>, A1 extends Record<string, unknown>, S1 extends Record<string, ScreenDefinition<any, any>>, C2 extends Record<string, any>, E2 extends Record<string, any>, R2 extends Record<string, any>, A2 extends Record<string, unknown>, S2 extends Record<string, ScreenDefinition<any, any>>>(id: string, bundle1: Bundle<C1, E1, R1, A1, S1>, bundle2: BundlesAreCompatible<C1, C2, E1, E2, R1, R2, A1, A2, S1, S2> extends true ? Bundle<C2, E2, R2, A2, S2> : never): Bundle<C1 & C2, E1 & E2, R1 & R2, A1 & A2, S1 & S2>;
|
|
141
123
|
export declare function mergeBundles<ComponentTypes extends Record<string, any>, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>, AssetTypes extends Record<string, unknown>, ScreenStates extends Record<string, ScreenDefinition<any, any>>>(id: string, ...bundles: Array<Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates>>): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates>;
|
|
142
|
-
export {};
|
|
@@ -3,29 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* An opt-in PixiJS rendering bundle that automates scene graph wiring.
|
|
5
5
|
* Import from 'ecspresso/bundles/renderers/pixi'
|
|
6
|
+
*
|
|
7
|
+
* This bundle includes transform propagation automatically.
|
|
6
8
|
*/
|
|
7
9
|
import type { Application, ApplicationOptions, Container, Sprite, Graphics } from 'pixi.js';
|
|
8
10
|
import Bundle from '../../bundle';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export interface LocalTransform {
|
|
13
|
-
x: number;
|
|
14
|
-
y: number;
|
|
15
|
-
rotation: number;
|
|
16
|
-
scaleX: number;
|
|
17
|
-
scaleY: number;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Computed world transform (accumulated from parent chain)
|
|
21
|
-
*/
|
|
22
|
-
export interface WorldTransform {
|
|
23
|
-
x: number;
|
|
24
|
-
y: number;
|
|
25
|
-
rotation: number;
|
|
26
|
-
scaleX: number;
|
|
27
|
-
scaleY: number;
|
|
28
|
-
}
|
|
11
|
+
import { type LocalTransform, type WorldTransform, type TransformComponentTypes, type TransformBundleOptions } from '../utils/transform';
|
|
12
|
+
export type { LocalTransform, WorldTransform, TransformComponentTypes };
|
|
13
|
+
export { createTransform, createLocalTransform, createWorldTransform } from '../utils/transform';
|
|
29
14
|
/**
|
|
30
15
|
* PixiJS Sprite component
|
|
31
16
|
*/
|
|
@@ -67,9 +52,7 @@ export interface PixiVisible {
|
|
|
67
52
|
* }
|
|
68
53
|
* ```
|
|
69
54
|
*/
|
|
70
|
-
export interface PixiComponentTypes {
|
|
71
|
-
localTransform: LocalTransform;
|
|
72
|
-
worldTransform: WorldTransform;
|
|
55
|
+
export interface PixiComponentTypes extends TransformComponentTypes {
|
|
73
56
|
pixiSprite: PixiSprite;
|
|
74
57
|
pixiGraphics: PixiGraphics;
|
|
75
58
|
pixiContainer: PixiContainer;
|
|
@@ -100,10 +83,10 @@ interface PixiBundleCommonOptions {
|
|
|
100
83
|
rootContainer?: Container;
|
|
101
84
|
/** System group name (default: 'pixi-renderer') */
|
|
102
85
|
systemGroup?: string;
|
|
103
|
-
/** Priority for transform propagation system (default: 1000) */
|
|
104
|
-
transformPriority?: number;
|
|
105
86
|
/** Priority for render sync system (default: 500) */
|
|
106
87
|
renderSyncPriority?: number;
|
|
88
|
+
/** Options for the included transform bundle */
|
|
89
|
+
transform?: TransformBundleOptions;
|
|
107
90
|
}
|
|
108
91
|
/**
|
|
109
92
|
* Options when providing a pre-initialized PixiJS Application
|
|
@@ -131,18 +114,20 @@ export interface PixiBundleManagedOptions extends PixiBundleCommonOptions {
|
|
|
131
114
|
* 1. **Pre-initialized**: Pass an already-initialized Application via `app`
|
|
132
115
|
* 2. **Managed**: Pass `init` options and the bundle creates the Application during `ecs.initialize()`
|
|
133
116
|
*
|
|
117
|
+
* This bundle includes transform propagation automatically - no need to add createTransformBundle() separately.
|
|
118
|
+
*
|
|
134
119
|
* @example Pre-initialized mode (full control)
|
|
135
120
|
* ```typescript
|
|
136
121
|
* const app = new Application();
|
|
137
122
|
* await app.init({ resizeTo: window });
|
|
138
|
-
* const ecs = ECSpresso.create
|
|
123
|
+
* const ecs = ECSpresso.create<GameComponents, {}, {}>()
|
|
139
124
|
* .withBundle(createPixiBundle({ app }))
|
|
140
125
|
* .build();
|
|
141
126
|
* ```
|
|
142
127
|
*
|
|
143
128
|
* @example Managed mode (convenience)
|
|
144
129
|
* ```typescript
|
|
145
|
-
* const ecs = ECSpresso.create
|
|
130
|
+
* const ecs = ECSpresso.create<GameComponents, {}, {}>()
|
|
146
131
|
* .withBundle(createPixiBundle({
|
|
147
132
|
* init: { background: '#1099bb', resizeTo: window },
|
|
148
133
|
* container: document.body,
|
|
@@ -219,7 +204,7 @@ export declare function createContainerComponents(container: Container, position
|
|
|
219
204
|
* Create a PixiJS rendering bundle for ECSpresso.
|
|
220
205
|
*
|
|
221
206
|
* This bundle provides:
|
|
222
|
-
* - Transform propagation
|
|
207
|
+
* - Transform propagation (localTransform → worldTransform)
|
|
223
208
|
* - Render sync system (updates PixiJS objects from ECS components)
|
|
224
209
|
* - Scene graph management (mirrors ECS hierarchy in PixiJS scene graph)
|
|
225
210
|
*
|
|
@@ -245,4 +230,3 @@ export declare function createContainerComponents(container: Container, position
|
|
|
245
230
|
* ```
|
|
246
231
|
*/
|
|
247
232
|
export declare function createPixiBundle(options: PixiBundleOptions): Bundle<PixiComponentTypes, PixiEventTypes, PixiResourceTypes>;
|
|
248
|
-
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var O=Object.create;var{getPrototypeOf:I,defineProperty:j,getOwnPropertyNames:T}=Object;var f=Object.prototype.hasOwnProperty;var u=(H,J,K)=>{K=H!=null?O(I(H)):{};let F=J||!H||!H.__esModule?j(K,"default",{value:H,enumerable:!0}):K;for(let R of T(H))if(!f.call(F,R))j(F,R,{get:()=>H[R],enumerable:!0});return F};var b=((H)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(H,{get:(J,K)=>(typeof require<"u"?require:J)[K]}):H)(function(H){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+H+'" is not supported')});class k{_label;_ecspresso;_bundle;queries={};processFunction;detachFunction;initializeFunction;eventHandlers;_priority=0;_isRegistered=!1;_groups=[];_inScreens;_excludeScreens;_requiredAssets;constructor(H,J=null,K=null){this._label=H;this._ecspresso=J;this._bundle=K}get label(){return this._label}get bundle(){return this._bundle}get ecspresso(){return this._ecspresso}_autoRegister(){if(this._isRegistered||!this._ecspresso)return;let H=this._buildSystemObject();G(H,this._ecspresso),this._isRegistered=!0}_buildSystemObject(){return this._createSystemObject()}_createSystemObject(){let H={label:this._label,entityQueries:this.queries,priority:this._priority};if(this.processFunction)H.process=this.processFunction;if(this.detachFunction)H.onDetach=this.detachFunction;if(this.initializeFunction)H.onInitialize=this.initializeFunction;if(this.eventHandlers)H.eventHandlers=this.eventHandlers;if(this._groups.length>0)H.groups=[...this._groups];if(this._inScreens)H.inScreens=this._inScreens;if(this._excludeScreens)H.excludeScreens=this._excludeScreens;if(this._requiredAssets)H.requiredAssets=this._requiredAssets;return H}setPriority(H){return this._priority=H,this}inGroup(H){if(!this._groups.includes(H))this._groups.push(H);return this}inScreens(H){return this._inScreens=[...H],this}excludeScreens(H){return this._excludeScreens=[...H],this}requiresAssets(H){return this._requiredAssets=[...H],this}addQuery(H,J){let K=this;return K.queries={...this.queries,[H]:J},K}setProcess(H){return this.processFunction=H,this}registerAndContinue(){if(!this._ecspresso)throw Error(`Cannot register system '${this._label}': SystemBuilder is not attached to an ECSpresso instance. Use Bundle.addSystem() or ECSpresso.addSystem() instead.`);return this._autoRegister(),this._ecspresso}and(){if(this._ecspresso)return this._autoRegister(),this._ecspresso;if(this._bundle)return this._bundle;throw Error(`Cannot use and() on system '${this._label}': not attached to ECSpresso or Bundle.`)}setOnDetach(H){return this.detachFunction=H,this}setOnInitialize(H){return this.initializeFunction=H,this}setEventHandlers(H){return this.eventHandlers=H,this}build(H){let J=this._createSystemObject();if(this._ecspresso)G(J,this._ecspresso);if(H)G(J,H);return this}}function G(H,J){J._registerSystem(H)}function c(H,J){return new k(H,J)}function x(H,J){return new k(H,null,J)}function y(){return`bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,9)}`}class E{_systems=[];_resources=new Map;_assets=new Map;_assetGroups=new Map;_screens=new Map;_id;constructor(H){this._id=H||y()}get id(){return this._id}set id(H){this._id=H}addSystem(H){if(typeof H==="string"){let J=x(H,this);return this._systems.push(J),J}else return this._systems.push(H),H}addResource(H,J){return this._resources.set(H,J),this}addAsset(H,J,K){return this._assets.set(H,{loader:J,eager:K?.eager??!0,group:K?.group}),this}addAssetGroup(H,J){let K=new Map;for(let[F,R]of Object.entries(J))K.set(F,R),this._assets.set(F,{loader:R,eager:!1,group:H});return this._assetGroups.set(H,K),this}addScreen(H,J){return this._screens.set(H,J),this}getAssets(){return new Map(this._assets)}getScreens(){return new Map(this._screens)}_setResource(H,J){this._resources.set(H,J)}_setAsset(H,J){this._assets.set(H,J)}_setScreen(H,J){this._screens.set(H,J)}getSystems(){return this._systems.map((H)=>H.build())}registerSystemsWithEcspresso(H){for(let J of this._systems)J.build(H)}getResources(){return new Map(this._resources)}getResource(H){return this._resources.get(H)}getSystemBuilders(){return[...this._systems]}hasResource(H){return this._resources.has(H)}}function n(H,...J){if(J.length===0)return new E(H);let K=new E(H);for(let F of J){for(let R of F.getSystemBuilders())K.addSystem(R);for(let[R,Y]of F.getResources().entries())K._setResource(R,Y);for(let[R,Y]of F.getAssets().entries())K._setAsset(R,Y);for(let[R,Y]of F.getScreens().entries())K._setScreen(R,Y)}return K}async function m(H){let{Application:J}=await import("pixi.js"),K=new J;return await K.init(H),K}var t={x:0,y:0,rotation:0,scaleX:1,scaleY:1},o={x:0,y:0,rotation:0,scaleX:1,scaleY:1};function L(H,J){let K=J?.scale,F=typeof K==="number"?K:K?.x??1,R=typeof K==="number"?K:K?.y??1;return{x:H?.x??0,y:H?.y??0,rotation:J?.rotation??0,scaleX:F,scaleY:R}}function V(H,J){let K=J?.scale,F=typeof K==="number"?K:K?.x??1,R=typeof K==="number"?K:K?.y??1;return{x:H?.x??0,y:H?.y??0,rotation:J?.rotation??0,scaleX:F,scaleY:R}}function B(H){return{visible:H?.visible??!0,alpha:H?.alpha}}function e(H,J,K){return{pixiSprite:{sprite:H,anchor:K?.anchor},localTransform:L(J,K),worldTransform:V(J,K),pixiVisible:B(K)}}function HH(H,J,K){return{pixiGraphics:{graphics:H},localTransform:L(J,K),worldTransform:V(J,K),pixiVisible:B(K)}}function JH(H,J,K){return{pixiContainer:{container:H},localTransform:L(J,K),worldTransform:V(J,K),pixiVisible:B(K)}}function KH(H){let{rootContainer:J,systemGroup:K="pixi-renderer",transformPriority:F=1000,renderSyncPriority:R=500}=H,Y=new E("pixi-renderer");if("init"in H&&H.init!==void 0){let{init:_,container:Q}=H;Y.addResource("pixiApp",async()=>{let U=await m(_);if(Q){let D=typeof Q==="string"?document.querySelector(Q):Q;if(D)D.appendChild(U.canvas);else if(typeof Q==="string")console.warn(`PixiJS bundle: container selector "${Q}" not found`)}return U}),Y.addResource("pixiRootContainer",{dependsOn:["pixiApp"],factory:(U)=>J??U.getResource("pixiApp").stage})}else{let _=H.app;Y.addResource("pixiApp",_),Y.addResource("pixiRootContainer",J??_.stage)}let q=new Map;function W(_,Q){let U=q.get(_);if(U)return U;let D=Q.entityManager.getComponent(_,"pixiSprite");if(D)return q.set(_,D.sprite),D.sprite;let z=Q.entityManager.getComponent(_,"pixiGraphics");if(z)return q.set(_,z.graphics),z.graphics;let $=Q.entityManager.getComponent(_,"pixiContainer");if($)return q.set(_,$.container),$.container;return null}function A(_,Q,U){let D=U.getResource("pixiRootContainer"),z=U.getParent(_),M=(z!==null?W(z,U):null)??D;if(Q.parent!==M)M.addChild(Q)}function N(_){let Q=q.get(_);if(Q)Q.removeFromParent(),q.delete(_)}function g(_,Q){let U=q.get(_);if(!U)return;let D=Q.getResource("pixiRootContainer"),z=Q.getParent(_),M=(z!==null?W(z,Q):null)??D;if(U.parent!==M)U.removeFromParent(),M.addChild(U)}return Y.addSystem("pixi-transform-propagation").setPriority(F).inGroup(K).setProcess((_,Q,U)=>{U.forEachInHierarchy((z,$)=>{let M=U.entityManager.getComponent(z,"localTransform"),Z=U.entityManager.getComponent(z,"worldTransform");if(!M||!Z)return;if($===null)Z.x=M.x,Z.y=M.y,Z.rotation=M.rotation,Z.scaleX=M.scaleX,Z.scaleY=M.scaleY;else{let X=U.entityManager.getComponent($,"worldTransform");if(X){let P=M.x*X.scaleX,v=M.y*X.scaleY,S=Math.cos(X.rotation),C=Math.sin(X.rotation),w=P*S-v*C,h=P*C+v*S;Z.x=X.x+w,Z.y=X.y+h,Z.rotation=X.rotation+M.rotation,Z.scaleX=X.scaleX*M.scaleX,Z.scaleY=X.scaleY*M.scaleY}else Z.x=M.x,Z.y=M.y,Z.rotation=M.rotation,Z.scaleX=M.scaleX,Z.scaleY=M.scaleY}});let D=U.getEntitiesWithQuery(["localTransform","worldTransform"]);for(let z of D)if(U.getParent(z.id)===null&&U.getChildren(z.id).length===0){let{localTransform:M,worldTransform:Z}=z.components;Z.x=M.x,Z.y=M.y,Z.rotation=M.rotation,Z.scaleX=M.scaleX,Z.scaleY=M.scaleY}}).and(),Y.addSystem("pixi-render-sync").setPriority(R).inGroup(K).addQuery("sprites",{with:["pixiSprite","worldTransform"]}).addQuery("graphics",{with:["pixiGraphics","worldTransform"]}).addQuery("containers",{with:["pixiContainer","worldTransform"]}).setProcess((_,Q,U)=>{for(let D of _.sprites){let{pixiSprite:z,worldTransform:$}=D.components,{sprite:M,anchor:Z}=z;if(M.position.set($.x,$.y),M.rotation=$.rotation,M.scale.set($.scaleX,$.scaleY),Z)M.anchor.set(Z.x,Z.y);let X=U.entityManager.getComponent(D.id,"pixiVisible");if(X){if(M.visible=X.visible,X.alpha!==void 0)M.alpha=X.alpha}}for(let D of _.graphics){let{pixiGraphics:z,worldTransform:$}=D.components,{graphics:M}=z;M.position.set($.x,$.y),M.rotation=$.rotation,M.scale.set($.scaleX,$.scaleY);let Z=U.entityManager.getComponent(D.id,"pixiVisible");if(Z){if(M.visible=Z.visible,Z.alpha!==void 0)M.alpha=Z.alpha}}for(let D of _.containers){let{pixiContainer:z,worldTransform:$}=D.components,{container:M}=z;M.position.set($.x,$.y),M.rotation=$.rotation,M.scale.set($.scaleX,$.scaleY);let Z=U.entityManager.getComponent(D.id,"pixiVisible");if(Z){if(M.visible=Z.visible,Z.alpha!==void 0)M.alpha=Z.alpha}}}).and(),Y.addSystem("pixi-scene-graph-manager").setPriority(9999).inGroup(K).setOnInitialize((_)=>{_.addReactiveQuery("pixi-sprites",{with:["pixiSprite"],onEnter:(Q)=>{let U=Q.components.pixiSprite.sprite;q.set(Q.id,U),A(Q.id,U,_)},onExit:(Q)=>{N(Q)}}),_.addReactiveQuery("pixi-graphics",{with:["pixiGraphics"],onEnter:(Q)=>{let U=Q.components.pixiGraphics.graphics;q.set(Q.id,U),A(Q.id,U,_)},onExit:(Q)=>{N(Q)}}),_.addReactiveQuery("pixi-containers",{with:["pixiContainer"],onEnter:(Q)=>{let U=Q.components.pixiContainer.container;q.set(Q.id,U),A(Q.id,U,_)},onExit:(Q)=>{N(Q)}}),_.on("hierarchyChanged",({entityId:Q})=>{g(Q,_)})}).and(),Y}export{e as createSpriteComponents,KH as createPixiBundle,HH as createGraphicsComponents,JH as createContainerComponents,o as DEFAULT_WORLD_TRANSFORM,t as DEFAULT_LOCAL_TRANSFORM};
|
|
1
|
+
var O=Object.create;var{getPrototypeOf:x,defineProperty:C,getOwnPropertyNames:T}=Object;var I=Object.prototype.hasOwnProperty;var f=(H,J,K)=>{K=H!=null?O(x(H)):{};let _=J||!H||!H.__esModule?C(K,"default",{value:H,enumerable:!0}):K;for(let U of T(H))if(!I.call(_,U))C(_,U,{get:()=>H[U],enumerable:!0});return _};var b=((H)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(H,{get:(J,K)=>(typeof require<"u"?require:J)[K]}):H)(function(H){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+H+'" is not supported')});class L{_label;_ecspresso;_bundle;queries={};processFunction;detachFunction;initializeFunction;eventHandlers;_priority=0;_isRegistered=!1;_groups=[];_inScreens;_excludeScreens;_requiredAssets;constructor(H,J=null,K=null){this._label=H;this._ecspresso=J;this._bundle=K}get label(){return this._label}get bundle(){return this._bundle}get ecspresso(){return this._ecspresso}_autoRegister(){if(this._isRegistered||!this._ecspresso)return;let H=this._buildSystemObject();k(H,this._ecspresso),this._isRegistered=!0}_buildSystemObject(){return this._createSystemObject()}_createSystemObject(){let H={label:this._label,entityQueries:this.queries,priority:this._priority};if(this.processFunction)H.process=this.processFunction;if(this.detachFunction)H.onDetach=this.detachFunction;if(this.initializeFunction)H.onInitialize=this.initializeFunction;if(this.eventHandlers)H.eventHandlers=this.eventHandlers;if(this._groups.length>0)H.groups=[...this._groups];if(this._inScreens)H.inScreens=this._inScreens;if(this._excludeScreens)H.excludeScreens=this._excludeScreens;if(this._requiredAssets)H.requiredAssets=this._requiredAssets;return H}setPriority(H){return this._priority=H,this}inGroup(H){if(!this._groups.includes(H))this._groups.push(H);return this}inScreens(H){return this._inScreens=[...H],this}excludeScreens(H){return this._excludeScreens=[...H],this}requiresAssets(H){return this._requiredAssets=[...H],this}addQuery(H,J){let K=this;return K.queries={...this.queries,[H]:J},K}setProcess(H){return this.processFunction=H,this}registerAndContinue(){if(!this._ecspresso)throw Error(`Cannot register system '${this._label}': SystemBuilder is not attached to an ECSpresso instance. Use Bundle.addSystem() or ECSpresso.addSystem() instead.`);return this._autoRegister(),this._ecspresso}and(){if(this._ecspresso)return this._autoRegister(),this._ecspresso;if(this._bundle)return this._bundle;throw Error(`Cannot use and() on system '${this._label}': not attached to ECSpresso or Bundle.`)}setOnDetach(H){return this.detachFunction=H,this}setOnInitialize(H){return this.initializeFunction=H,this}setEventHandlers(H){return this.eventHandlers=H,this}build(H){let J=this._createSystemObject();if(this._ecspresso)k(J,this._ecspresso);if(H)k(J,H);return this}}function k(H,J){J._registerSystem(H)}function r(H,J){return new L(H,J)}function B(H,J){return new L(H,null,J)}function u(){return`bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,9)}`}class E{_systems=[];_resources=new Map;_assets=new Map;_assetGroups=new Map;_screens=new Map;_id;constructor(H){this._id=H||u()}get id(){return this._id}set id(H){this._id=H}addSystem(H){if(typeof H==="string"){let J=B(H,this);return this._systems.push(J),J}else return this._systems.push(H),H}addResource(H,J){return this._resources.set(H,J),this}addAsset(H,J,K){return this._assets.set(H,{loader:J,eager:K?.eager??!0,group:K?.group}),this}addAssetGroup(H,J){let K=new Map;for(let[_,U]of Object.entries(J))K.set(_,U),this._assets.set(_,{loader:U,eager:!1,group:H});return this._assetGroups.set(H,K),this}addScreen(H,J){return this._screens.set(H,J),this}getAssets(){return new Map(this._assets)}getScreens(){return new Map(this._screens)}_setResource(H,J){this._resources.set(H,J)}_setAsset(H,J){this._assets.set(H,J)}_setScreen(H,J){this._screens.set(H,J)}getSystems(){return this._systems.map((H)=>H.build())}registerSystemsWithEcspresso(H){for(let J of this._systems)J.build(H)}getResources(){return new Map(this._resources)}getResource(H){return this._resources.get(H)}getSystemBuilders(){return[...this._systems]}hasResource(H){return this._resources.has(H)}}function j(H,...J){if(J.length===0)return new E(H);let K=new E(H);for(let _ of J){for(let U of _.getSystemBuilders())K.addSystem(U);for(let[U,M]of _.getResources().entries())K._setResource(U,M);for(let[U,M]of _.getAssets().entries())K._setAsset(U,M);for(let[U,M]of _.getScreens().entries())K._setScreen(U,M)}return K}function m(H,J){return{localTransform:{x:H,y:J,rotation:0,scaleX:1,scaleY:1}}}function y(H,J){return{worldTransform:{x:H,y:J,rotation:0,scaleX:1,scaleY:1}}}function d(H,J,K){let _=K?.scale??K?.scaleX??1,U=K?.scale??K?.scaleY??1,M=K?.rotation??0,Y={x:H,y:J,rotation:M,scaleX:_,scaleY:U};return{localTransform:{...Y},worldTransform:{...Y}}}function w(H){let{systemGroup:J="transform",priority:K=500}=H??{},_=new E("transform");return _.addSystem("transform-propagation").setPriority(K).inGroup(J).setProcess((U,M,Y)=>{l(Y)}).and(),_}function l(H){H.forEachInHierarchy((K,_)=>{let U=H.entityManager.getComponent(K,"localTransform"),M=H.entityManager.getComponent(K,"worldTransform");if(!U||!M)return;if(_===null)V(U,M);else{let Y=H.entityManager.getComponent(_,"worldTransform");if(Y)c(Y,U,M);else V(U,M)}});let J=H.getEntitiesWithQuery(["localTransform","worldTransform"]);for(let K of J)if(H.getParent(K.id)===null&&H.getChildren(K.id).length===0){let{localTransform:U,worldTransform:M}=K.components;V(U,M)}}function V(H,J){J.x=H.x,J.y=H.y,J.rotation=H.rotation,J.scaleX=H.scaleX,J.scaleY=H.scaleY}function c(H,J,K){let _=J.x*H.scaleX,U=J.y*H.scaleY,M=Math.cos(H.rotation),Y=Math.sin(H.rotation),X=_*M-U*Y,N=_*Y+U*M;K.x=H.x+X,K.y=H.y+N,K.rotation=H.rotation+J.rotation,K.scaleX=H.scaleX*J.scaleX,K.scaleY=H.scaleY*J.scaleY}async function p(H){let{Application:J}=await import("pixi.js"),K=new J;return await K.init(H),K}var KH={x:0,y:0,rotation:0,scaleX:1,scaleY:1},QH={x:0,y:0,rotation:0,scaleX:1,scaleY:1};function P(H,J){let K=J?.scale,_=typeof K==="number"?K:K?.x??1,U=typeof K==="number"?K:K?.y??1;return{x:H?.x??0,y:H?.y??0,rotation:J?.rotation??0,scaleX:_,scaleY:U}}function v(H,J){let K=J?.scale,_=typeof K==="number"?K:K?.x??1,U=typeof K==="number"?K:K?.y??1;return{x:H?.x??0,y:H?.y??0,rotation:J?.rotation??0,scaleX:_,scaleY:U}}function S(H){return{visible:H?.visible??!0,alpha:H?.alpha}}function UH(H,J,K){return{pixiSprite:{sprite:H,anchor:K?.anchor},localTransform:P(J,K),worldTransform:v(J,K),pixiVisible:S(K)}}function ZH(H,J,K){return{pixiGraphics:{graphics:H},localTransform:P(J,K),worldTransform:v(J,K),pixiVisible:S(K)}}function _H(H,J,K){return{pixiContainer:{container:H},localTransform:P(J,K),worldTransform:v(J,K),pixiVisible:S(K)}}function $H(H){let{rootContainer:J,systemGroup:K="pixi-renderer",renderSyncPriority:_=500,transform:U}=H,M=new E("pixi-renderer-internal");if("init"in H&&H.init!==void 0){let{init:Z,container:Q}=H;M.addResource("pixiApp",async()=>{let $=await p(Z);if(Q){let F=typeof Q==="string"?document.querySelector(Q):Q;if(F)F.appendChild($.canvas);else if(typeof Q==="string")console.warn(`PixiJS bundle: container selector "${Q}" not found`)}return $}),M.addResource("pixiRootContainer",{dependsOn:["pixiApp"],factory:($)=>J??$.getResource("pixiApp").stage})}else{let Z=H.app;M.addResource("pixiApp",Z),M.addResource("pixiRootContainer",J??Z.stage)}let X=new Map;function N(Z,Q){let $=X.get(Z);if($)return $;let F=Q.entityManager.getComponent(Z,"pixiSprite");if(F)return X.set(Z,F.sprite),F.sprite;let R=Q.entityManager.getComponent(Z,"pixiGraphics");if(R)return X.set(Z,R.graphics),R.graphics;let D=Q.entityManager.getComponent(Z,"pixiContainer");if(D)return X.set(Z,D.container),D.container;return null}function A(Z,Q,$){let F=$.getResource("pixiRootContainer"),R=$.getParent(Z),z=(R!==null?N(R,$):null)??F;if(Q.parent!==z)z.addChild(Q)}function G(Z){let Q=X.get(Z);if(Q)Q.removeFromParent(),X.delete(Z)}function g(Z,Q){let $=X.get(Z);if(!$)return;let F=Q.getResource("pixiRootContainer"),R=Q.getParent(Z),z=(R!==null?N(R,Q):null)??F;if($.parent!==z)$.removeFromParent(),z.addChild($)}M.addSystem("pixi-render-sync").setPriority(_).inGroup(K).addQuery("sprites",{with:["pixiSprite","worldTransform"]}).addQuery("graphics",{with:["pixiGraphics","worldTransform"]}).addQuery("containers",{with:["pixiContainer","worldTransform"]}).setProcess((Z,Q,$)=>{for(let F of Z.sprites){let{pixiSprite:R,worldTransform:D}=F.components,{sprite:z,anchor:q}=R;if(z.position.set(D.x,D.y),z.rotation=D.rotation,z.scale.set(D.scaleX,D.scaleY),q)z.anchor.set(q.x,q.y);let W=$.entityManager.getComponent(F.id,"pixiVisible");if(W){if(z.visible=W.visible,W.alpha!==void 0)z.alpha=W.alpha}}for(let F of Z.graphics){let{pixiGraphics:R,worldTransform:D}=F.components,{graphics:z}=R;z.position.set(D.x,D.y),z.rotation=D.rotation,z.scale.set(D.scaleX,D.scaleY);let q=$.entityManager.getComponent(F.id,"pixiVisible");if(q){if(z.visible=q.visible,q.alpha!==void 0)z.alpha=q.alpha}}for(let F of Z.containers){let{pixiContainer:R,worldTransform:D}=F.components,{container:z}=R;z.position.set(D.x,D.y),z.rotation=D.rotation,z.scale.set(D.scaleX,D.scaleY);let q=$.entityManager.getComponent(F.id,"pixiVisible");if(q){if(z.visible=q.visible,q.alpha!==void 0)z.alpha=q.alpha}}}).and(),M.addSystem("pixi-scene-graph-manager").setPriority(9999).inGroup(K).setOnInitialize((Z)=>{Z.addReactiveQuery("pixi-sprites",{with:["pixiSprite"],onEnter:(Q)=>{let $=Q.components.pixiSprite.sprite;X.set(Q.id,$),A(Q.id,$,Z)},onExit:(Q)=>{G(Q)}}),Z.addReactiveQuery("pixi-graphics",{with:["pixiGraphics"],onEnter:(Q)=>{let $=Q.components.pixiGraphics.graphics;X.set(Q.id,$),A(Q.id,$,Z)},onExit:(Q)=>{G(Q)}}),Z.addReactiveQuery("pixi-containers",{with:["pixiContainer"],onEnter:(Q)=>{let $=Q.components.pixiContainer.container;X.set(Q.id,$),A(Q.id,$,Z)},onExit:(Q)=>{G(Q)}}),Z.on("hierarchyChanged",({entityId:Q})=>{g(Q,Z)})}).and();let h=w(U);return j("pixi-renderer",h,M)}export{y as createWorldTransform,d as createTransform,UH as createSpriteComponents,$H as createPixiBundle,m as createLocalTransform,ZH as createGraphicsComponents,_H as createContainerComponents,QH as DEFAULT_WORLD_TRANSFORM,KH as DEFAULT_LOCAL_TRANSFORM};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=FD4EB6F5A72B658164756E2164756E21
|
|
4
4
|
//# sourceMappingURL=pixi.js.map
|