@ste_tisci/ecs 0.1.4 → 0.1.6
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/.prettierrc +12 -12
- package/README.md +89 -84
- package/package.json +23 -23
- package/src/ComponentStore.ts +96 -64
- package/src/Ecs.ts +98 -94
- package/src/EntityManager.ts +60 -58
- package/src/index.ts +1 -1
- package/src/types/IComponentRegistry.d.ts +29 -29
- package/src/types/IComponentStore.d.ts +53 -53
- package/src/types/IEcs.d.ts +64 -63
- package/src/types/IEntityManager.d.ts +65 -64
- package/src/types/ISparseSet.d.ts +49 -49
- package/src/types/index.d.ts +43 -42
- package/src/utils/ComponentRegistry.ts +32 -32
- package/src/utils/SparseSet.ts +54 -54
- package/tsconfig.json +17 -17
- package/tsup.config.ts +10 -10
package/src/types/index.d.ts
CHANGED
|
@@ -1,42 +1,43 @@
|
|
|
1
|
-
import type { IComponentStore } from './IComponentStore.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Represents component data in Structure of Arrays (SoA) format.
|
|
5
|
-
* Each property of the component is stored in a separate array,
|
|
6
|
-
* enabling cache-efficient iteration and SIMD operations.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
};
|
|
1
|
+
import type { IComponentStore } from './IComponentStore.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents component data in Structure of Arrays (SoA) format.
|
|
5
|
+
* Each property of the component is stored in a separate array,
|
|
6
|
+
* enabling cache-efficient iteration and SIMD operations.
|
|
7
|
+
* Numeric properties are backed by a Float32Array for a compact, homogeneous
|
|
8
|
+
* memory layout; non-numeric properties fall back to a plain JS array.
|
|
9
|
+
* @template T - The component data type
|
|
10
|
+
* @example
|
|
11
|
+
* // For Position component: { x: number, y: number }
|
|
12
|
+
* // ComponentDataArrays would be: { x: Float32Array, y: Float32Array }
|
|
13
|
+
*/
|
|
14
|
+
export type ComponentDataArrays<T> = {
|
|
15
|
+
[K in keyof T]: T[K] extends number ? Float32Array : T[K][];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Maps component names to their corresponding ComponentStore instances.
|
|
20
|
+
* Used internally by CreateWorld to manage all component stores.
|
|
21
|
+
* @template T - Record type defining all component types
|
|
22
|
+
*/
|
|
23
|
+
export type StoreMap<T> = {
|
|
24
|
+
[K in keyof T]: IComponentStore<T[K]>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Maps component names to their data arrays for direct access.
|
|
29
|
+
* Used to expose component data to the user without store methods.
|
|
30
|
+
* @template T - Record type defining all component types
|
|
31
|
+
*/
|
|
32
|
+
export type StoreDataMap<T> = {
|
|
33
|
+
[K in keyof T]: ComponentDataArrays<T[K]>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Type helper for the query result.
|
|
38
|
+
* Creates an object with an entry for each requested component, holding its store index.
|
|
39
|
+
* @template C - Array of the component names requested in the query.
|
|
40
|
+
*/
|
|
41
|
+
export type QueryResult<C extends readonly PropertyKey[]> = {
|
|
42
|
+
[K in C[number]]: { id: number };
|
|
43
|
+
};
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import type { IComponentRegistry } from '../types/IComponentRegistry.js';
|
|
2
|
-
|
|
3
|
-
export function createComponentRegistry<K extends string>(): IComponentRegistry<K> {
|
|
4
|
-
const nameToID = new Map<K, number>();
|
|
5
|
-
const IDtoName = new Map<number, K>();
|
|
6
|
-
let nextID: number = 0;
|
|
7
|
-
|
|
8
|
-
function register(name: K): void {
|
|
9
|
-
if (!nameToID.has(name)) {
|
|
10
|
-
const eid = nextID++;
|
|
11
|
-
|
|
12
|
-
nameToID.set(name, eid);
|
|
13
|
-
IDtoName.set(eid, name);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function getID(name: K): number {
|
|
18
|
-
const eid = nameToID.get(name);
|
|
19
|
-
if (eid === undefined) throw new Error(`Component ${name} not registered`);
|
|
20
|
-
|
|
21
|
-
return eid;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function getName(cid: number): K {
|
|
25
|
-
const name = IDtoName.get(cid);
|
|
26
|
-
if (!name) throw new Error(`Component ID ${cid} not registered`);
|
|
27
|
-
|
|
28
|
-
return name;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return { register, getID, getName };
|
|
32
|
-
}
|
|
1
|
+
import type { IComponentRegistry } from '../types/IComponentRegistry.js';
|
|
2
|
+
|
|
3
|
+
export function createComponentRegistry<K extends string>(): IComponentRegistry<K> {
|
|
4
|
+
const nameToID = new Map<K, number>();
|
|
5
|
+
const IDtoName = new Map<number, K>();
|
|
6
|
+
let nextID: number = 0;
|
|
7
|
+
|
|
8
|
+
function register(name: K): void {
|
|
9
|
+
if (!nameToID.has(name)) {
|
|
10
|
+
const eid = nextID++;
|
|
11
|
+
|
|
12
|
+
nameToID.set(name, eid);
|
|
13
|
+
IDtoName.set(eid, name);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getID(name: K): number {
|
|
18
|
+
const eid = nameToID.get(name);
|
|
19
|
+
if (eid === undefined) throw new Error(`Component ${name} not registered`);
|
|
20
|
+
|
|
21
|
+
return eid;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getName(cid: number): K {
|
|
25
|
+
const name = IDtoName.get(cid);
|
|
26
|
+
if (!name) throw new Error(`Component ID ${cid} not registered`);
|
|
27
|
+
|
|
28
|
+
return name;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { register, getID, getName };
|
|
32
|
+
}
|
package/src/utils/SparseSet.ts
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
import type { ISparseSet } from '../types/ISparseSet.js';
|
|
2
|
-
|
|
3
|
-
export function SparseSet(): ISparseSet {
|
|
4
|
-
const sparse: number[] = [];
|
|
5
|
-
const dense: number[] = [];
|
|
6
|
-
let size = 0;
|
|
7
|
-
|
|
8
|
-
function has(eid: number): boolean {
|
|
9
|
-
const ID = getIndex(eid);
|
|
10
|
-
|
|
11
|
-
const exist = ID !== undefined && dense[ID] === eid;
|
|
12
|
-
const isWhitinBounds = ID >= 0 && ID < size;
|
|
13
|
-
|
|
14
|
-
return exist && isWhitinBounds;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function add(eid: number): void {
|
|
18
|
-
if (has(eid)) throw new Error(`ID ${eid} already present in the set`);
|
|
19
|
-
|
|
20
|
-
sparse[eid] = size;
|
|
21
|
-
dense[size] = eid;
|
|
22
|
-
size++;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function remove(eid: number): void {
|
|
26
|
-
if (!has(eid)) throw new Error(`Cannot remove ID ${eid} because does not exists`);
|
|
27
|
-
|
|
28
|
-
const ID = getIndex(eid);
|
|
29
|
-
const last = dense[size - 1];
|
|
30
|
-
|
|
31
|
-
// Swap with the last element inserted
|
|
32
|
-
dense[ID] = last;
|
|
33
|
-
sparse[last] = ID;
|
|
34
|
-
|
|
35
|
-
// Remove the element
|
|
36
|
-
dense.pop();
|
|
37
|
-
size--;
|
|
38
|
-
delete sparse[eid];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getIndex(eid: number): number {
|
|
42
|
-
return sparse[eid];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getDense(): number[] {
|
|
46
|
-
return dense;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getSize(): number {
|
|
50
|
-
return size;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return { has, add, remove, getIndex, getDense, getSize };
|
|
54
|
-
}
|
|
1
|
+
import type { ISparseSet } from '../types/ISparseSet.js';
|
|
2
|
+
|
|
3
|
+
export function SparseSet(): ISparseSet {
|
|
4
|
+
const sparse: number[] = [];
|
|
5
|
+
const dense: number[] = [];
|
|
6
|
+
let size = 0;
|
|
7
|
+
|
|
8
|
+
function has(eid: number): boolean {
|
|
9
|
+
const ID = getIndex(eid);
|
|
10
|
+
|
|
11
|
+
const exist = ID !== undefined && dense[ID] === eid;
|
|
12
|
+
const isWhitinBounds = ID >= 0 && ID < size;
|
|
13
|
+
|
|
14
|
+
return exist && isWhitinBounds;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function add(eid: number): void {
|
|
18
|
+
if (has(eid)) throw new Error(`ID ${eid} already present in the set`);
|
|
19
|
+
|
|
20
|
+
sparse[eid] = size;
|
|
21
|
+
dense[size] = eid;
|
|
22
|
+
size++;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function remove(eid: number): void {
|
|
26
|
+
if (!has(eid)) throw new Error(`Cannot remove ID ${eid} because does not exists`);
|
|
27
|
+
|
|
28
|
+
const ID = getIndex(eid);
|
|
29
|
+
const last = dense[size - 1];
|
|
30
|
+
|
|
31
|
+
// Swap with the last element inserted
|
|
32
|
+
dense[ID] = last;
|
|
33
|
+
sparse[last] = ID;
|
|
34
|
+
|
|
35
|
+
// Remove the element
|
|
36
|
+
dense.pop();
|
|
37
|
+
size--;
|
|
38
|
+
delete sparse[eid];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getIndex(eid: number): number {
|
|
42
|
+
return sparse[eid];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getDense(): number[] {
|
|
46
|
+
return dense;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getSize(): number {
|
|
50
|
+
return size;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { has, add, remove, getIndex, getDense, getSize };
|
|
54
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "es2024",
|
|
4
|
-
"allowSyntheticDefaultImports": true,
|
|
5
|
-
"esModuleInterop": true,
|
|
6
|
-
"outDir": "dist",
|
|
7
|
-
"forceConsistentCasingInFileNames": true,
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"strict": true,
|
|
10
|
-
"sourceMap": true,
|
|
11
|
-
"rootDir": "src",
|
|
12
|
-
"moduleResolution": "nodenext",
|
|
13
|
-
"module": "node20"
|
|
14
|
-
},
|
|
15
|
-
"exclude": ["node_modules/"],
|
|
16
|
-
"include": ["src"]
|
|
17
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2024",
|
|
4
|
+
"allowSyntheticDefaultImports": true,
|
|
5
|
+
"esModuleInterop": true,
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"forceConsistentCasingInFileNames": true,
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"rootDir": "src",
|
|
12
|
+
"moduleResolution": "nodenext",
|
|
13
|
+
"module": "node20"
|
|
14
|
+
},
|
|
15
|
+
"exclude": ["node_modules/"],
|
|
16
|
+
"include": ["src"]
|
|
17
|
+
}
|
package/tsup.config.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { defineConfig } from 'tsup';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
format: ['cjs', 'esm'],
|
|
5
|
-
entry: ['./src/index.ts'],
|
|
6
|
-
dts: true,
|
|
7
|
-
shims: true,
|
|
8
|
-
skipNodeModulesBundle: true,
|
|
9
|
-
clean: true,
|
|
10
|
-
});
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
format: ['cjs', 'esm'],
|
|
5
|
+
entry: ['./src/index.ts'],
|
|
6
|
+
dts: true,
|
|
7
|
+
shims: true,
|
|
8
|
+
skipNodeModulesBundle: true,
|
|
9
|
+
clean: true,
|
|
10
|
+
});
|