@codehz/ecs 0.0.1 → 0.0.2

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 CHANGED
@@ -4,10 +4,11 @@
4
4
 
5
5
  ## 特性
6
6
 
7
- - 🚀 高性能:基于原型的组件存储和高效的查询系统
7
+ - 🚀 高性能:基于 Archetype 的组件存储和高效的查询系统
8
8
  - 🔧 类型安全:完整的 TypeScript 支持
9
9
  - 🏗️ 模块化:清晰的架构,支持自定义系统和组件
10
10
  - 📦 轻量级:零依赖,易于集成
11
+ - ⚡ 内存高效:连续内存布局,优化的迭代性能
11
12
 
12
13
  ## 安装
13
14
 
@@ -44,6 +45,7 @@ world.flushCommands();
44
45
 
45
46
  // 创建查询并更新
46
47
  const query = world.createQuery([PositionId, VelocityId]);
48
+ const deltaTime = 1.0 / 60.0; // 假设60FPS
47
49
  query.forEach([PositionId, VelocityId], (entity, position, velocity) => {
48
50
  position.x += velocity.x * deltaTime;
49
51
  position.y += velocity.y * deltaTime;
@@ -79,6 +81,12 @@ world.flushCommands(); // 钩子在这里被调用
79
81
 
80
82
  ### 运行示例
81
83
 
84
+ ```bash
85
+ bun run demo
86
+ ```
87
+
88
+ 或者直接运行:
89
+
82
90
  ```bash
83
91
  bun run examples/simple/demo.ts
84
92
  ```
@@ -104,6 +112,8 @@ bun run examples/simple/demo.ts
104
112
  ### Query
105
113
 
106
114
  - `forEach(componentIds, callback)`: 遍历匹配的实体
115
+ - `getEntities()`: 获取所有匹配实体的ID列表
116
+ - `getEntitiesWithComponents(componentIds)`: 获取实体及其组件数据
107
117
 
108
118
  ### System
109
119
 
@@ -117,6 +127,13 @@ class MySystem implements System {
117
127
  }
118
128
  ```
119
129
 
130
+ ## 性能特点
131
+
132
+ - **Archetype 系统**:实体按组件组合分组,实现连续内存访问
133
+ - **缓存查询**:查询结果自动缓存,减少重复计算
134
+ - **命令缓冲区**:延迟执行组件添加/移除,提高批处理效率
135
+ - **类型安全**:编译时类型检查,无运行时开销
136
+
120
137
  ## 开发
121
138
 
122
139
  ### 运行测试
@@ -135,20 +152,28 @@ bunx tsc --noEmit
135
152
 
136
153
  ```
137
154
  src/
138
- ├── index.ts # 入口文件
139
- ├── entity.ts # 实体和组件管理
140
- ├── world.ts # 世界管理
141
- ├── archetype.ts # 原型系统
142
- ├── query.ts # 查询系统
143
- ├── system.ts # 系统接口
144
- ├── command-buffer.ts # 命令缓冲区
145
- ├── types.ts # 类型定义
146
- └── utils.ts # 工具函数
155
+ ├── index.ts # 入口文件
156
+ ├── entity.ts # 实体和组件管理
157
+ ├── world.ts # 世界管理
158
+ ├── archetype.ts # Archetype 系统(高效组件存储)
159
+ ├── query.ts # 查询系统
160
+ ├── query-filter.ts # 查询过滤器
161
+ ├── system.ts # 系统接口
162
+ ├── command-buffer.ts # 命令缓冲区
163
+ ├── types.ts # 类型定义
164
+ ├── utils.ts # 工具函数
165
+ ├── *.test.ts # 单元测试
166
+ ├── query.example.ts # 查询示例
167
+ └── *.perf.test.ts # 性能测试
147
168
 
148
169
  examples/
149
170
  └── simple/
150
- ├── demo.ts # 基本示例
151
- └── README.md # 示例说明
171
+ ├── demo.ts # 基本示例
172
+ └── README.md # 示例说明
173
+
174
+ scripts/
175
+ ├── build.ts # 构建脚本
176
+ └── release.ts # 发布脚本
152
177
  ```
153
178
 
154
179
  ## 许可证
package/archetype.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { EntityId } from "./entity";
1
+ import type { EntityId, WildcardRelationId } from "./entity";
2
2
  import type { ComponentTuple } from "./types";
3
3
  /**
4
4
  * Archetype class for ECS architecture
@@ -60,6 +60,13 @@ export declare class Archetype {
60
60
  * @param entityId The entity to check
61
61
  */
62
62
  hasEntity(entityId: EntityId): boolean;
63
+ /**
64
+ * Get component data for a specific entity and wildcard relation type
65
+ * Returns an array of all matching relation instances
66
+ * @param entityId The entity
67
+ * @param componentType The wildcard relation type
68
+ */
69
+ getComponent<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, any][] | undefined;
63
70
  /**
64
71
  * Get component data for a specific entity and component type
65
72
  * @param entityId The entity
package/index.d.ts CHANGED
@@ -3,3 +3,4 @@ export * from "./world";
3
3
  export * from "./archetype";
4
4
  export * from "./query";
5
5
  export * from "./system";
6
+ export * from "./types";
package/index.js CHANGED
@@ -275,9 +275,29 @@ class Archetype {
275
275
  getComponent(entityId, componentType) {
276
276
  const index = this.entityToIndex.get(entityId);
277
277
  if (index === undefined) {
278
- return;
278
+ if (getIdType(componentType) === "wildcard-relation") {
279
+ return [];
280
+ } else {
281
+ return;
282
+ }
283
+ }
284
+ if (getIdType(componentType) === "wildcard-relation") {
285
+ const decoded = decodeRelationId(componentType);
286
+ const componentId = decoded.componentId;
287
+ const relations = [];
288
+ for (const relType of this.componentTypes) {
289
+ const relDecoded = decodeRelationId(relType);
290
+ if (relDecoded.componentId === componentId && (getIdType(relType) === "entity-relation" || getIdType(relType) === "component-relation")) {
291
+ const dataArray = this.componentData.get(relType);
292
+ if (dataArray && dataArray[index] !== undefined) {
293
+ relations.push([relDecoded.targetId, dataArray[index]]);
294
+ }
295
+ }
296
+ }
297
+ return relations;
298
+ } else {
299
+ return this.componentData.get(componentType)?.[index];
279
300
  }
280
- return this.componentData.get(componentType)?.[index];
281
301
  }
282
302
  setComponent(entityId, componentType, data) {
283
303
  const index = this.entityToIndex.get(entityId);
@@ -582,12 +602,23 @@ class World {
582
602
  if (!this.hasEntity(entityId)) {
583
603
  throw new Error(`Entity ${entityId} does not exist`);
584
604
  }
605
+ const detailedType = getDetailedIdType(componentType);
606
+ if (detailedType.type === "invalid") {
607
+ throw new Error(`Invalid component type: ${componentType}`);
608
+ }
609
+ if (detailedType.type === "wildcard-relation") {
610
+ throw new Error(`Cannot directly add wildcard relation components: ${componentType}`);
611
+ }
585
612
  this.commandBuffer.addComponent(entityId, componentType, component);
586
613
  }
587
614
  removeComponent(entityId, componentType) {
588
615
  if (!this.hasEntity(entityId)) {
589
616
  throw new Error(`Entity ${entityId} does not exist`);
590
617
  }
618
+ const detailedType = getDetailedIdType(componentType);
619
+ if (detailedType.type === "invalid") {
620
+ throw new Error(`Invalid component type: ${componentType}`);
621
+ }
591
622
  this.commandBuffer.removeComponent(entityId, componentType);
592
623
  }
593
624
  destroyEntity(entityId) {
@@ -599,7 +630,14 @@ class World {
599
630
  }
600
631
  getComponent(entityId, componentType) {
601
632
  const archetype = this.entityToArchetype.get(entityId);
602
- return archetype ? archetype.getComponent(entityId, componentType) : undefined;
633
+ if (!archetype) {
634
+ if (getIdType(componentType) === "wildcard-relation") {
635
+ return [];
636
+ } else {
637
+ return;
638
+ }
639
+ }
640
+ return archetype.getComponent(entityId, componentType);
603
641
  }
604
642
  registerSystem(system) {
605
643
  this.systems.push(system);
@@ -758,8 +796,22 @@ class World {
758
796
  break;
759
797
  case "removeComponent":
760
798
  if (cmd.componentType) {
761
- removes.add(cmd.componentType);
762
- adds.delete(cmd.componentType);
799
+ const detailedType = getDetailedIdType(cmd.componentType);
800
+ if (detailedType.type === "wildcard-relation") {
801
+ const baseComponentId = detailedType.componentId;
802
+ for (const componentType of currentArchetype.componentTypes) {
803
+ const componentDetailedType = getDetailedIdType(componentType);
804
+ if (componentDetailedType.type === "entity-relation" || componentDetailedType.type === "component-relation") {
805
+ if (componentDetailedType.componentId === baseComponentId) {
806
+ removes.add(componentType);
807
+ adds.delete(componentType);
808
+ }
809
+ }
810
+ }
811
+ } else {
812
+ removes.add(cmd.componentType);
813
+ adds.delete(cmd.componentType);
814
+ }
763
815
  }
764
816
  break;
765
817
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codehz/ecs",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
package/types.d.ts CHANGED
@@ -1,4 +1,17 @@
1
1
  import type { EntityId, WildcardRelationId } from "./entity";
2
+ /**
3
+ * Hook types for component lifecycle events
4
+ */
5
+ export interface LifecycleHook<T = unknown> {
6
+ /**
7
+ * Called when a component is added to an entity
8
+ */
9
+ onAdded?: (entityId: EntityId, componentType: EntityId<T>, component: T) => void;
10
+ /**
11
+ * Called when a component is removed from an entity
12
+ */
13
+ onRemoved?: (entityId: EntityId, componentType: EntityId<T>) => void;
14
+ }
2
15
  /**
3
16
  * Type helper for component tuples extracted from EntityId array
4
17
  */
package/world.d.ts CHANGED
@@ -1,37 +1,10 @@
1
1
  import { Archetype } from "./archetype";
2
2
  import { type Command } from "./command-buffer";
3
- import type { EntityId } from "./entity";
3
+ import type { EntityId, WildcardRelationId } from "./entity";
4
4
  import { Query } from "./query";
5
5
  import type { QueryFilter } from "./query-filter";
6
- import type { ComponentTuple } from "./types";
6
+ import type { ComponentTuple, LifecycleHook } from "./types";
7
7
  import type { System } from "./system";
8
- /**
9
- * Hook types for component lifecycle events
10
- */
11
- export interface ComponentLifecycleHook<T> {
12
- /**
13
- * Called when a component is added to an entity
14
- */
15
- onAdded?: (entityId: EntityId, componentType: EntityId<T>, component: T) => void;
16
- /**
17
- * Called when a component is removed from an entity
18
- */
19
- onRemoved?: (entityId: EntityId, componentType: EntityId<T>) => void;
20
- }
21
- /**
22
- * Hook types for wildcard relation lifecycle events
23
- * These hooks are triggered for any component that matches a wildcard relation pattern
24
- */
25
- export interface WildcardRelationLifecycleHook<T = unknown> {
26
- /**
27
- * Called when any component matching the wildcard relation pattern is added to an entity
28
- */
29
- onAdded?: (entityId: EntityId, componentType: EntityId<T>, component: T) => void;
30
- /**
31
- * Called when any component matching the wildcard relation pattern is removed from an entity
32
- */
33
- onRemoved?: (entityId: EntityId, componentType: EntityId<T>) => void;
34
- }
35
8
  /**
36
9
  * World class for ECS architecture
37
10
  * Manages entities, components, and systems
@@ -93,6 +66,10 @@ export declare class World<ExtraParams extends any[] = [deltaTime: number]> {
93
66
  * Check if an entity has a specific component
94
67
  */
95
68
  hasComponent<T>(entityId: EntityId, componentType: EntityId<T>): boolean;
69
+ /**
70
+ * Get wildcard relations from an entity
71
+ */
72
+ getComponent<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, any][] | undefined;
96
73
  /**
97
74
  * Get a component from an entity
98
75
  */
@@ -108,20 +85,20 @@ export declare class World<ExtraParams extends any[] = [deltaTime: number]> {
108
85
  /**
109
86
  * Register a lifecycle hook for component events
110
87
  */
111
- registerComponentLifecycleHook<T>(componentType: EntityId<T>, hook: ComponentLifecycleHook<T>): void;
88
+ registerComponentLifecycleHook<T>(componentType: EntityId<T>, hook: LifecycleHook<T>): void;
112
89
  /**
113
90
  * Unregister a lifecycle hook for component events
114
91
  */
115
- unregisterComponentLifecycleHook<T>(componentType: EntityId<T>, hook: ComponentLifecycleHook<T>): void;
92
+ unregisterComponentLifecycleHook<T>(componentType: EntityId<T>, hook: LifecycleHook<T>): void;
116
93
  /**
117
94
  * Register a lifecycle hook for wildcard relation events
118
95
  * The hook will be triggered for any component that matches the wildcard relation pattern
119
96
  */
120
- registerWildcardRelationLifecycleHook<T>(baseComponentType: EntityId<T>, hook: WildcardRelationLifecycleHook<T>): void;
97
+ registerWildcardRelationLifecycleHook<T>(baseComponentType: EntityId<T>, hook: LifecycleHook<T>): void;
121
98
  /**
122
99
  * Unregister a lifecycle hook for wildcard relation events
123
100
  */
124
- unregisterWildcardRelationLifecycleHook<T>(baseComponentType: EntityId<T>, hook: WildcardRelationLifecycleHook<T>): void;
101
+ unregisterWildcardRelationLifecycleHook<T>(baseComponentType: EntityId<T>, hook: LifecycleHook<T>): void;
125
102
  /**
126
103
  * Update the world (run all systems)
127
104
  */