@driftengine/entities

中文文档
4.14.3发布于 2026/11/04Apache-2.0TypeScriptES Module近 30 天下载 2147

Entities with generational identity, component storage, systems and scenes

原始使用文档
文档目录4 节
  1. The handle
  2. Systems declare what they touch
  3. Prefabs and scenes
  4. Documentation

@driftengine/entities

Entities with generational identity, component storage, queries, systems with declared reads and writes, prefabs and scene serialization.

It imports no other engine package. An entity model that knew what a Transform was would be an entity model that knew it had a scene — and the argument for declared reads and writes is that a declaration is about a component id rather than about what the component means.

It also declares no component types. A consumer writes the ones its world needs, or takes them from whichever package owns the subsystem.

import { EntityAllocator } from '@driftengine/entities';

const entities = new EntityAllocator();
const player = entities.create();
entities.destroy(player);
entities.alive(player); // false — and the entity that takes the slot is a different handle

The handle

An entity is one number: a 26-bit index and a 27-bit generation, combined arithmetically. The generation is what stops a stale handle addressing a live entity — an index freed and reused makes every old reference silently valid, pointing at a different thing with no error anywhere.

The conventional packed Uint32 splits 24 and 8, so a slot's 257th reuse hands back a handle identical to its first. This split gives sixty-seven million entities and 134 million reuses of each slot, and costs four bytes per stored entity because a handle lives in a Float64Array.

26 + 27 is the whole 53-bit budget, exactly. There is no headroom, deliberately, and a test asserts the top handle so that is discovered here rather than in a wrong comparison.

Systems declare what they touch

const schedule = buildSchedule([
  { name: 'hunger', writes: [Hunger], everyTicks: 60, run(view) { … } },
  { name: 'decay', reads: [Hunger], writes: [Health], after: ['hunger'], run(view) { … } },
]);
runSchedule(world, schedule, tick);

The declaration is enforced. A system that writes something it did not declare is refused naming both — a schedule derived from a lie is worse than no schedule. writes implies reads.

A system that throws is skipped, and the others still run. By default the failure is logged once per system per world. Pass a reporter as the fourth argument to take every failure, every tick, with the system's name, the tick and the error, and nothing is logged; throw from it to make a failure fatal while developing:

runSchedule(world, schedule, tick, ({ system, tick, error }) => overlay.add(system, tick, error));

Order is declaration order, adjusted only by after, which is topologically sorted with declaration order as the tie-break. A cycle in after is refused naming the systems.

everyTicks strides the fixed step and is never a rate in seconds: a rate in wall-clock time leaves the determinism contract on its first dropped frame.

Destroying from inside a system is deferred to after it returns. A removal swaps the last dense entry into the hole, so removing mid-walk moves an entity the cursor has already passed into a position it has already visited — and that entity is never seen, silently, and only whichever one happened to be last. create and add are immediate, and a newly added entity may be visited in the same pass.

Prefabs and scenes

const guard = definePrefab('guard', [
  [Health, { current: 100 }],
  [Perception, { sightRange: 40 }],
]);
const spawned = instantiate(world, guard, { Perception: { sightRange: 60 } });

const scene = serializeWorld(world, [Health, Perception]);
const result = deserializeWorld(fresh, scene, [Health, Perception]);

A prefab is a value with no link back to its instances. A live link is what an editor wants, and it costs every instance a record of which fields it has overridden; Track K can add that record without changing what a prefab is.

A scene addresses every value by a stable field id, never a name and never a position. A field inserted in the middle would renumber everything after it, and a save would then load the right names with the wrong values — worse than failing, because it looks like it worked.

Loading across a change goes through driftscript's migration, unchanged: a field added since arrives with its default, one removed is dropped, and one whose type changed is refused naming it. Nothing is written into the world until every entity has migrated, so a refusal on the fifth does not leave four behind.

Entity references are remapped on load rather than preserved, because a saved handle's slot may be live in the world being loaded into. That only works for fields declared Entity; a number that happens to hold one is a number.

Documentation

This file is the reference, including the five things the model refuses and what would reverse each.

版本与发布记录

@driftengine/entities 共发布 52 个版本,首次发布于 2026/09/13;过去 12 个月发布了 52 个版本。

版本发布日期距今
4.14.3latest2026/11/049 天前
4.14.22026/11/0310 天前
4.14.12026/11/0310 天前
4.14.02026/10/1826 天前
4.13.62026/10/1529 天前
4.13.52026/10/121 个月前

关于 @driftengine/entities 的常见问题

@driftengine/entities 是什么?

Entities with generational identity, component storage, systems and scenes

如何安装 @driftengine/entities?

在项目目录运行 npm install @driftengine/entities,或使用 pnpm add / yarn add / bun add。

@driftengine/entities 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@driftengine/entities 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@driftengine/entities 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.12.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@driftengine/entities 可以商用吗?

@driftengine/entities 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@driftengine/entities 的下载量有多少?

根据 npm 官方下载统计,@driftengine/entities 在 2026/10/13 – 2026/11/11 这 30 天内被下载 2,147 次,日均约 72 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@driftengine/entities 还在维护吗?

最新版本 4.14.3 发布于 2026/11/04(9 天前);至今共发布 52 个版本,其中过去 12 个月发布了 52 个;首次发布于 2026/09/13。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。