@codehz/ecs 0.6.9 → 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 +34 -5
- package/package.json +1 -1
- package/world.mjs +261 -121
- 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
|
@@ -64,10 +64,11 @@ declare function decodeRelationId(relationId: RelationId<any>): {
|
|
|
64
64
|
};
|
|
65
65
|
//#endregion
|
|
66
66
|
//#region src/core/component-registry.d.ts
|
|
67
|
+
type ComponentMerge<T = any> = (prev: T, next: T) => T;
|
|
67
68
|
/**
|
|
68
69
|
* Component options that define intrinsic properties
|
|
69
70
|
*/
|
|
70
|
-
interface ComponentOptions {
|
|
71
|
+
interface ComponentOptions<T = any> {
|
|
71
72
|
/**
|
|
72
73
|
* Optional name for the component (for serialization/debugging)
|
|
73
74
|
*/
|
|
@@ -93,6 +94,10 @@ interface ComponentOptions {
|
|
|
93
94
|
* Inspired by Flecs' DontFragment trait.
|
|
94
95
|
*/
|
|
95
96
|
dontFragment?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Custom merge behavior for repeated set() of the same componentType in a single sync batch.
|
|
99
|
+
*/
|
|
100
|
+
merge?: ComponentMerge<T>;
|
|
96
101
|
}
|
|
97
102
|
/**
|
|
98
103
|
* Allocate a new component ID from the global allocator.
|
|
@@ -108,7 +113,7 @@ interface ComponentOptions {
|
|
|
108
113
|
* // With name and options
|
|
109
114
|
* const ChildOf = component({ name: "ChildOf", exclusive: true });
|
|
110
115
|
*/
|
|
111
|
-
declare function component<T = void>(nameOrOptions?: string | ComponentOptions): ComponentId<T>;
|
|
116
|
+
declare function component<T = void>(nameOrOptions?: string | ComponentOptions<T>): ComponentId<T>;
|
|
112
117
|
/**
|
|
113
118
|
* Get a component ID by its registered name
|
|
114
119
|
* @param name The component name
|
|
@@ -229,6 +234,7 @@ interface LifecycleHookEntry {
|
|
|
229
234
|
componentTypes: readonly ComponentType<any>[];
|
|
230
235
|
requiredComponents: EntityId<any>[];
|
|
231
236
|
optionalComponents: EntityId<any>[];
|
|
237
|
+
filter: QueryFilter;
|
|
232
238
|
hook: LifecycleHook<any>;
|
|
233
239
|
}
|
|
234
240
|
//#endregion
|
|
@@ -286,6 +292,7 @@ declare class Archetype {
|
|
|
286
292
|
addEntity(entityId: EntityId, componentData: Map<EntityId<any>, any>): void;
|
|
287
293
|
private addDontFragmentRelations;
|
|
288
294
|
getEntity(entityId: EntityId): Map<EntityId<any>, any> | undefined;
|
|
295
|
+
getEntityDontFragmentRelations(entityId: EntityId): Map<EntityId<any>, any> | undefined;
|
|
289
296
|
dump(): Array<{
|
|
290
297
|
entity: EntityId;
|
|
291
298
|
components: Map<EntityId<any>, any>;
|
|
@@ -457,9 +464,14 @@ declare class World {
|
|
|
457
464
|
private relationEntityIdsByTarget;
|
|
458
465
|
private queries;
|
|
459
466
|
private queryCache;
|
|
460
|
-
private commandBuffer;
|
|
461
467
|
private legacyHooks;
|
|
462
468
|
private hooks;
|
|
469
|
+
private commandBuffer;
|
|
470
|
+
private readonly _changeset;
|
|
471
|
+
/** Cached command processor context to avoid per-entity object allocation */
|
|
472
|
+
private readonly _commandCtx;
|
|
473
|
+
/** Cached hooks context to avoid per-entity object allocation */
|
|
474
|
+
private readonly _hooksCtx;
|
|
463
475
|
constructor(snapshot?: SerializedWorld);
|
|
464
476
|
private deserializeSnapshot;
|
|
465
477
|
private createArchetypeSignature;
|
|
@@ -495,6 +507,12 @@ declare class World {
|
|
|
495
507
|
* }
|
|
496
508
|
*/
|
|
497
509
|
exists(entityId: EntityId): boolean;
|
|
510
|
+
private assertEntityExists;
|
|
511
|
+
private assertComponentTypeValid;
|
|
512
|
+
private assertSetComponentTypeValid;
|
|
513
|
+
private resolveSetOperation;
|
|
514
|
+
private resolveRemoveOperation;
|
|
515
|
+
private getComponentEntityWildcardRelations;
|
|
498
516
|
/**
|
|
499
517
|
* Adds or updates a component on an entity (or marks void component as present).
|
|
500
518
|
* The change is buffered and takes effect after calling `world.sync()`.
|
|
@@ -645,12 +663,14 @@ declare class World {
|
|
|
645
663
|
* @overload hook<const T extends readonly ComponentType<any>[]>(
|
|
646
664
|
* componentTypes: T,
|
|
647
665
|
* hook: LifecycleHook<T> | LifecycleCallback<T>,
|
|
666
|
+
* filter?: QueryFilter,
|
|
648
667
|
* ): () => void
|
|
649
668
|
* Registers a hook for multiple component types.
|
|
650
|
-
* The hook is triggered when
|
|
669
|
+
* The hook is triggered when entities enter/exit the matching set.
|
|
651
670
|
*
|
|
652
671
|
* @param componentTypesOrSingle - A single component type or an array of component types
|
|
653
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
|
|
654
674
|
* @returns A function that unsubscribes the hook when called
|
|
655
675
|
*
|
|
656
676
|
* @throws {Error} If no required components are specified in array overload
|
|
@@ -668,9 +688,18 @@ declare class World {
|
|
|
668
688
|
* const unsubscribe = world.hook([Position], (event, entityId, position) => {
|
|
669
689
|
* if (event === "init") console.log("Initialized");
|
|
670
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
|
+
* );
|
|
671
700
|
*/
|
|
672
701
|
hook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T> | LegacyLifecycleCallback<T>): () => void;
|
|
673
|
-
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;
|
|
674
703
|
/** @deprecated use the unsubscribe function returned by hook() instead */
|
|
675
704
|
unhook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T>): void;
|
|
676
705
|
/** @deprecated use the unsubscribe function returned by hook() instead */
|