@codehz/ecs 0.1.3 → 0.1.5

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
@@ -171,6 +171,8 @@ bun run examples/simple/demo.ts
171
171
 
172
172
  - `new()`: 创建新实体
173
173
  - `set(entity, componentId, data)`: 向实体添加组件
174
+ - `get(entity, componentId)`: 获取实体的组件数据(注意:只能获取已设置的组件,使用前请先用 `has()` 检查组件是否存在)
175
+ - `has(entity, componentId)`: 检查实体是否拥有指定组件
174
176
  - `delete(entity, componentId)`: 从实体移除组件
175
177
  - `setExclusive(componentId)`: 将组件标记为独占关系
176
178
  - `createQuery(componentIds)`: 创建查询
@@ -251,6 +253,7 @@ const restored = World.deserialize(readySnapshot);
251
253
 
252
254
  注意事项
253
255
 
256
+ - **重要警告**:`get()` 方法只能获取实体已设置的组件。如果尝试获取不存在的组件,会抛出错误。由于 `undefined` 是组件的有效值,不能使用 `get()` 的返回值是否为 `undefined` 来判断组件是否存在。请在使用 `get()` 之前先用 `has()` 方法检查组件是否存在。
254
257
  - 快照只包含实体、组件、以及 `EntityIdManager` 的分配器状态(用于保留下一次分配的 ID);并不会自动恢复已注册的系统、查询缓存或生命周期钩子。恢复后应由应用负责重新注册系统与钩子。
255
258
  - 若需要跨版本兼容,建议在持久化格式中包含 `version` 字段,并在恢复时进行格式兼容性检查与迁移。
256
259
 
package/changeset.d.ts CHANGED
@@ -29,8 +29,4 @@ export declare class ComponentChangeset {
29
29
  * Apply the changeset to existing components and return the final state
30
30
  */
31
31
  applyTo(existingComponents: Map<EntityId<any>, any>): Map<EntityId<any>, any>;
32
- /**
33
- * Get the final component types after applying changes
34
- */
35
- getFinalComponentTypes(existingComponents: Map<EntityId<any>, any>): EntityId<any>[];
36
32
  }
package/index.js CHANGED
@@ -277,17 +277,23 @@ class Archetype {
277
277
  if (index === undefined) {
278
278
  return;
279
279
  }
280
- this.entities.splice(index, 1);
281
- this.entityToIndex.delete(entityId);
282
280
  const removedData = new Map;
283
281
  for (const componentType of this.componentTypes) {
284
282
  const dataArray = this.getComponentData(componentType);
285
- removedData.set(componentType, dataArray[index]);
286
- dataArray.splice(index, 1);
283
+ removedData.set(componentType, dataArray[index] === MISSING_COMPONENT ? undefined : dataArray[index]);
287
284
  }
288
- for (let i = index;i < this.entities.length; i++) {
289
- this.entityToIndex.set(this.entities[i], i);
285
+ const lastIndex = this.entities.length - 1;
286
+ if (index !== lastIndex) {
287
+ const lastEntity = this.entities[lastIndex];
288
+ this.entities[index] = lastEntity;
289
+ this.entityToIndex.set(lastEntity, index);
290
+ for (const componentType of this.componentTypes) {
291
+ const dataArray = this.getComponentData(componentType);
292
+ [dataArray[index], dataArray[lastIndex]] = [dataArray[lastIndex], dataArray[index]];
293
+ }
290
294
  }
295
+ this.entities.pop();
296
+ this.entityToIndex.delete(entityId);
291
297
  return removedData;
292
298
  }
293
299
  exists(entityId) {
@@ -439,10 +445,6 @@ class ComponentChangeset {
439
445
  }
440
446
  return finalComponents;
441
447
  }
442
- getFinalComponentTypes(existingComponents) {
443
- const finalComponents = this.applyTo(existingComponents);
444
- return Array.from(finalComponents.keys()).sort((a, b) => a - b);
445
- }
446
448
  }
447
449
 
448
450
  // src/command-buffer.ts
@@ -790,6 +792,12 @@ class World {
790
792
  if (!archetype) {
791
793
  throw new Error(`Entity ${entityId} does not exist`);
792
794
  }
795
+ const detailedType = getDetailedIdType(componentType);
796
+ if (detailedType.type !== "wildcard-relation") {
797
+ if (!archetype.componentTypes.includes(componentType)) {
798
+ throw new Error(`Entity ${entityId} does not have component ${componentType}. Use has() to check component existence before calling get().`);
799
+ }
800
+ }
793
801
  return archetype.get(entityId, componentType);
794
802
  }
795
803
  registerSystem(system) {
@@ -989,11 +997,10 @@ class World {
989
997
  }
990
998
  }
991
999
  const finalComponents = changeset.applyTo(currentComponents);
992
- const finalComponentTypes = changeset.getFinalComponentTypes(currentComponents);
993
- const currentComponentTypes = currentArchetype.componentTypes.sort((a, b) => a - b);
994
- const needsArchetypeChange = finalComponentTypes.length !== currentComponentTypes.length || !finalComponentTypes.every((type, index) => type === currentComponentTypes[index]);
1000
+ const currentComponentTypes = currentArchetype.componentTypes;
1001
+ const needsArchetypeChange = finalComponents.size !== currentComponentTypes.length || !currentComponentTypes.every((type) => finalComponents.has(type));
995
1002
  if (needsArchetypeChange) {
996
- const newArchetype = this.ensureArchetype(finalComponentTypes);
1003
+ const newArchetype = this.ensureArchetype(finalComponents.keys().toArray());
997
1004
  currentArchetype.removeEntity(entityId);
998
1005
  newArchetype.addEntity(entityId, finalComponents);
999
1006
  this.entityToArchetype.set(entityId, newArchetype);
@@ -1024,7 +1031,7 @@ class World {
1024
1031
  return changeset;
1025
1032
  }
1026
1033
  ensureArchetype(componentTypes) {
1027
- const sortedTypes = [...componentTypes].sort((a, b) => a - b);
1034
+ const sortedTypes = componentTypes.toSorted((a, b) => a - b);
1028
1035
  const hashKey = this.createArchetypeSignature(sortedTypes);
1029
1036
  return getOrCreateWithSideEffect(this.archetypeBySignature, hashKey, () => {
1030
1037
  const newArchetype = new Archetype(sortedTypes);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codehz/ecs",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",