@codehz/ecs 0.3.15 → 0.4.0

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
@@ -6,11 +6,10 @@
6
6
 
7
7
  - 🚀 高性能:基于 Archetype 的组件存储和高效的查询系统
8
8
  - 🔧 类型安全:完整的 TypeScript 支持
9
- - 🏗️ 模块化:清晰的架构,支持自定义系统和组件
9
+ - 🏗️ 模块化:清晰的架构,支持自定义组件
10
10
  - 📦 轻量级:零依赖,易于集成
11
11
  - ⚡ 内存高效:连续内存布局,优化的迭代性能
12
12
  - 🎣 生命周期钩子:支持组件和通配符关系的事件监听
13
- - 🔄 系统调度:支持系统依赖关系和拓扑排序执行
14
13
 
15
14
  ## 安装
16
15
 
@@ -180,11 +179,9 @@ bun run examples/simple/demo.ts
180
179
  - `delete(entity, componentId)`: 从实体移除组件
181
180
  - `setExclusive(componentId)`: 将组件标记为独占关系
182
181
  - `createQuery(componentIds)`: 创建查询
183
- - `registerSystem(system, dependencies?)`: 注册系统
184
182
  - `hook(componentId, hook)`: 注册组件或通配符关系生命周期钩子
185
183
  - `unhook(componentId, hook)`: 注销组件或通配符关系生命周期钩子
186
- - `update(...params)`: 更新世界(参数取决于泛型配置)
187
- - `sync()`: 应用命令缓冲区
184
+ - `sync()`: 执行所有延迟命令
188
185
 
189
186
  ### 序列化(快照)
190
187
 
@@ -258,7 +255,7 @@ const restored = World.deserialize(readySnapshot);
258
255
  注意事项
259
256
 
260
257
  - **重要警告**:`get()` 方法只能获取实体已设置的组件。如果尝试获取不存在的组件,会抛出错误。由于 `undefined` 是组件的有效值,不能使用 `get()` 的返回值是否为 `undefined` 来判断组件是否存在。请在使用 `get()` 之前先用 `has()` 方法检查组件是否存在。
261
- - 快照只包含实体、组件、以及 `EntityIdManager` 的分配器状态(用于保留下一次分配的 ID);并不会自动恢复已注册的系统、查询缓存或生命周期钩子。恢复后应由应用负责重新注册系统与钩子。
258
+ - 快照只包含实体、组件、以及 `EntityIdManager` 的分配器状态(用于保留下一次分配的 ID);并不会自动恢复查询缓存或生命周期钩子。恢复后应由应用负责重新注册钩子。
262
259
  - 若需要跨版本兼容,建议在持久化格式中包含 `version` 字段,并在恢复时进行格式兼容性检查与迁移。
263
260
 
264
261
  ### Entity
@@ -271,57 +268,81 @@ const restored = World.deserialize(readySnapshot);
271
268
  - `getEntities()`: 获取所有匹配实体的ID列表
272
269
  - `getEntitiesWithComponents(componentIds)`: 获取实体及其组件数据
273
270
 
274
- ### System
271
+ ## System 迁移到 Pipeline
275
272
 
276
- 实现 `System` 接口来创建自定义系统:
273
+ v0.4.0 开始,本库移除了内置的 `System` 和 `SystemScheduler` 功能。推荐使用 `@codehz/pipeline` 作为替代方案来组织游戏循环逻辑。
277
274
 
278
- ```typescript
279
- class MySystem implements System {
280
- update(): void {
281
- // 系统逻辑
282
- }
283
- }
284
- ```
275
+ ### 为什么移除 System?
285
276
 
286
- 如果需要接收额外参数(如时间增量),可以指定泛型参数:
277
+ - **简化库的维护**:System 调度器增加了代码复杂度,但其功能可以通过更通用的 pipeline 模式实现
278
+ - **更灵活的执行控制**:Pipeline 模式允许更细粒度的控制,支持异步操作和条件执行
279
+ - **更好的关注点分离**:ECS 库专注于实体和组件管理,系统调度由外部库处理
287
280
 
288
- ```typescript
289
- class MovementSystem implements System<[deltaTime: number]> {
290
- update(deltaTime: number): void {
291
- // 使用 deltaTime 更新位置
292
- }
293
- }
294
- ```
281
+ ### 迁移示例
295
282
 
296
- 系统支持依赖关系排序,确保正确的执行顺序。依赖关系可以通过系统的 `dependencies` 属性指定:
283
+ **旧代码(使用 System)**:
297
284
 
298
285
  ```typescript
299
- class InputSystem implements System<[deltaTime: number]> {
300
- readonly dependencies: readonly System<[deltaTime: number]>[] = [];
301
- update(deltaTime: number): void {
302
- // 处理输入
303
- }
304
- }
286
+ import { World, component } from "@codehz/ecs";
287
+ import type { System } from "@codehz/ecs";
305
288
 
306
289
  class MovementSystem implements System<[deltaTime: number]> {
307
- readonly dependencies: readonly System<[deltaTime: number]>[];
290
+ private query: Query;
308
291
 
309
- constructor(inputSystem: InputSystem) {
310
- this.dependencies = [inputSystem]; // 指定依赖
292
+ constructor(world: World<[deltaTime: number]>) {
293
+ this.query = world.createQuery([PositionId, VelocityId]);
311
294
  }
312
295
 
313
296
  update(deltaTime: number): void {
314
- // 更新位置
297
+ this.query.forEach([PositionId, VelocityId], (entity, position, velocity) => {
298
+ position.x += velocity.x * deltaTime;
299
+ position.y += velocity.y * deltaTime;
300
+ });
315
301
  }
316
302
  }
317
303
 
318
- // 注册系统
319
- const inputSystem = new InputSystem();
320
- world.registerSystem(inputSystem);
321
- world.registerSystem(new MovementSystem(inputSystem), [inputSystem]); // 也可以在注册时指定额外依赖
304
+ const world = new World<[deltaTime: number]>();
305
+ world.registerSystem(new MovementSystem(world));
306
+ world.update(0.016); // 自动调用 sync()
307
+ ```
308
+
309
+ **新代码(使用 Pipeline)**:
310
+
311
+ ```typescript
312
+ import { pipeline } from "@codehz/pipeline";
313
+ import { World, component } from "@codehz/ecs";
314
+
315
+ const world = new World();
316
+ const movementQuery = world.createQuery([PositionId, VelocityId]);
317
+
318
+ const gameLoop = pipeline<{ deltaTime: number }>()
319
+ .addPass((env) => {
320
+ movementQuery.forEach([PositionId, VelocityId], (entity, position, velocity) => {
321
+ position.x += velocity.x * env.deltaTime;
322
+ position.y += velocity.y * env.deltaTime;
323
+ });
324
+ })
325
+ // 重要:world.sync() 必须作为最后一个 pass 调用,以还原之前 world.update() 的自动提交行为
326
+ .addPass(() => {
327
+ world.sync();
328
+ })
329
+ .build();
330
+
331
+ gameLoop({ deltaTime: 0.016 });
322
332
  ```
323
333
 
324
- 系统将按照拓扑排序执行,依赖系统始终在被依赖系统之前运行。
334
+ ### 关键变化
335
+
336
+ 1. **移除泛型参数**:`World` 不再需要 `UpdateParams` 泛型参数
337
+ 2. **移除的方法**:`registerSystem()` 和 `update()` 方法已移除
338
+ 3. **手动调用 sync()**:之前 `world.update()` 会自动调用 `sync()`,现在需要在 pipeline 末尾显式调用
339
+ 4. **执行顺序**:Pass 的执行顺序由添加顺序决定,无需手动声明依赖关系
340
+
341
+ ### 安装 Pipeline
342
+
343
+ ```bash
344
+ bun add @codehz/pipeline
345
+ ```
325
346
 
326
347
  ## 性能特点
327
348
 
@@ -354,8 +375,6 @@ src/
354
375
  ├── archetype.ts # Archetype 系统(高效组件存储)
355
376
  ├── query.ts # 查询系统
356
377
  ├── query-filter.ts # 查询过滤器
357
- ├── system.ts # 系统接口
358
- ├── system-scheduler.ts # 系统调度器
359
378
  ├── command-buffer.ts # 命令缓冲区
360
379
  ├── types.ts # 类型定义
361
380
  ├── utils.ts # 工具函数
@@ -364,9 +383,11 @@ src/
364
383
  └── *.perf.test.ts # 性能测试
365
384
 
366
385
  examples/
367
- └── simple/
368
- ├── demo.ts # 基本示例
369
- └── README.md # 示例说明
386
+ ├── simple/
387
+ ├── demo.ts # 基本示例
388
+ └── README.md # 示例说明
389
+ └── advanced-scheduling/
390
+ └── demo.ts # Pipeline 调度示例
370
391
 
371
392
  scripts/
372
393
  ├── build.ts # 构建脚本