@codehz/ecs 0.6.10 → 0.6.11
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 +24 -1
- package/builder.d.mts +14 -2
- package/package.json +1 -1
- package/world.mjs +11 -8
- package/world.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -124,6 +124,29 @@ const unhook = world.hook([PositionId, { optional: VelocityId }], {
|
|
|
124
124
|
});
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
多组件 `hook()` 还支持第三个可选参数 `filter`(与 `createQuery()` 的过滤语义一致),可用于排除带有某些负面组件的实体:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const DisabledId = component<void>();
|
|
131
|
+
|
|
132
|
+
const unhook = world.hook(
|
|
133
|
+
[PositionId, VelocityId],
|
|
134
|
+
{
|
|
135
|
+
on_set: (entityId, position, velocity) => {
|
|
136
|
+
// 实体进入匹配集合时触发(包括移除 Disabled 后重新进入)
|
|
137
|
+
console.log("active", entityId, position, velocity);
|
|
138
|
+
},
|
|
139
|
+
on_remove: (entityId, position, velocity) => {
|
|
140
|
+
// 实体退出匹配集合时触发(包括新增 Disabled 后退出)
|
|
141
|
+
console.log("inactive", entityId, position, velocity);
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
negativeComponentTypes: [DisabledId],
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
```
|
|
149
|
+
|
|
127
150
|
### 通配符关系钩子
|
|
128
151
|
|
|
129
152
|
ECS 支持通配符关系钩子,可以监听特定组件的所有关系变化:
|
|
@@ -226,7 +249,7 @@ bun run examples/simple/demo.ts
|
|
|
226
249
|
- `delete(entity)`: 销毁实体及其所有组件
|
|
227
250
|
- `query(componentIds)`: 快速查询具有指定组件的实体
|
|
228
251
|
- `createQuery(componentIds)`: 创建可重用的查询对象
|
|
229
|
-
- `hook(componentIds, hook)`:
|
|
252
|
+
- `hook(componentIds, hook, filter?)`: 注册生命周期钩子,返回卸载函数(数组形式支持可选 filter)
|
|
230
253
|
- `serialize()`: 序列化世界状态为快照对象
|
|
231
254
|
- `sync()`: 执行所有延迟命令
|
|
232
255
|
|
package/builder.d.mts
CHANGED
|
@@ -234,6 +234,7 @@ interface LifecycleHookEntry {
|
|
|
234
234
|
componentTypes: readonly ComponentType<any>[];
|
|
235
235
|
requiredComponents: EntityId<any>[];
|
|
236
236
|
optionalComponents: EntityId<any>[];
|
|
237
|
+
filter: QueryFilter;
|
|
237
238
|
hook: LifecycleHook<any>;
|
|
238
239
|
}
|
|
239
240
|
//#endregion
|
|
@@ -662,12 +663,14 @@ declare class World {
|
|
|
662
663
|
* @overload hook<const T extends readonly ComponentType<any>[]>(
|
|
663
664
|
* componentTypes: T,
|
|
664
665
|
* hook: LifecycleHook<T> | LifecycleCallback<T>,
|
|
666
|
+
* filter?: QueryFilter,
|
|
665
667
|
* ): () => void
|
|
666
668
|
* Registers a hook for multiple component types.
|
|
667
|
-
* The hook is triggered when
|
|
669
|
+
* The hook is triggered when entities enter/exit the matching set.
|
|
668
670
|
*
|
|
669
671
|
* @param componentTypesOrSingle - A single component type or an array of component types
|
|
670
672
|
* @param hook - Either a hook object with on_init/on_set/on_remove handlers, or a callback function
|
|
673
|
+
* @param filter - Optional filter, only applied to array overload
|
|
671
674
|
* @returns A function that unsubscribes the hook when called
|
|
672
675
|
*
|
|
673
676
|
* @throws {Error} If no required components are specified in array overload
|
|
@@ -685,9 +688,18 @@ declare class World {
|
|
|
685
688
|
* const unsubscribe = world.hook([Position], (event, entityId, position) => {
|
|
686
689
|
* if (event === "init") console.log("Initialized");
|
|
687
690
|
* });
|
|
691
|
+
*
|
|
692
|
+
* // With filter
|
|
693
|
+
* const unsubscribe2 = world.hook(
|
|
694
|
+
* [Position, Velocity],
|
|
695
|
+
* {
|
|
696
|
+
* on_set: (entityId, position, velocity) => console.log(entityId, position, velocity),
|
|
697
|
+
* },
|
|
698
|
+
* { negativeComponentTypes: [Disabled] },
|
|
699
|
+
* );
|
|
688
700
|
*/
|
|
689
701
|
hook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T> | LegacyLifecycleCallback<T>): () => void;
|
|
690
|
-
hook<const T extends readonly ComponentType<any>[]>(componentTypes: T, hook: LifecycleHook<T> | LifecycleCallback<T
|
|
702
|
+
hook<const T extends readonly ComponentType<any>[]>(componentTypes: T, hook: LifecycleHook<T> | LifecycleCallback<T>, filter?: QueryFilter): () => void;
|
|
691
703
|
/** @deprecated use the unsubscribe function returned by hook() instead */
|
|
692
704
|
unhook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T>): void;
|
|
693
705
|
/** @deprecated use the unsubscribe function returned by hook() instead */
|
package/package.json
CHANGED
package/world.mjs
CHANGED
|
@@ -1686,12 +1686,14 @@ function triggerMultiComponentHooks(ctx, entityId, addedComponents, removedCompo
|
|
|
1686
1686
|
const anyRequiredAdded = requiredComponents.some((c) => anyComponentMatches(addedComponents, c));
|
|
1687
1687
|
const anyOptionalAdded = optionalComponents.some((c) => anyComponentMatches(addedComponents, c));
|
|
1688
1688
|
const anyOptionalRemoved = optionalComponents.some((c) => anyComponentMatches(removedComponents, c));
|
|
1689
|
-
if ((anyRequiredAdded || anyOptionalAdded || anyOptionalRemoved) && entityHasAllComponents(ctx, entityId, requiredComponents)) hook.on_set(entityId, ...collectMultiHookComponents(ctx, entityId, componentTypes));
|
|
1689
|
+
if (!oldArchetype.matchingMultiHooks.has(entry) || (anyRequiredAdded || anyOptionalAdded || anyOptionalRemoved) && entityHasAllComponents(ctx, entityId, requiredComponents)) hook.on_set(entityId, ...collectMultiHookComponents(ctx, entityId, componentTypes));
|
|
1690
1690
|
}
|
|
1691
|
-
|
|
1691
|
+
for (const entry of oldArchetype.matchingMultiHooks) {
|
|
1692
1692
|
const { hook, requiredComponents, componentTypes } = entry;
|
|
1693
1693
|
if (!hook.on_remove) continue;
|
|
1694
|
-
|
|
1694
|
+
const lostRequiredMatch = requiredComponents.some((c) => anyComponentMatches(removedComponents, c)) && entityHadAllComponentsBefore(ctx, entityId, requiredComponents, removedComponents) && !entityHasAllComponents(ctx, entityId, requiredComponents);
|
|
1695
|
+
const exitedMatchingSet = !newArchetype.matchingMultiHooks.has(entry);
|
|
1696
|
+
if (lostRequiredMatch || exitedMatchingSet) hook.on_remove(entityId, ...collectMultiHookComponentsWithRemoved(ctx, entityId, componentTypes, removedComponents));
|
|
1695
1697
|
}
|
|
1696
1698
|
}
|
|
1697
1699
|
function entityHasAllComponents(ctx, entityId, requiredComponents) {
|
|
@@ -2190,7 +2192,7 @@ var World = class {
|
|
|
2190
2192
|
}
|
|
2191
2193
|
return archetype.getOptional(entityId, componentType);
|
|
2192
2194
|
}
|
|
2193
|
-
hook(componentTypesOrSingle, hook) {
|
|
2195
|
+
hook(componentTypesOrSingle, hook, filter) {
|
|
2194
2196
|
if (typeof hook === "function") if (Array.isArray(componentTypesOrSingle)) {
|
|
2195
2197
|
const callback = hook;
|
|
2196
2198
|
hook = {
|
|
@@ -2217,14 +2219,15 @@ var World = class {
|
|
|
2217
2219
|
componentTypes,
|
|
2218
2220
|
requiredComponents,
|
|
2219
2221
|
optionalComponents,
|
|
2222
|
+
filter: filter || {},
|
|
2220
2223
|
hook
|
|
2221
2224
|
};
|
|
2222
2225
|
this.hooks.add(entry);
|
|
2223
2226
|
for (const archetype of this.archetypes) if (this.archetypeMatchesHook(archetype, entry)) archetype.matchingMultiHooks.add(entry);
|
|
2224
2227
|
const multiHook = hook;
|
|
2225
|
-
if (multiHook.on_init !== void 0) {
|
|
2226
|
-
|
|
2227
|
-
for (const
|
|
2228
|
+
if (multiHook.on_init !== void 0) for (const archetype of this.archetypes) {
|
|
2229
|
+
if (!this.archetypeMatchesHook(archetype, entry)) continue;
|
|
2230
|
+
for (const entityId of archetype.getEntities()) {
|
|
2228
2231
|
const components = collectMultiHookComponents(this.createHooksContext(), entityId, componentTypes);
|
|
2229
2232
|
multiHook.on_init(entityId, ...components);
|
|
2230
2233
|
}
|
|
@@ -2567,7 +2570,7 @@ var World = class {
|
|
|
2567
2570
|
return componentId !== void 0 && archetype.hasRelationWithComponentId(componentId);
|
|
2568
2571
|
}
|
|
2569
2572
|
return archetype.componentTypeSet.has(c) || isDontFragmentRelation(c);
|
|
2570
|
-
});
|
|
2573
|
+
}) && matchesFilter(archetype, entry.filter);
|
|
2571
2574
|
}
|
|
2572
2575
|
archetypeReferencesEntity(archetype, entityId) {
|
|
2573
2576
|
return archetype.componentTypes.some((ct) => ct === entityId || isEntityRelation(ct) && getTargetIdFromRelationId(ct) === entityId);
|