createComponent()
创建一个同步 setup 的 Entity-local Component Definition。
签名
ts
interface ComponentContext {
readonly engine: Engine;
readonly entity: Entity;
onDestroy(handler: () => void): void;
}
interface ComponentDefinition<T> {
readonly name: string;
readonly setup: (context: ComponentContext) => T;
}
function createComponent<T>(
name: string,
setup: (context: ComponentContext) => T,
): ComponentDefinition<T>;每个 Entity 首次 add 该 Definition 时同步执行一次 setup,并原样保存返回值:
ts
interface Health {
current: number;
maximum: number;
}
const Health = createComponent('health', ({ entity, onDestroy }) => {
const health: Health = { current: 80, maximum: 100 };
const unsubscribe = observeHealth(entity.object, health);
onDestroy(unsubscribe);
return health;
});
const health = entity.addComponent(Health);
health.current -= 10;
entity.addComponent(Health) === health; // true,重复 add 幂等
entity.getComponent(Health) === health; // true,exact identity
entity.hasComponent(Health); // true
entity.removeComponent(Health); // true语义
name只用于诊断;同名调用仍返回互不冲突的 Definition identities。ComponentDefinition是 structural type,不携带 nominal brand;createComponent()是负责输入校验与冻结的规范构造入口,Engine 运行时只按传入 Definition 的 object identity 区分 owner,不验证其构造来源。- setup 只接收
engine、entity与 setup 期onDestroy,没有 installation input、enabled 或 frame hooks。 - setup 严格同步。Promise 与自定义 thenable result 会使 add 失败,失败候选不会进入 Entity lookup 或 World Query。
- setup result 可以是对象、
undefinedmarker 或 primitive。Engine 不 clone、freeze、proxy、markRaw或自动调用其dispose()。 - 可变状态应返回 mutable object。primitive 可以读取,但没有 set/replacement API;整体替换需 remove 后重新 add。
- 重复
addComponent()返回既有 exact result,不重新执行 setup,也不改变 cleanup 顺序。 removeComponent()对不存在的 Definition 返回false;存在时先发布 lookup/Query absence,再按 LIFO 尽量执行全部onDestroycallbacks。- Entity subtree destroy 采用 child-before-parent;单个 Entity 内 Script 先于 Component cleanup,Component 又先于 Engine-owned 标准 Three.js 资源释放。
- Vue 集成不会自动处理动态 Component result;是否使用
reactive、shallowRef或markRaw由业务决定。