@codehz/ecs 0.1.8 → 0.1.9
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/archetype.d.ts +9 -3
- package/index.js +40 -6
- package/package.json +1 -1
- package/query.d.ts +3 -3
- package/types.d.ts +12 -2
package/archetype.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EntityId, WildcardRelationId } from "./entity";
|
|
2
|
-
import type
|
|
2
|
+
import { type ComponentTuple, type ComponentType } from "./types";
|
|
3
3
|
/**
|
|
4
4
|
* Archetype class for ECS architecture
|
|
5
5
|
* Represents a group of entities that share the same set of components
|
|
@@ -89,13 +89,19 @@ export declare class Archetype {
|
|
|
89
89
|
* @param componentType The component type
|
|
90
90
|
*/
|
|
91
91
|
getComponentData<T>(componentType: EntityId<T>): T[];
|
|
92
|
+
/**
|
|
93
|
+
* Get optional component data for all entities of a specific component type
|
|
94
|
+
* @param componentType The component type
|
|
95
|
+
* @returns An array of component data or undefined if not present
|
|
96
|
+
*/
|
|
97
|
+
getOptionalComponentData<T>(componentType: EntityId<T>): T[] | undefined;
|
|
92
98
|
/**
|
|
93
99
|
* Get entities with their component data for specified component types
|
|
94
100
|
* Optimized for bulk component access with pre-computed indices
|
|
95
101
|
* @param componentTypes Array of component types to retrieve
|
|
96
102
|
* @returns Array of objects with entity and component data
|
|
97
103
|
*/
|
|
98
|
-
getEntitiesWithComponents<const T extends readonly
|
|
104
|
+
getEntitiesWithComponents<const T extends readonly ComponentType<any>[]>(componentTypes: T): Array<{
|
|
99
105
|
entity: EntityId;
|
|
100
106
|
components: ComponentTuple<T>;
|
|
101
107
|
}>;
|
|
@@ -105,7 +111,7 @@ export declare class Archetype {
|
|
|
105
111
|
* @param componentTypes Array of component types to retrieve
|
|
106
112
|
* @param callback Function called for each entity with its components
|
|
107
113
|
*/
|
|
108
|
-
forEachWithComponents<const T extends readonly
|
|
114
|
+
forEachWithComponents<const T extends readonly ComponentType<any>[]>(componentTypes: T, callback: (entity: EntityId, ...components: ComponentTuple<T>) => void): void;
|
|
109
115
|
/**
|
|
110
116
|
* Iterate over all entities with their component data
|
|
111
117
|
* @param callback Function called for each entity with its component data
|
package/index.js
CHANGED
|
@@ -217,6 +217,11 @@ var globalComponentIdAllocator = new ComponentIdAllocator;
|
|
|
217
217
|
function component() {
|
|
218
218
|
return globalComponentIdAllocator.allocate();
|
|
219
219
|
}
|
|
220
|
+
// src/types.ts
|
|
221
|
+
function isOptionalEntityId(type) {
|
|
222
|
+
return typeof type === "object" && type !== null && "optional" in type;
|
|
223
|
+
}
|
|
224
|
+
|
|
220
225
|
// src/utils.ts
|
|
221
226
|
function getOrComputeCache(cache, key, compute) {
|
|
222
227
|
let value = cache.get(key);
|
|
@@ -348,6 +353,9 @@ class Archetype {
|
|
|
348
353
|
}
|
|
349
354
|
return data;
|
|
350
355
|
}
|
|
356
|
+
getOptionalComponentData(componentType) {
|
|
357
|
+
return this.componentData.get(componentType);
|
|
358
|
+
}
|
|
351
359
|
getEntitiesWithComponents(componentTypes) {
|
|
352
360
|
const result = [];
|
|
353
361
|
this.forEachWithComponents(componentTypes, (entity, ...components) => {
|
|
@@ -359,6 +367,11 @@ class Archetype {
|
|
|
359
367
|
const cacheKey = componentTypes.map((id) => id.toString()).join(",");
|
|
360
368
|
const componentDataSources = getOrComputeCache(this.componentDataSourcesCache, cacheKey, () => {
|
|
361
369
|
return componentTypes.map((compType) => {
|
|
370
|
+
let optional = false;
|
|
371
|
+
if (isOptionalEntityId(compType)) {
|
|
372
|
+
compType = compType.optional;
|
|
373
|
+
optional = true;
|
|
374
|
+
}
|
|
362
375
|
const detailedType = getDetailedIdType(compType);
|
|
363
376
|
if (detailedType.type === "wildcard-relation") {
|
|
364
377
|
const componentId = detailedType.componentId;
|
|
@@ -368,17 +381,29 @@ class Archetype {
|
|
|
368
381
|
return false;
|
|
369
382
|
return detailedCt.componentId === componentId;
|
|
370
383
|
});
|
|
371
|
-
return matchingRelations;
|
|
384
|
+
return optional ? matchingRelations.length > 0 ? matchingRelations : undefined : matchingRelations;
|
|
372
385
|
} else {
|
|
373
|
-
return this.getComponentData(compType);
|
|
386
|
+
return optional ? this.getOptionalComponentData(compType) : this.getComponentData(compType);
|
|
374
387
|
}
|
|
375
388
|
});
|
|
376
389
|
});
|
|
377
390
|
for (let entityIndex = 0;entityIndex < this.entities.length; entityIndex++) {
|
|
378
391
|
const entity = this.entities[entityIndex];
|
|
379
392
|
const components = componentDataSources.map((dataSource, i) => {
|
|
380
|
-
|
|
393
|
+
let compType = componentTypes[i];
|
|
394
|
+
let optional = false;
|
|
395
|
+
if (isOptionalEntityId(compType)) {
|
|
396
|
+
compType = compType.optional;
|
|
397
|
+
optional = true;
|
|
398
|
+
}
|
|
381
399
|
if (getIdType(compType) === "wildcard-relation") {
|
|
400
|
+
if (dataSource === undefined) {
|
|
401
|
+
if (optional) {
|
|
402
|
+
return;
|
|
403
|
+
} else {
|
|
404
|
+
throw new Error(`No matching relations found for mandatory wildcard relation component type`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
382
407
|
const matchingRelations = dataSource;
|
|
383
408
|
const relations = [];
|
|
384
409
|
for (const relType of matchingRelations) {
|
|
@@ -387,11 +412,19 @@ class Archetype {
|
|
|
387
412
|
const decodedRel = decodeRelationId(relType);
|
|
388
413
|
relations.push([decodedRel.targetId, data === MISSING_COMPONENT ? undefined : data]);
|
|
389
414
|
}
|
|
390
|
-
return relations;
|
|
415
|
+
return optional ? { value: relations } : relations;
|
|
391
416
|
} else {
|
|
417
|
+
if (dataSource === undefined) {
|
|
418
|
+
if (optional) {
|
|
419
|
+
return;
|
|
420
|
+
} else {
|
|
421
|
+
throw new Error(`No matching relations found for mandatory wildcard relation component type`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
392
424
|
const dataArray = dataSource;
|
|
393
|
-
const data = dataArray
|
|
394
|
-
|
|
425
|
+
const data = dataArray[entityIndex];
|
|
426
|
+
const result = data === MISSING_COMPONENT ? undefined : data;
|
|
427
|
+
return optional ? { value: result } : result;
|
|
395
428
|
}
|
|
396
429
|
});
|
|
397
430
|
callback(entity, ...components);
|
|
@@ -1183,6 +1216,7 @@ export {
|
|
|
1183
1216
|
relation,
|
|
1184
1217
|
isWildcardRelationId,
|
|
1185
1218
|
isRelationId,
|
|
1219
|
+
isOptionalEntityId,
|
|
1186
1220
|
isEntityId,
|
|
1187
1221
|
isComponentId,
|
|
1188
1222
|
inspectEntityId,
|
package/package.json
CHANGED
package/query.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Archetype } from "./archetype";
|
|
2
2
|
import type { EntityId } from "./entity";
|
|
3
3
|
import { type QueryFilter } from "./query-filter";
|
|
4
|
-
import type { ComponentTuple } from "./types";
|
|
4
|
+
import type { ComponentTuple, ComponentType } from "./types";
|
|
5
5
|
import type { World } from "./world";
|
|
6
6
|
/**
|
|
7
7
|
* Query class for efficient entity queries with cached archetypes
|
|
@@ -23,7 +23,7 @@ export declare class Query {
|
|
|
23
23
|
* @param componentTypes Array of component types to retrieve
|
|
24
24
|
* @returns Array of objects with entity and component data
|
|
25
25
|
*/
|
|
26
|
-
getEntitiesWithComponents<const T extends readonly
|
|
26
|
+
getEntitiesWithComponents<const T extends readonly ComponentType<any>[]>(componentTypes: T): Array<{
|
|
27
27
|
entity: EntityId;
|
|
28
28
|
components: ComponentTuple<T>;
|
|
29
29
|
}>;
|
|
@@ -32,7 +32,7 @@ export declare class Query {
|
|
|
32
32
|
* @param componentTypes Array of component types to retrieve
|
|
33
33
|
* @param callback Function called for each entity with its components
|
|
34
34
|
*/
|
|
35
|
-
forEach<const T extends readonly
|
|
35
|
+
forEach<const T extends readonly ComponentType<any>[]>(componentTypes: T, callback: (entity: EntityId, ...components: ComponentTuple<T>) => void): void;
|
|
36
36
|
/**
|
|
37
37
|
* Get component data arrays for all matching entities
|
|
38
38
|
* @param componentType The component type to retrieve
|
package/types.d.ts
CHANGED
|
@@ -12,9 +12,19 @@ export interface LifecycleHook<T = unknown> {
|
|
|
12
12
|
*/
|
|
13
13
|
onRemoved?: (entityId: EntityId, componentType: EntityId<T>) => void;
|
|
14
14
|
}
|
|
15
|
+
export type ComponentType<T> = EntityId<T> | OptionalEntityId<T>;
|
|
16
|
+
export type OptionalEntityId<T> = {
|
|
17
|
+
optional: EntityId<T>;
|
|
18
|
+
};
|
|
19
|
+
export declare function isOptionalEntityId<T>(type: ComponentType<T>): type is OptionalEntityId<T>;
|
|
20
|
+
export type ComponentTypeToData<T> = T extends {
|
|
21
|
+
optional: infer U;
|
|
22
|
+
} ? {
|
|
23
|
+
value: ComponentTypeToData<U>;
|
|
24
|
+
} | undefined : T extends WildcardRelationId<infer U> ? [EntityId<unknown>, U][] : T extends EntityId<infer U> ? U : never;
|
|
15
25
|
/**
|
|
16
26
|
* Type helper for component tuples extracted from EntityId array
|
|
17
27
|
*/
|
|
18
|
-
export type ComponentTuple<T extends readonly
|
|
19
|
-
readonly [K in keyof T]:
|
|
28
|
+
export type ComponentTuple<T extends readonly ComponentType<any>[]> = {
|
|
29
|
+
readonly [K in keyof T]: ComponentTypeToData<T[K]>;
|
|
20
30
|
};
|