feat(ecs): 核心系统改进 - 句柄、调度、变更检测与查询编译 (#304)

新增功能:
- EntityHandle: 轻量级实体句柄 (28位索引 + 20位代数)
- SystemScheduler: 声明式系统调度,支持 @Stage/@Before/@After/@InSet 装饰器
- EpochManager: 帧级变更检测
- CompiledQuery: 预编译类型安全查询

API 改进:
- EntitySystem 添加 getBefore()/getAfter()/getSets() getter 方法
- Entity 添加 markDirty() 辅助方法
- IScene 添加 epochManager 属性
- CommandBuffer.pendingCount 修正为返回实际操作数

文档更新:
- 更新系统调度和查询相关文档
This commit is contained in:
YHH
2025-12-15 09:17:00 +08:00
committed by GitHub
parent b5158b6ac6
commit cd6ef222d1
27 changed files with 5233 additions and 43 deletions

View File

@@ -57,6 +57,27 @@ export abstract class Component implements IComponent {
@Int32
public entityId: number | null = null;
/**
* 最后写入的 epoch
*
* 用于帧级变更检测,记录组件最后一次被修改时的 epoch。
* 0 表示从未被标记为已修改。
*
* Last write epoch.
* Used for frame-level change detection, records the epoch when component was last modified.
* 0 means never marked as modified.
*/
private _lastWriteEpoch: number = 0;
/**
* 获取最后写入的 epoch
*
* Get last write epoch.
*/
public get lastWriteEpoch(): number {
return this._lastWriteEpoch;
}
/**
* 创建组件实例
*
@@ -66,6 +87,29 @@ export abstract class Component implements IComponent {
this.id = Component.idGenerator++;
}
/**
* 标记组件为已修改
*
* 调用此方法会更新组件的 lastWriteEpoch 为当前帧的 epoch。
* 系统可以通过比较 lastWriteEpoch 和上次检查的 epoch 来判断组件是否发生变更。
*
* Mark component as modified.
* Calling this method updates the component's lastWriteEpoch to the current frame's epoch.
* Systems can compare lastWriteEpoch with their last checked epoch to detect changes.
*
* @param epoch 当前帧的 epoch | Current frame's epoch
*
* @example
* ```typescript
* // 在修改组件数据后调用
* velocity.x = 10;
* velocity.markDirty(scene.epochManager.current);
* ```
*/
public markDirty(epoch: number): void {
this._lastWriteEpoch = epoch;
}
/**
* 组件添加到实体时的回调
*

View File

@@ -1,6 +1,6 @@
import { Entity } from '../Entity';
import { Component } from '../Component';
import { ComponentType } from './ComponentStorage';
import { ComponentType, ComponentRegistry } from './ComponentStorage';
import { IScene } from '../IScene';
import { createLogger } from '../../Utils/Logger';
@@ -38,6 +38,24 @@ export interface DeferredCommand {
value?: boolean;
}
/**
* 每个实体的待处理操作
* Pending operations per entity
*
* 使用 last-write-wins 语义进行去重。
* Uses last-write-wins semantics for deduplication.
*/
interface PendingPerEntity {
/** 是否销毁实体(如果为 true忽略其他操作| Destroy entity (if true, ignores other operations) */
bDestroy?: boolean;
/** 最终的激活状态 | Final active state */
active?: boolean;
/** 要添加的组件typeId -> Component| Components to add (typeId -> Component) */
adds?: Map<number, Component>;
/** 要移除的组件类型 ID 集合 | Component type IDs to remove */
removes?: Set<number>;
}
/**
* 命令缓冲区 - 用于延迟执行实体操作
* Command Buffer - for deferred entity operations
@@ -45,8 +63,17 @@ export interface DeferredCommand {
* 在系统的 process() 方法中使用 CommandBuffer 可以避免迭代过程中修改实体列表,
* 从而提高性能(无需每帧拷贝数组)并保证迭代安全。
*
* Using CommandBuffer in system's process() method avoids modifying entity list during iteration,
* improving performance (no array copy per frame) and ensuring iteration safety.
* 特点:
* - **去重**: 同一实体的多次同类操作会被合并last-write-wins
* - **有序执行**: removes -> adds -> active -> destroy
* - **冲突处理**: addComponent 会取消同类型的 removeComponent反之亦然
* - **销毁优先**: destroyEntity 会取消该实体的所有其他操作
*
* Features:
* - **Deduplication**: Multiple operations on same entity are merged (last-write-wins)
* - **Ordered execution**: removes -> adds -> active -> destroy
* - **Conflict handling**: addComponent cancels same-type removeComponent and vice versa
* - **Destroy priority**: destroyEntity cancels all other operations for that entity
*
* @example
* ```typescript
@@ -66,7 +93,10 @@ export interface DeferredCommand {
* ```
*/
export class CommandBuffer {
/** 命令队列 | Command queue */
/** 每个实体的待处理操作 | Pending operations per entity */
private _pending: Map<Entity, PendingPerEntity> = new Map();
/** 旧式命令队列(用于兼容)| Legacy command queue (for compatibility) */
private _commands: DeferredCommand[] = [];
/** 关联的场景 | Associated scene */
@@ -75,6 +105,9 @@ export class CommandBuffer {
/** 是否启用调试日志 | Enable debug logging */
private _debug: boolean = false;
/** 是否使用去重模式 | Whether to use deduplication mode */
private _useDeduplication: boolean = true;
/**
* 创建命令缓冲区
* Create command buffer
@@ -103,11 +136,34 @@ export class CommandBuffer {
return this._scene;
}
/**
* 设置是否使用去重模式
* Set whether to use deduplication mode
*
* @param enabled - 是否启用 | Whether to enable
*/
public setDeduplication(enabled: boolean): void {
this._useDeduplication = enabled;
}
/**
* 获取待执行的命令数量
* Get pending command count
*
* 返回实际的操作数量,而不是实体数量。
* Returns actual operation count, not entity count.
*/
public get pendingCount(): number {
if (this._useDeduplication) {
let count = 0;
for (const ops of this._pending.values()) {
if (ops.bDestroy) count++;
if (ops.active !== undefined) count++;
if (ops.adds) count += ops.adds.size;
if (ops.removes) count += ops.removes.size;
}
return count;
}
return this._commands.length;
}
@@ -116,25 +172,82 @@ export class CommandBuffer {
* Check if there are pending commands
*/
public get hasPending(): boolean {
if (this._useDeduplication) {
return this._pending.size > 0;
}
return this._commands.length > 0;
}
/**
* 获取或创建实体的待处理操作
* Get or create pending operations for entity
*/
private getPending(entity: Entity): PendingPerEntity {
let pending = this._pending.get(entity);
if (!pending) {
pending = {};
this._pending.set(entity, pending);
}
return pending;
}
/**
* 获取组件类型 ID位索引
* Get component type ID (bit index)
*/
private getTypeId(componentOrType: Component | ComponentType): number {
if (typeof componentOrType === 'function') {
// ComponentType
return ComponentRegistry.getBitIndex(componentOrType);
} else {
// Component instance
return ComponentRegistry.getBitIndex(componentOrType.constructor as ComponentType);
}
}
/**
* 延迟添加组件
* Deferred add component
*
* 如果之前有同类型的 removeComponent会被取消。
* If there was a removeComponent for the same type, it will be canceled.
*
* @param entity - 目标实体 | Target entity
* @param component - 要添加的组件 | Component to add
*/
public addComponent(entity: Entity, component: Component): void {
this._commands.push({
type: CommandType.ADD_COMPONENT,
entity,
component
});
if (this._useDeduplication) {
const pending = this.getPending(entity);
if (this._debug) {
logger.debug(`CommandBuffer: 延迟添加组件 ${component.constructor.name} 到实体 ${entity.name}`);
// 如果实体已标记销毁,忽略
if (pending.bDestroy) {
if (this._debug) {
logger.debug(`CommandBuffer: 忽略添加组件,实体 ${entity.name} 已标记销毁`);
}
return;
}
const typeId = this.getTypeId(component);
// 取消同类型的 remove
pending.removes?.delete(typeId);
// 添加(覆盖同类型的之前的 add
if (!pending.adds) {
pending.adds = new Map();
}
pending.adds.set(typeId, component);
if (this._debug) {
logger.debug(`CommandBuffer: 延迟添加组件 ${component.constructor.name} 到实体 ${entity.name}`);
}
} else {
// 旧模式
this._commands.push({
type: CommandType.ADD_COMPONENT,
entity,
component
});
}
}
@@ -142,18 +255,45 @@ export class CommandBuffer {
* 延迟移除组件
* Deferred remove component
*
* 如果之前有同类型的 addComponent会被取消。
* If there was an addComponent for the same type, it will be canceled.
*
* @param entity - 目标实体 | Target entity
* @param componentType - 要移除的组件类型 | Component type to remove
*/
public removeComponent<T extends Component>(entity: Entity, componentType: ComponentType<T>): void {
this._commands.push({
type: CommandType.REMOVE_COMPONENT,
entity,
componentType
});
if (this._useDeduplication) {
const pending = this.getPending(entity);
if (this._debug) {
logger.debug(`CommandBuffer: 延迟移除组件 ${componentType.name} 从实体 ${entity.name}`);
// 如果实体已标记销毁,忽略
if (pending.bDestroy) {
if (this._debug) {
logger.debug(`CommandBuffer: 忽略移除组件,实体 ${entity.name} 已标记销毁`);
}
return;
}
const typeId = this.getTypeId(componentType);
// 取消同类型的 add
pending.adds?.delete(typeId);
// 添加到移除列表
if (!pending.removes) {
pending.removes = new Set();
}
pending.removes.add(typeId);
if (this._debug) {
logger.debug(`CommandBuffer: 延迟移除组件 ${componentType.name} 从实体 ${entity.name}`);
}
} else {
// 旧模式
this._commands.push({
type: CommandType.REMOVE_COMPONENT,
entity,
componentType
});
}
}
@@ -161,16 +301,32 @@ export class CommandBuffer {
* 延迟销毁实体
* Deferred destroy entity
*
* 会取消该实体的所有其他待处理操作。
* Cancels all other pending operations for this entity.
*
* @param entity - 要销毁的实体 | Entity to destroy
*/
public destroyEntity(entity: Entity): void {
this._commands.push({
type: CommandType.DESTROY_ENTITY,
entity
});
if (this._useDeduplication) {
const pending = this.getPending(entity);
if (this._debug) {
logger.debug(`CommandBuffer: 延迟销毁实体 ${entity.name}`);
// 清除所有其他操作
pending.adds?.clear();
pending.removes?.clear();
delete pending.active;
// 标记销毁
pending.bDestroy = true;
if (this._debug) {
logger.debug(`CommandBuffer: 延迟销毁实体 ${entity.name}`);
}
} else {
// 旧模式
this._commands.push({
type: CommandType.DESTROY_ENTITY,
entity
});
}
}
@@ -182,14 +338,30 @@ export class CommandBuffer {
* @param active - 激活状态 | Active state
*/
public setEntityActive(entity: Entity, active: boolean): void {
this._commands.push({
type: CommandType.SET_ENTITY_ACTIVE,
entity,
value: active
});
if (this._useDeduplication) {
const pending = this.getPending(entity);
if (this._debug) {
logger.debug(`CommandBuffer: 延迟设置实体 ${entity.name} 激活状态为 ${active}`);
// 如果实体已标记销毁,忽略
if (pending.bDestroy) {
if (this._debug) {
logger.debug(`CommandBuffer: 忽略设置激活状态,实体 ${entity.name} 已标记销毁`);
}
return;
}
// 设置(覆盖之前的设置)
pending.active = active;
if (this._debug) {
logger.debug(`CommandBuffer: 延迟设置实体 ${entity.name} 激活状态为 ${active}`);
}
} else {
// 旧模式
this._commands.push({
type: CommandType.SET_ENTITY_ACTIVE,
entity,
value: active
});
}
}
@@ -197,12 +369,120 @@ export class CommandBuffer {
* 执行所有待处理的命令
* Execute all pending commands
*
* 执行顺序removes -> adds -> active -> destroy
* Execution order: removes -> adds -> active -> destroy
*
* 通常在帧末由 Scene 自动调用。
* Usually called automatically by Scene at end of frame.
*
* @returns 执行的命令数量 | Number of commands executed
*/
public flush(): number {
if (this._useDeduplication) {
return this.flushDeduplication();
} else {
return this.flushLegacy();
}
}
/**
* 使用去重模式刷新
* Flush using deduplication mode
*/
private flushDeduplication(): number {
if (this._pending.size === 0) {
return 0;
}
const entityCount = this._pending.size;
let commandCount = 0;
if (this._debug) {
logger.debug(`CommandBuffer: 开始执行 ${entityCount} 个实体的延迟命令`);
}
// 复制并清空,防止执行过程中有新命令加入
const pending = this._pending;
this._pending = new Map();
// 分阶段执行
// Phase 1: Removes
for (const [entity, ops] of pending) {
if (ops.bDestroy || !entity.scene) continue;
if (ops.removes && ops.removes.size > 0) {
for (const typeId of ops.removes) {
try {
const componentType = ComponentRegistry.getTypeByBitIndex(typeId);
if (componentType) {
entity.removeComponentByType(componentType);
commandCount++;
}
} catch (error) {
logger.error(`CommandBuffer: 移除组件失败`, { entity: entity.name, typeId, error });
}
}
}
}
// Phase 2: Adds
for (const [entity, ops] of pending) {
if (ops.bDestroy || !entity.scene) continue;
if (ops.adds && ops.adds.size > 0) {
for (const component of ops.adds.values()) {
try {
entity.addComponent(component);
commandCount++;
} catch (error) {
logger.error(`CommandBuffer: 添加组件失败`, {
entity: entity.name,
component: component.constructor.name,
error
});
}
}
}
}
// Phase 3: Active state
for (const [entity, ops] of pending) {
if (ops.bDestroy || !entity.scene) continue;
if (ops.active !== undefined) {
try {
entity.active = ops.active;
commandCount++;
} catch (error) {
logger.error(`CommandBuffer: 设置激活状态失败`, { entity: entity.name, error });
}
}
}
// Phase 4: Destroy
for (const [entity, ops] of pending) {
if (!ops.bDestroy || !entity.scene) continue;
try {
entity.destroy();
commandCount++;
} catch (error) {
logger.error(`CommandBuffer: 销毁实体失败`, { entity: entity.name, error });
}
}
if (this._debug) {
logger.debug(`CommandBuffer: 完成执行 ${commandCount} 个延迟命令`);
}
return commandCount;
}
/**
* 使用旧模式刷新
* Flush using legacy mode
*/
private flushLegacy(): number {
if (this._commands.length === 0) {
return 0;
}
@@ -214,7 +494,6 @@ export class CommandBuffer {
}
// 复制命令列表并清空,防止执行过程中有新命令加入
// Copy and clear command list to prevent new commands during execution
const commands = this._commands;
this._commands = [];
@@ -230,12 +509,11 @@ export class CommandBuffer {
}
/**
* 执行单个命令
* Execute single command
* 执行单个命令(旧模式)
* Execute single command (legacy mode)
*/
private executeCommand(cmd: DeferredCommand): void {
// 检查实体是否仍然有效
// Check if entity is still valid
if (!cmd.entity.scene) {
if (this._debug) {
logger.debug(`CommandBuffer: 跳过命令,实体 ${cmd.entity.name} 已无效`);
@@ -277,9 +555,13 @@ export class CommandBuffer {
* Clear all pending commands (without executing)
*/
public clear(): void {
if (this._debug && this._commands.length > 0) {
logger.debug(`CommandBuffer: 清空 ${this._commands.length} 个未执行的命令`);
if (this._debug) {
const count = this._useDeduplication ? this._pending.size : this._commands.length;
if (count > 0) {
logger.debug(`CommandBuffer: 清空 ${count} 个未执行的命令`);
}
}
this._pending.clear();
this._commands.length = 0;
}

View File

@@ -0,0 +1,136 @@
/**
* 轻量级实体句柄
*
* 使用数值表示实体,包含索引和代数信息。
* 28位索引 + 20位代数 = 48位在 JavaScript 安全整数范围内。
*
* Lightweight entity handle.
* Uses numeric value to represent entity with index and generation.
* 28-bit index + 20-bit generation = 48-bit, within JavaScript safe integer range.
*
* @example
* ```typescript
* const handle = makeHandle(42, 1);
* console.log(indexOf(handle)); // 42
* console.log(genOf(handle)); // 1
* console.log(isValidHandle(handle)); // true
* ```
*/
/**
* 实体句柄类型
*
* 使用 branded type 提供类型安全。
*
* Entity handle type.
* Uses branded type for type safety.
*/
export type EntityHandle = number & { readonly __brand: 'EntityHandle' };
/**
* 索引位数 | Index bits
*/
export const INDEX_BITS = 28;
/**
* 代数位数 | Generation bits
*/
export const GEN_BITS = 20;
/**
* 索引掩码 | Index mask
*/
export const INDEX_MASK = (1 << INDEX_BITS) - 1; // 0x0FFFFFFF
/**
* 代数掩码 | Generation mask
*/
export const GEN_MASK = (1 << GEN_BITS) - 1; // 0x000FFFFF
/**
* 最大实体数量 | Maximum entity count
*/
export const MAX_ENTITIES = 1 << INDEX_BITS; // 268,435,456
/**
* 最大代数值 | Maximum generation value
*/
export const MAX_GENERATION = 1 << GEN_BITS; // 1,048,576
/**
* 空句柄常量 | Null handle constant
*/
export const NULL_HANDLE = 0 as EntityHandle;
/**
* 创建实体句柄
* Create entity handle
*
* @param index 实体索引 | Entity index
* @param generation 实体代数 | Entity generation
* @returns 实体句柄 | Entity handle
*/
export function makeHandle(index: number, generation: number): EntityHandle {
// handle = generation * 2^28 + index
// 使用乘法而不是位移,因为位移只支持 32 位
return ((generation & GEN_MASK) * MAX_ENTITIES + (index & INDEX_MASK)) as EntityHandle;
}
/**
* 从句柄提取索引
* Extract index from handle
*
* @param handle 实体句柄 | Entity handle
* @returns 实体索引 | Entity index
*/
export function indexOf(handle: EntityHandle): number {
return handle & INDEX_MASK;
}
/**
* 从句柄提取代数
* Extract generation from handle
*
* @param handle 实体句柄 | Entity handle
* @returns 实体代数 | Entity generation
*/
export function genOf(handle: EntityHandle): number {
return Math.floor(handle / MAX_ENTITIES) & GEN_MASK;
}
/**
* 检查句柄是否有效(非空)
* Check if handle is valid (non-null)
*
* @param handle 实体句柄 | Entity handle
* @returns 是否有效 | Whether valid
*/
export function isValidHandle(handle: EntityHandle): boolean {
return handle !== NULL_HANDLE;
}
/**
* 比较两个句柄是否相等
* Compare two handles for equality
*
* @param a 第一个句柄 | First handle
* @param b 第二个句柄 | Second handle
* @returns 是否相等 | Whether equal
*/
export function handleEquals(a: EntityHandle, b: EntityHandle): boolean {
return a === b;
}
/**
* 将句柄转换为字符串(用于调试)
* Convert handle to string (for debugging)
*
* @param handle 实体句柄 | Entity handle
* @returns 字符串表示 | String representation
*/
export function handleToString(handle: EntityHandle): string {
if (handle === NULL_HANDLE) {
return 'Entity(NULL)';
}
return `Entity(idx=${indexOf(handle)}, gen=${genOf(handle)})`;
}

View File

@@ -0,0 +1,331 @@
/**
* 实体句柄管理器
*
* 管理轻量级实体句柄的生命周期,包括创建、销毁和状态查询。
* 使用 TypedArray 实现高效的内存布局。
*
* Entity handle manager.
* Manages lifecycle of lightweight entity handles including creation, destruction and state queries.
* Uses TypedArray for efficient memory layout.
*
* @example
* ```typescript
* const manager = new EntityHandleManager();
* const handle = manager.create();
* console.log(manager.isAlive(handle)); // true
*
* manager.destroy(handle);
* console.log(manager.isAlive(handle)); // false
*
* // 索引会被复用,但代数会增加
* const newHandle = manager.create();
* console.log(indexOf(handle) === indexOf(newHandle)); // true
* console.log(genOf(newHandle) > genOf(handle)); // true
* ```
*/
import {
EntityHandle,
makeHandle,
indexOf,
genOf,
MAX_ENTITIES,
MAX_GENERATION
} from './EntityHandle';
/**
* 初始容量 | Initial capacity
*/
const INITIAL_CAPACITY = 1024;
/**
* 实体句柄管理器
*
* Entity handle manager.
*/
export class EntityHandleManager {
/**
* 代数数组(每个索引的当前代数)
* Generation array (current generation for each index)
*/
private _generations: Uint32Array;
/**
* 存活状态数组0=死亡1=存活)
* Alive state array (0=dead, 1=alive)
*/
private _alive: Uint8Array;
/**
* 启用状态数组0=禁用1=启用)
* Enabled state array (0=disabled, 1=enabled)
*/
private _enabled: Uint8Array;
/**
* 空闲索引列表(用于复用已销毁的索引)
* Free index list (for reusing destroyed indices)
*/
private _freeList: number[] = [];
/**
* 下一个新索引
* Next new index
*/
private _nextIndex: number = 1; // 从 1 开始0 保留给 NULL_HANDLE
/**
* 当前存活实体数量
* Current alive entity count
*/
private _aliveCount: number = 0;
/**
* 当前容量
* Current capacity
*/
private _capacity: number;
/**
* 创建实体句柄管理器
* Create entity handle manager
*
* @param initialCapacity 初始容量 | Initial capacity
*/
constructor(initialCapacity: number = INITIAL_CAPACITY) {
this._capacity = initialCapacity;
this._generations = new Uint32Array(initialCapacity);
this._alive = new Uint8Array(initialCapacity);
this._enabled = new Uint8Array(initialCapacity);
// 索引 0 保留给 NULL_HANDLE
this._alive[0] = 0;
this._enabled[0] = 0;
}
/**
* 获取存活实体数量
* Get alive entity count
*/
public get aliveCount(): number {
return this._aliveCount;
}
/**
* 获取当前容量
* Get current capacity
*/
public get capacity(): number {
return this._capacity;
}
/**
* 创建新实体句柄
* Create new entity handle
*
* @returns 新实体句柄 | New entity handle
*/
public create(): EntityHandle {
let index: number;
// 优先从空闲列表中获取索引
if (this._freeList.length > 0) {
index = this._freeList.pop()!;
} else {
// 分配新索引
index = this._nextIndex++;
// 检查是否需要扩容
if (index >= this._capacity) {
this.grow(index);
}
}
// 获取当前代数(可能因之前销毁而增加)
const generation = this._generations[index]!;
// 标记为存活和启用
this._alive[index] = 1;
this._enabled[index] = 1;
this._aliveCount++;
return makeHandle(index, generation);
}
/**
* 销毁实体句柄
* Destroy entity handle
*
* @param handle 要销毁的句柄 | Handle to destroy
* @returns 是否成功销毁 | Whether destruction succeeded
*/
public destroy(handle: EntityHandle): boolean {
const index = indexOf(handle);
const generation = genOf(handle);
// 验证句柄有效性
if (index >= this._capacity || index === 0) {
return false;
}
if (this._generations[index] !== generation) {
return false;
}
if (this._alive[index] !== 1) {
return false;
}
// 标记为死亡
this._alive[index] = 0;
this._enabled[index] = 0;
this._aliveCount--;
// 增加代数(防止 ABA 问题)
const newGen = (generation + 1) % MAX_GENERATION;
this._generations[index] = newGen;
// 将索引加入空闲列表
this._freeList.push(index);
return true;
}
/**
* 检查句柄是否存活
* Check if handle is alive
*
* @param handle 实体句柄 | Entity handle
* @returns 是否存活 | Whether alive
*/
public isAlive(handle: EntityHandle): boolean {
const index = indexOf(handle);
const generation = genOf(handle);
if (index >= this._capacity || index === 0) {
return false;
}
return this._alive[index] === 1 && this._generations[index] === generation;
}
/**
* 检查句柄是否启用
* Check if handle is enabled
*
* @param handle 实体句柄 | Entity handle
* @returns 是否启用 | Whether enabled
*/
public isEnabled(handle: EntityHandle): boolean {
if (!this.isAlive(handle)) {
return false;
}
const index = indexOf(handle);
return this._enabled[index] === 1;
}
/**
* 设置句柄启用状态
* Set handle enabled state
*
* @param handle 实体句柄 | Entity handle
* @param enabled 启用状态 | Enabled state
* @returns 是否成功设置 | Whether setting succeeded
*/
public setEnabled(handle: EntityHandle, enabled: boolean): boolean {
if (!this.isAlive(handle)) {
return false;
}
const index = indexOf(handle);
this._enabled[index] = enabled ? 1 : 0;
return true;
}
/**
* 验证句柄是否有效(存活且代数匹配)
* Validate if handle is valid (alive and generation matches)
*
* @param handle 实体句柄 | Entity handle
* @returns 是否有效 | Whether valid
*/
public validate(handle: EntityHandle): boolean {
return this.isAlive(handle);
}
/**
* 扩容数组
* Grow arrays
*/
private grow(minIndex: number): void {
let newSize = this._capacity;
// 按 2 倍扩容
while (newSize <= minIndex) {
newSize <<= 1;
}
// 检查是否超过最大容量
if (newSize > MAX_ENTITIES) {
newSize = MAX_ENTITIES;
if (minIndex >= newSize) {
throw new Error(`EntityHandleManager: 超过最大实体数量 ${MAX_ENTITIES}`);
}
}
// 创建新数组并复制数据
const newGenerations = new Uint32Array(newSize);
const newAlive = new Uint8Array(newSize);
const newEnabled = new Uint8Array(newSize);
newGenerations.set(this._generations);
newAlive.set(this._alive);
newEnabled.set(this._enabled);
this._generations = newGenerations;
this._alive = newAlive;
this._enabled = newEnabled;
this._capacity = newSize;
}
/**
* 重置管理器状态
* Reset manager state
*/
public reset(): void {
this._generations.fill(0);
this._alive.fill(0);
this._enabled.fill(0);
this._freeList.length = 0;
this._nextIndex = 1;
this._aliveCount = 0;
}
/**
* 遍历所有存活的句柄
* Iterate all alive handles
*
* @param callback 回调函数 | Callback function
*/
public forEach(callback: (handle: EntityHandle) => void): void {
for (let i = 1; i < this._nextIndex; i++) {
if (this._alive[i] === 1) {
const handle = makeHandle(i, this._generations[i]!);
callback(handle);
}
}
}
/**
* 获取所有存活的句柄
* Get all alive handles
*
* @returns 存活句柄数组 | Alive handles array
*/
public getAllAlive(): EntityHandle[] {
const result: EntityHandle[] = [];
this.forEach((handle) => result.push(handle));
return result;
}
}

View File

@@ -0,0 +1,103 @@
/**
* 帧级变更检测管理器
*
* 基于 epoch帧计数的组件变更追踪系统。
* 每帧递增 epoch组件写入时记录当前 epoch
* 系统可以查询自上次检查以来发生变更的组件。
*
* Frame-level change detection manager.
* Epoch-based component change tracking system.
* Epoch increments each frame, components record current epoch on write,
* systems can query components changed since last check.
*
* @example
* ```typescript
* // 在 System 中使用
* class MovementSystem extends EntitySystem {
* private _lastEpoch = 0;
*
* process(): void {
* const epoch = this.scene.epochManager;
*
* // 只处理 Velocity 变化的实体
* for (const entity of this.entities) {
* const vel = entity.getComponent(Velocity);
* if (vel && vel.lastWriteEpoch > this._lastEpoch) {
* // 处理变更
* }
* }
*
* this._lastEpoch = epoch.current;
* }
* }
* ```
*/
/**
* Epoch 管理器
*
* Epoch manager.
*/
export class EpochManager {
/**
* 当前 epoch 值
*
* 从 1 开始0 表示"从未写入"。
*
* Current epoch value.
* Starts from 1, 0 means "never written".
*/
private _current: number = 1;
/**
* 获取当前 epoch
*
* Get current epoch.
*/
public get current(): number {
return this._current;
}
/**
* 递增 epoch
*
* 应在每帧开始时调用。
*
* Increment epoch.
* Should be called at the start of each frame.
*/
public increment(): void {
this._current++;
// 处理溢出(虽然 2^53 帧基本不可能达到)
// Handle overflow (though 2^53 frames is practically unreachable)
if (this._current >= Number.MAX_SAFE_INTEGER) {
this._current = 1;
}
}
/**
* 重置 epoch
*
* 在场景重置时调用。
*
* Reset epoch.
* Called when scene is reset.
*/
public reset(): void {
this._current = 1;
}
/**
* 检查给定 epoch 是否在指定 epoch 之后发生变更
*
* Check if given epoch is after the specified epoch (changed since).
*
* @param writeEpoch 写入时的 epoch | Epoch when written
* @param sinceEpoch 检查点 epoch | Checkpoint epoch
* @returns 是否在检查点之后发生变更 | Whether changed after checkpoint
*/
public isChangedSince(writeEpoch: number, sinceEpoch: number): boolean {
return writeEpoch > sinceEpoch;
}
}

View File

@@ -0,0 +1,366 @@
/**
* 编译查询
*
* 预编译的查询执行计划,避免每次查询时重复构建匹配条件。
* 提供便捷的迭代方法和变更检测支持。
*
* Compiled Query.
* Pre-compiled query execution plan that avoids repeated condition building.
* Provides convenient iteration methods and change detection support.
*
* @example
* ```typescript
* class MovementSystem extends EntitySystem {
* private _query: CompiledQuery<[typeof Position, typeof Velocity]>;
*
* onInitialize(): void {
* this._query = this.scene.querySystem.compile(Position, Velocity);
* }
*
* update(): void {
* this._query.forEach((entity, pos, vel) => {
* pos.x += vel.x * this.deltaTime;
* pos.y += vel.y * this.deltaTime;
* });
* }
* }
* ```
*/
import { Entity } from '../../Entity';
import { Component } from '../../Component';
import { ComponentType } from '../ComponentStorage';
import { QuerySystem } from '../QuerySystem';
/**
* 组件实例类型提取
*
* Extract component instance types from component type tuple.
*/
export type InstanceTypes<T extends ComponentType[]> = {
[K in keyof T]: T[K] extends ComponentType<infer C> ? C : never;
};
/**
* 编译查询类
*
* Compiled query class.
*/
export class CompiledQuery<T extends ComponentType[] = ComponentType[]> {
/**
* 查询的组件类型列表
*
* Component types for this query.
*/
private readonly _componentTypes: T;
/**
* 查询系统引用
*
* Reference to query system.
*/
private readonly _querySystem: QuerySystem;
/**
* 上次查询的版本号(用于缓存失效检测)
*
* Last query version (for cache invalidation detection).
*/
private _lastVersion: number = -1;
/**
* 缓存的实体列表
*
* Cached entity list.
*/
private _cachedEntities: readonly Entity[] = [];
/**
* 创建编译查询
*
* Create compiled query.
*
* @param querySystem 查询系统引用 | Query system reference
* @param componentTypes 组件类型列表 | Component type list
*/
constructor(querySystem: QuerySystem, ...componentTypes: T) {
this._querySystem = querySystem;
this._componentTypes = componentTypes;
}
/**
* 获取组件类型列表
*
* Get component type list.
*/
public get componentTypes(): readonly ComponentType[] {
return this._componentTypes;
}
/**
* 获取匹配的实体列表
*
* Get matching entity list.
*/
public get entities(): readonly Entity[] {
this._refreshCache();
return this._cachedEntities;
}
/**
* 获取匹配的实体数量
*
* Get matching entity count.
*/
public get count(): number {
return this.entities.length;
}
/**
* 刷新缓存
*
* Refresh cache if needed.
*/
private _refreshCache(): void {
const currentVersion = this._querySystem.version;
if (this._lastVersion !== currentVersion) {
const result = this._querySystem.queryAll(...this._componentTypes);
this._cachedEntities = result.entities;
this._lastVersion = currentVersion;
}
}
/**
* 遍历所有匹配的实体及其组件
*
* Iterate all matching entities with their components.
*
* @param callback 回调函数,接收实体和组件实例 | Callback receiving entity and component instances
*
* @example
* ```typescript
* query.forEach((entity, position, velocity) => {
* position.x += velocity.x * deltaTime;
* });
* ```
*/
public forEach(
callback: (entity: Entity, ...components: InstanceTypes<T>) => void
): void {
const entities = this.entities;
const componentTypes = this._componentTypes;
const typeCount = componentTypes.length;
for (let i = 0, len = entities.length; i < len; i++) {
const entity = entities[i]!;
const components: Component[] = new Array(typeCount);
// 获取所有组件
for (let j = 0; j < typeCount; j++) {
const component = entity.getComponent(componentTypes[j]!);
if (!component) {
// 组件不存在,跳过这个实体
continue;
}
components[j] = component;
}
// 调用回调
callback(entity, ...(components as InstanceTypes<T>));
}
}
/**
* 遍历自指定 epoch 以来发生变更的实体
*
* Iterate entities with components changed since specified epoch.
*
* @param sinceEpoch 检查点 epoch | Checkpoint epoch
* @param callback 回调函数 | Callback function
*
* @example
* ```typescript
* class PhysicsSystem extends EntitySystem {
* private _lastEpoch = 0;
*
* update(): void {
* this._query.forEachChanged(this._lastEpoch, (entity, pos, vel) => {
* // 只处理变更的实体
* });
* this._lastEpoch = this.scene.epochManager.current;
* }
* }
* ```
*/
public forEachChanged(
sinceEpoch: number,
callback: (entity: Entity, ...components: InstanceTypes<T>) => void
): void {
const entities = this.entities;
const componentTypes = this._componentTypes;
const typeCount = componentTypes.length;
for (let i = 0, len = entities.length; i < len; i++) {
const entity = entities[i]!;
const components: Component[] = new Array(typeCount);
let hasChanged = false;
// 获取所有组件并检查变更
for (let j = 0; j < typeCount; j++) {
const component = entity.getComponent(componentTypes[j]!);
if (!component) {
continue;
}
components[j] = component;
if (component.lastWriteEpoch > sinceEpoch) {
hasChanged = true;
}
}
// 只在有变更时调用回调
if (hasChanged) {
callback(entity, ...(components as InstanceTypes<T>));
}
}
}
/**
* 获取第一个匹配的实体及其组件
*
* Get first matching entity with its components.
*
* @returns 实体和组件元组,或 null | Entity and components tuple, or null
*/
public first(): [Entity, ...InstanceTypes<T>] | null {
const entities = this.entities;
if (entities.length === 0) {
return null;
}
const entity = entities[0]!;
const components: Component[] = [];
for (const type of this._componentTypes) {
const component = entity.getComponent(type);
if (!component) {
return null;
}
components.push(component);
}
return [entity, ...(components as InstanceTypes<T>)];
}
/**
* 转换为数组
*
* Convert to array.
*
* @returns 实体和组件元组数组 | Array of entity and components tuples
*/
public toArray(): Array<[Entity, ...InstanceTypes<T>]> {
const result: Array<[Entity, ...InstanceTypes<T>]> = [];
this.forEach((entity, ...components) => {
result.push([entity, ...components]);
});
return result;
}
/**
* 映射转换
*
* Map transformation.
*
* @param callback 转换函数 | Transform function
* @returns 转换结果数组 | Array of transform results
*/
public map<R>(
callback: (entity: Entity, ...components: InstanceTypes<T>) => R
): R[] {
const result: R[] = [];
this.forEach((entity, ...components) => {
result.push(callback(entity, ...components));
});
return result;
}
/**
* 过滤实体
*
* Filter entities.
*
* @param predicate 过滤条件 | Filter predicate
* @returns 过滤后的实体数组 | Filtered entity array
*/
public filter(
predicate: (entity: Entity, ...components: InstanceTypes<T>) => boolean
): Entity[] {
const result: Entity[] = [];
this.forEach((entity, ...components) => {
if (predicate(entity, ...components)) {
result.push(entity);
}
});
return result;
}
/**
* 查找满足条件的实体
*
* Find entity matching predicate.
*
* @param predicate 查找条件 | Find predicate
* @returns 找到的实体或 undefined | Found entity or undefined
*/
public find(
predicate: (entity: Entity, ...components: InstanceTypes<T>) => boolean
): Entity | undefined {
const entities = this.entities;
const componentTypes = this._componentTypes;
const typeCount = componentTypes.length;
for (let i = 0, len = entities.length; i < len; i++) {
const entity = entities[i]!;
const components: Component[] = new Array(typeCount);
for (let j = 0; j < typeCount; j++) {
const component = entity.getComponent(componentTypes[j]!);
if (!component) {
continue;
}
components[j] = component;
}
if (predicate(entity, ...(components as InstanceTypes<T>))) {
return entity;
}
}
return undefined;
}
/**
* 检查是否有任何实体匹配
*
* Check if any entity matches.
*/
public any(): boolean {
return this.count > 0;
}
/**
* 检查是否没有实体匹配
*
* Check if no entity matches.
*/
public empty(): boolean {
return this.count === 0;
}
}

View File

@@ -7,8 +7,10 @@ import { getComponentTypeName } from '../Decorators';
import { Archetype, ArchetypeSystem } from './ArchetypeSystem';
import { ReactiveQuery, ReactiveQueryConfig } from './ReactiveQuery';
import { QueryCondition, QueryConditionType, QueryResult } from './QueryTypes';
import { CompiledQuery } from './Query/CompiledQuery';
export { QueryCondition, QueryConditionType, QueryResult };
export { CompiledQuery };
/**
* 实体索引结构
@@ -530,6 +532,76 @@ export class QuerySystem {
};
}
/**
* 查询自指定 epoch 以来发生变更的实体
*
* 返回拥有指定组件且该组件在 sinceEpoch 之后被修改过的实体。
* 用于实现增量更新,只处理发生变化的实体。
*
* Query entities with components changed since the specified epoch.
* Returns entities that have the specified component type(s) and that component
* was modified after sinceEpoch.
* Used for incremental updates, only processing entities that have changed.
*
* @param sinceEpoch 上次检查的 epoch | Last checked epoch
* @param componentTypes 要检查的组件类型 | Component types to check
* @returns 查询结果,包含变更的实体 | Query result with changed entities
*
* @example
* ```typescript
* class MovementSystem extends EntitySystem {
* private _lastEpoch = 0;
*
* update(): void {
* // 只处理 Velocity 组件发生变化的实体
* const result = this.scene.querySystem.queryChangedSince(
* this._lastEpoch,
* Velocity
* );
*
* for (const entity of result.entities) {
* // 处理变更的实体
* }
*
* this._lastEpoch = this.scene.epochManager.current;
* }
* }
* ```
*/
public queryChangedSince(sinceEpoch: number, ...componentTypes: ComponentType[]): QueryResult {
const startTime = performance.now();
this._queryStats.totalQueries++;
this._queryStats.dirtyChecks++;
// 先获取所有拥有这些组件的实体
const baseResult = this.queryAll(...componentTypes);
const changedEntities: Entity[] = [];
// 过滤出组件发生变更的实体
for (const entity of baseResult.entities) {
let hasChanged = false;
for (const componentType of componentTypes) {
const component = entity.getComponent(componentType);
if (component && component.lastWriteEpoch > sinceEpoch) {
hasChanged = true;
break;
}
}
if (hasChanged) {
changedEntities.push(entity);
}
}
return {
entities: changedEntities,
count: changedEntities.length,
executionTime: performance.now() - startTime,
fromCache: false
};
}
/**
* 按单个组件类型查询实体
*
@@ -704,6 +776,42 @@ export class QuerySystem {
this.clearReactiveQueries();
}
/**
* 编译查询
*
* 创建预编译的查询对象,避免每次查询时重复构建匹配条件。
* 适用于在 System 中频繁执行的查询,提供便捷的迭代方法。
*
* Compile query.
* Creates a pre-compiled query object that avoids repeated condition building.
* Suitable for frequently executed queries in Systems, provides convenient iteration.
*
* @param componentTypes 要查询的组件类型 | Component types to query
* @returns 编译查询实例 | Compiled query instance
*
* @example
* ```typescript
* class MovementSystem extends EntitySystem {
* private _query: CompiledQuery<[typeof Position, typeof Velocity]>;
*
* onInitialize(): void {
* this._query = this.scene.querySystem.compile(Position, Velocity);
* }
*
* update(): void {
* this._query.forEach((entity, pos, vel) => {
* pos.x += vel.x * this.deltaTime;
* pos.y += vel.y * this.deltaTime;
* pos.markDirty(this.scene.epochManager.current);
* });
* }
* }
* ```
*/
public compile<T extends ComponentType[]>(...componentTypes: T): CompiledQuery<T> {
return new CompiledQuery<T>(this, ...componentTypes);
}
/**
* 创建响应式查询
*

View File

@@ -0,0 +1,263 @@
/**
* 系统依赖图
*
* 用于构建系统间的依赖关系并进行拓扑排序。
* 支持 before/after 依赖声明和 set 分组。
*
* System dependency graph.
* Used to build dependency relationships between systems and perform topological sorting.
* Supports before/after dependencies and set grouping.
*/
/**
* 循环依赖错误
* Cycle dependency error
*/
export class CycleDependencyError extends Error {
/**
* 参与循环的节点 ID
* Node IDs involved in the cycle
*/
public readonly involvedNodes: string[];
constructor(involvedNodes: string[]) {
const message = `[SystemDependencyGraph] 检测到循环依赖 | Cycle dependency detected: ${involvedNodes.join(' -> ')}`;
super(message);
this.name = 'CycleDependencyError';
this.involvedNodes = involvedNodes;
Object.setPrototypeOf(this, new.target.prototype);
}
}
/**
* 依赖图节点
* Dependency graph node
*/
interface GraphNode {
/** 节点 ID | Node ID */
id: string;
/** 是否为虚拟集合节点 | Whether this is a virtual set node */
bIsVirtual: boolean;
/** 入边(依赖于此节点的节点) | Incoming edges (nodes that depend on this node) */
inEdges: Set<string>;
/** 出边(此节点依赖的节点) | Outgoing edges (nodes this node depends on) */
outEdges: Set<string>;
}
/**
* 系统依赖信息
* System dependency info
*/
export interface SystemDependencyInfo {
/** 系统名称 | System name */
name: string;
/** 在这些系统之前执行 | Execute before these systems */
before: string[];
/** 在这些系统之后执行 | Execute after these systems */
after: string[];
/** 所属集合 | Sets this system belongs to */
sets: string[];
}
/** 集合前缀 | Set prefix */
const SET_PREFIX = 'set:';
/**
* 系统依赖图
*
* 使用 Kahn 算法进行拓扑排序,检测循环依赖。
*
* System dependency graph.
* Uses Kahn's algorithm for topological sorting and cycle detection.
*/
export class SystemDependencyGraph {
/** 节点映射 | Node map */
private _nodes: Map<string, GraphNode> = new Map();
/**
* 添加系统节点
* Add system node
*
* @param name 系统名称 | System name
*/
public addSystemNode(name: string): void {
this.getOrCreateNode(name, false);
}
/**
* 添加集合节点(虚拟节点)
* Add set node (virtual node)
*
* @param setName 集合名称 | Set name
*/
public addSetNode(setName: string): void {
const nodeId = SET_PREFIX + setName;
this.getOrCreateNode(nodeId, true);
}
/**
* 添加依赖边
* Add dependency edge
*
* @param from 起始节点 | Source node
* @param to 目标节点 | Target node
*/
public addEdge(from: string, to: string): void {
if (from === to) return;
const fromNode = this.getOrCreateNode(from, from.startsWith(SET_PREFIX));
const toNode = this.getOrCreateNode(to, to.startsWith(SET_PREFIX));
fromNode.outEdges.add(to);
toNode.inEdges.add(from);
}
/**
* 从系统依赖信息构建图
* Build graph from system dependency info
*
* @param systems 系统依赖信息列表 | System dependency info list
*/
public buildFromSystems(systems: SystemDependencyInfo[]): void {
this.clear();
// 1. 添加所有系统节点
for (const sys of systems) {
this.addSystemNode(sys.name);
// 添加集合节点
for (const setName of sys.sets) {
this.addSetNode(setName);
}
}
// 2. 添加边
for (const sys of systems) {
// 集合 -> 系统(系统属于集合,集合执行后系统执行)
for (const setName of sys.sets) {
const setId = SET_PREFIX + setName;
this.addEdge(setId, sys.name);
}
// before: 此系统 -> 目标(此系统先执行)
for (const target of sys.before) {
const targetId = this.resolveTargetId(target);
this.addEdge(sys.name, targetId);
}
// after: 目标 -> 此系统(目标先执行)
for (const target of sys.after) {
const targetId = this.resolveTargetId(target);
this.addEdge(targetId, sys.name);
}
}
}
/**
* 执行拓扑排序Kahn 算法)
* Perform topological sort (Kahn's algorithm)
*
* @returns 排序后的系统名称列表(不包含虚拟节点) | Sorted system names (excluding virtual nodes)
* @throws {CycleDependencyError} 如果存在循环依赖 | If cycle dependency detected
*/
public topologicalSort(): string[] {
// 复制入边计数(避免修改原始数据)
const inDegree = new Map<string, number>();
for (const [id, node] of this._nodes) {
inDegree.set(id, node.inEdges.size);
}
// 初始化队列:入度为 0 的节点
const queue: string[] = [];
for (const [id, degree] of inDegree) {
if (degree === 0) {
queue.push(id);
}
}
const result: string[] = [];
let processedCount = 0;
while (queue.length > 0) {
const nodeId = queue.shift()!;
processedCount++;
const node = this._nodes.get(nodeId);
if (!node) continue;
// 只添加非虚拟节点到结果
if (!node.bIsVirtual) {
result.push(nodeId);
}
// 减少所有出边目标的入度
for (const outId of node.outEdges) {
const newDegree = (inDegree.get(outId) ?? 0) - 1;
inDegree.set(outId, newDegree);
if (newDegree === 0) {
queue.push(outId);
}
}
}
// 检测循环:如果处理的节点数少于总节点数,说明存在循环
if (processedCount < this._nodes.size) {
const cycleNodes: string[] = [];
for (const [id, degree] of inDegree) {
if (degree > 0) {
cycleNodes.push(id);
}
}
throw new CycleDependencyError(cycleNodes);
}
return result;
}
/**
* 清空图
* Clear graph
*/
public clear(): void {
this._nodes.clear();
}
/**
* 获取节点数量
* Get node count
*/
public get size(): number {
return this._nodes.size;
}
/**
* 获取或创建节点
* Get or create node
*/
private getOrCreateNode(id: string, bIsVirtual: boolean): GraphNode {
let node = this._nodes.get(id);
if (!node) {
node = {
id,
bIsVirtual,
inEdges: new Set(),
outEdges: new Set()
};
this._nodes.set(id, node);
}
return node;
}
/**
* 解析目标 ID支持 set: 前缀)
* Resolve target ID (supports set: prefix)
*/
private resolveTargetId(nameOrSet: string): string {
// 如果已经有 set: 前缀,直接返回
if (nameOrSet.startsWith(SET_PREFIX)) {
return nameOrSet;
}
return nameOrSet;
}
}

View File

@@ -0,0 +1,338 @@
/**
* 系统调度器
*
* 负责管理系统的执行阶段和依赖关系。
* 支持声明式的 before/after 依赖和拓扑排序。
*
* System scheduler.
* Manages system execution stages and dependencies.
* Supports declarative before/after dependencies and topological sorting.
*
* @example
* ```typescript
* // 使用装饰器声明依赖
* @Stage('update')
* @After('PhysicsSystem')
* @Before('RenderSystem')
* class MovementSystem extends EntitySystem { }
*
* // 或者使用方法链
* class MovementSystem extends EntitySystem {
* constructor() {
* super();
* this.stage('update').after('PhysicsSystem').before('RenderSystem');
* }
* }
* ```
*/
import { SystemDependencyGraph, CycleDependencyError, type SystemDependencyInfo } from './SystemDependencyGraph';
import type { EntitySystem } from '../Systems/EntitySystem';
export { CycleDependencyError };
/**
* 系统执行阶段
* System execution stage
*/
export type SystemStage = 'startup' | 'preUpdate' | 'update' | 'postUpdate' | 'cleanup';
/**
* 默认阶段执行顺序
* Default stage execution order
*/
export const DEFAULT_STAGE_ORDER: readonly SystemStage[] = [
'startup',
'preUpdate',
'update',
'postUpdate',
'cleanup'
];
/**
* 系统调度元数据
* System scheduling metadata
*/
export interface SystemSchedulingMetadata {
/** 执行阶段 | Execution stage */
stage: SystemStage;
/** 在这些系统之前执行 | Execute before these systems */
before: string[];
/** 在这些系统之后执行 | Execute after these systems */
after: string[];
/** 所属集合 | Sets this system belongs to */
sets: string[];
}
/**
* 默认调度元数据
* Default scheduling metadata
*/
export const DEFAULT_SCHEDULING_METADATA: Readonly<SystemSchedulingMetadata> = {
stage: 'update',
before: [],
after: [],
sets: []
};
/**
* 系统调度器
*
* 管理系统的执行顺序,支持:
* - 阶段分组startup, preUpdate, update, postUpdate, cleanup
* - before/after 依赖声明
* - 拓扑排序和循环检测
* - 与现有 updateOrder 的兼容
*
* System scheduler.
* Manages system execution order, supports:
* - Stage grouping (startup, preUpdate, update, postUpdate, cleanup)
* - before/after dependency declarations
* - Topological sorting and cycle detection
* - Compatibility with existing updateOrder
*/
export class SystemScheduler {
/** 按阶段分组的排序结果 | Sorted results grouped by stage */
private _sortedByStage: Map<SystemStage, EntitySystem[]> = new Map();
/** 是否需要重新构建 | Whether rebuild is needed */
private _dirty: boolean = true;
/** 依赖图 | Dependency graph */
private _graph: SystemDependencyGraph = new SystemDependencyGraph();
/** 是否启用依赖排序 | Whether dependency sorting is enabled */
private _useDependencySort: boolean = true;
/**
* 设置是否使用依赖排序
* Set whether to use dependency sorting
*
* @param enabled 是否启用 | Whether to enable
*/
public setUseDependencySort(enabled: boolean): void {
if (this._useDependencySort !== enabled) {
this._useDependencySort = enabled;
this._dirty = true;
}
}
/**
* 标记需要重新构建
* Mark as needing rebuild
*/
public markDirty(): void {
this._dirty = true;
}
/**
* 获取指定阶段的排序后系统列表
* Get sorted system list for specified stage
*
* @param systems 所有系统 | All systems
* @param stage 阶段 | Stage
* @returns 排序后的系统列表 | Sorted system list
*/
public getSortedSystems(systems: EntitySystem[], stage?: SystemStage): EntitySystem[] {
this.ensureBuilt(systems);
if (stage) {
return this._sortedByStage.get(stage) ?? [];
}
// 返回所有阶段按顺序合并的结果
const result: EntitySystem[] = [];
for (const s of DEFAULT_STAGE_ORDER) {
const stageSystems = this._sortedByStage.get(s);
if (stageSystems) {
result.push(...stageSystems);
}
}
return result;
}
/**
* 获取所有排序后的系统(按默认阶段顺序)
* Get all sorted systems (by default stage order)
*
* @param systems 所有系统 | All systems
* @returns 排序后的系统列表 | Sorted system list
*/
public getAllSortedSystems(systems: EntitySystem[]): EntitySystem[] {
return this.getSortedSystems(systems);
}
/**
* 确保已构建排序结果
* Ensure sorted results are built
*/
private ensureBuilt(systems: EntitySystem[]): void {
if (!this._dirty) return;
this._sortedByStage.clear();
if (!this._useDependencySort || !this.hasDependencies(systems)) {
// 使用简单的 updateOrder 排序
this.buildWithUpdateOrder(systems);
} else {
// 使用依赖图拓扑排序
this.buildWithDependencyGraph(systems);
}
this._dirty = false;
}
/**
* 检查系统是否有依赖声明
* Check if systems have dependency declarations
*/
private hasDependencies(systems: EntitySystem[]): boolean {
for (const sys of systems) {
const meta = this.getSchedulingMetadata(sys);
if (meta.before.length > 0 || meta.after.length > 0 || meta.sets.length > 0) {
return true;
}
// 检查 stage 是否非默认值
if (meta.stage !== 'update') {
return true;
}
}
return false;
}
/**
* 使用 updateOrder 构建排序
* Build sorting with updateOrder
*/
private buildWithUpdateOrder(systems: EntitySystem[]): void {
// 先按 updateOrder 和 addOrder 排序
const sorted = [...systems].sort((a, b) => {
const orderDiff = a.updateOrder - b.updateOrder;
if (orderDiff !== 0) return orderDiff;
return a.addOrder - b.addOrder;
});
// 所有系统放入 update 阶段
this._sortedByStage.set('update', sorted);
}
/**
* 使用依赖图构建排序
* Build sorting with dependency graph
*/
private buildWithDependencyGraph(systems: EntitySystem[]): void {
// 按阶段分组
const byStage = new Map<SystemStage, EntitySystem[]>();
for (const stage of DEFAULT_STAGE_ORDER) {
byStage.set(stage, []);
}
for (const sys of systems) {
const meta = this.getSchedulingMetadata(sys);
const stage = meta.stage;
const stageList = byStage.get(stage);
if (stageList) {
stageList.push(sys);
} else {
// 未知阶段放入 update
byStage.get('update')!.push(sys);
}
}
// 对每个阶段内的系统进行拓扑排序
for (const [stage, stageSystems] of byStage) {
if (stageSystems.length === 0) {
this._sortedByStage.set(stage, []);
continue;
}
const sorted = this.sortSystemsInStage(stageSystems);
this._sortedByStage.set(stage, sorted);
}
}
/**
* 在阶段内对系统进行拓扑排序
* Sort systems within a stage using topological sort
*/
private sortSystemsInStage(systems: EntitySystem[]): EntitySystem[] {
// 构建名称到系统的映射
const nameToSystem = new Map<string, EntitySystem>();
const depInfos: SystemDependencyInfo[] = [];
for (const sys of systems) {
const name = sys.systemName;
nameToSystem.set(name, sys);
const meta = this.getSchedulingMetadata(sys);
depInfos.push({
name,
before: meta.before,
after: meta.after,
sets: meta.sets
});
}
// 构建依赖图并排序
this._graph.buildFromSystems(depInfos);
try {
const sortedNames = this._graph.topologicalSort();
// 将排序结果映射回系统实例
const result: EntitySystem[] = [];
for (const name of sortedNames) {
const sys = nameToSystem.get(name);
if (sys) {
result.push(sys);
}
}
// 对于没有依赖的系统,保持 updateOrder 作为次要排序键
return this.stableSortByUpdateOrder(result);
} catch (error) {
if (error instanceof CycleDependencyError) {
// 重新抛出循环错误,让用户知道
throw error;
}
// 其他错误回退到 updateOrder 排序
console.warn('[SystemScheduler] 拓扑排序失败,回退到 updateOrder 排序', error);
return this.fallbackSort(systems);
}
}
/**
* 稳定排序:在拓扑顺序的基础上,用 updateOrder 作为次要排序键
* Stable sort: use updateOrder as secondary key after topological order
*/
private stableSortByUpdateOrder(systems: EntitySystem[]): EntitySystem[] {
// 拓扑排序已经保证了依赖顺序
// 对于没有直接依赖关系的系统,按 updateOrder 排序
// 这里简单处理:假设拓扑排序已经正确,只需保持结果
return systems;
}
/**
* 回退排序:使用 updateOrder
* Fallback sort: use updateOrder
*/
private fallbackSort(systems: EntitySystem[]): EntitySystem[] {
return [...systems].sort((a, b) => {
const orderDiff = a.updateOrder - b.updateOrder;
if (orderDiff !== 0) return orderDiff;
return a.addOrder - b.addOrder;
});
}
/**
* 获取系统的调度元数据
* Get system scheduling metadata
*/
private getSchedulingMetadata(system: EntitySystem): SystemSchedulingMetadata {
// 使用公共 getter 方法获取调度信息
// Use public getter methods to access scheduling info
return {
stage: system.getStage(),
before: [...system.getBefore()],
after: [...system.getAfter()],
sets: [...system.getSets()]
};
}
}

View File

@@ -0,0 +1,177 @@
/**
* 系统调度装饰器
*
* 提供声明式的系统调度配置,包括执行阶段和依赖关系。
*
* System scheduling decorators.
* Provides declarative system scheduling configuration including execution stage and dependencies.
*
* @example
* ```typescript
* @Stage('update')
* @After('PhysicsSystem')
* @Before('RenderSystem')
* @InSet('GameplaySystems')
* class MovementSystem extends EntitySystem {
* // ...
* }
* ```
*/
import type { SystemStage, SystemSchedulingMetadata } from '../Core/SystemScheduler';
/**
* 调度元数据 Symbol
* Scheduling metadata symbol
*/
export const SCHEDULING_METADATA = Symbol('schedulingMetadata');
/**
* 获取或创建调度元数据
* Get or create scheduling metadata
*/
function getOrCreateMetadata(target: object): SystemSchedulingMetadata {
const proto = target as Record<symbol, SystemSchedulingMetadata | undefined>;
if (!proto[SCHEDULING_METADATA]) {
proto[SCHEDULING_METADATA] = {
stage: 'update',
before: [],
after: [],
sets: []
};
}
return proto[SCHEDULING_METADATA]!;
}
/**
* 设置系统执行阶段
* Set system execution stage
*
* @param stage 执行阶段 | Execution stage
* - 'startup': 只在首帧执行一次 | Execute once on first frame
* - 'preUpdate': 在 update 之前执行 | Execute before update
* - 'update': 主更新阶段(默认)| Main update stage (default)
* - 'postUpdate': 在 update 之后执行 | Execute after update
* - 'cleanup': 帧末清理阶段 | End of frame cleanup stage
*
* @example
* ```typescript
* @Stage('preUpdate')
* class InputSystem extends EntitySystem { }
*
* @Stage('postUpdate')
* class RenderSystem extends EntitySystem { }
* ```
*/
export function Stage(stage: SystemStage): ClassDecorator {
return function (target) {
const metadata = getOrCreateMetadata(target.prototype);
metadata.stage = stage;
return target;
};
}
/**
* 声明此系统在指定系统之前执行
* Declare this system executes before specified systems
*
* @param systems 系统名称列表 | System names
*
* @example
* ```typescript
* @Before('RenderSystem', 'UISystem')
* class TransformSystem extends EntitySystem { }
* ```
*/
export function Before(...systems: string[]): ClassDecorator {
return function (target) {
const metadata = getOrCreateMetadata(target.prototype);
metadata.before.push(...systems);
return target;
};
}
/**
* 声明此系统在指定系统之后执行
* Declare this system executes after specified systems
*
* @param systems 系统名称列表(可使用 'set:' 前缀指定集合)| System names (use 'set:' prefix for sets)
*
* @example
* ```typescript
* @After('PhysicsSystem')
* class CollisionResponseSystem extends EntitySystem { }
*
* // 使用集合
* @After('set:PhysicsSystems')
* class GameLogicSystem extends EntitySystem { }
* ```
*/
export function After(...systems: string[]): ClassDecorator {
return function (target) {
const metadata = getOrCreateMetadata(target.prototype);
metadata.after.push(...systems);
return target;
};
}
/**
* 将系统加入指定集合
* Add system to specified sets
*
* 集合是虚拟分组,可用于批量依赖声明。
* Sets are virtual groups that can be used for batch dependency declarations.
*
* @param sets 集合名称列表 | Set names
*
* @example
* ```typescript
* @InSet('PhysicsSystems')
* class RigidBodySystem extends EntitySystem { }
*
* @InSet('PhysicsSystems')
* class CollisionSystem extends EntitySystem { }
*
* // 其他系统可以依赖整个集合
* @After('set:PhysicsSystems')
* class GameLogicSystem extends EntitySystem { }
* ```
*/
export function InSet(...sets: string[]): ClassDecorator {
return function (target) {
const metadata = getOrCreateMetadata(target.prototype);
metadata.sets.push(...sets);
return target;
};
}
/**
* 获取系统的调度元数据
* Get system scheduling metadata
*
* @param target 系统类或实例 | System class or instance
* @returns 调度元数据或 undefined | Scheduling metadata or undefined
*/
export function getSchedulingMetadata(target: object): SystemSchedulingMetadata | undefined {
// 从原型链查找
let proto = Object.getPrototypeOf(target);
while (proto) {
const metadata = (proto as Record<symbol, SystemSchedulingMetadata | undefined>)[SCHEDULING_METADATA];
if (metadata) {
return metadata;
}
proto = Object.getPrototypeOf(proto);
}
return undefined;
}
/**
* 检查系统是否有调度元数据
* Check if system has scheduling metadata
*
* @param target 系统类或实例 | System class or instance
* @returns 是否有元数据 | Whether has metadata
*/
export function hasSchedulingMetadata(target: object): boolean {
return getSchedulingMetadata(target) !== undefined;
}

View File

@@ -67,3 +67,17 @@ export type {
PropertyAssetType,
EnumOption
} from './PropertyDecorator';
// ============================================================================
// System Scheduling Decorators
// 系统调度装饰器
// ============================================================================
export {
Stage,
Before,
After,
InSet,
getSchedulingMetadata,
hasSchedulingMetadata,
SCHEDULING_METADATA
} from './SystemScheduling';

View File

@@ -6,6 +6,7 @@ import { createLogger } from '../Utils/Logger';
import { getComponentInstanceTypeName, getComponentTypeName } from './Decorators';
import { generateGUID } from '../Utils/GUID';
import type { IScene } from './IScene';
import { EntityHandle, NULL_HANDLE } from './Core/EntityHandle';
/**
* 组件活跃状态变化接口
@@ -93,6 +94,19 @@ export class Entity {
*/
public readonly persistentId: string;
/**
* 轻量级实体句柄
*
* 数值类型的实体标识符,包含索引和代数信息。
* 用于高性能场景下替代对象引用,支持 Archetype 存储等优化。
*
* Lightweight entity handle.
* Numeric identifier containing index and generation.
* Used for high-performance scenarios instead of object references,
* supports Archetype storage optimizations.
*/
private _handle: EntityHandle = NULL_HANDLE;
/**
* 所属场景引用
*/
@@ -171,6 +185,34 @@ export class Entity {
return this._lifecyclePolicy === EEntityLifecyclePolicy.Persistent;
}
/**
* 获取实体句柄
*
* 返回轻量级数值句柄,用于高性能场景。
* 如果实体尚未分配句柄,返回 NULL_HANDLE。
*
* Get entity handle.
* Returns lightweight numeric handle for high-performance scenarios.
* Returns NULL_HANDLE if entity has no handle assigned.
*/
public get handle(): EntityHandle {
return this._handle;
}
/**
* 设置实体句柄(内部使用)
*
* 此方法供 Scene 在创建实体时调用。
*
* Set entity handle (internal use).
* Called by Scene when creating entities.
*
* @internal
*/
public setHandle(handle: EntityHandle): void {
this._handle = handle;
}
/**
* 设置实体为持久化(跨场景保留)
*
@@ -559,6 +601,39 @@ export class Entity {
return component;
}
/**
* 标记组件为已修改
*
* 便捷方法,自动从场景获取当前 epoch 并标记组件。
* 用于帧级变更检测系统。
*
* Mark component(s) as modified.
* Convenience method that auto-gets epoch from scene and marks components.
* Used for frame-level change detection system.
*
* @param components 要标记的组件 | Components to mark
*
* @example
* ```typescript
* const pos = entity.getComponent(Position)!;
* pos.x = 100;
* entity.markDirty(pos);
*
* // 或者标记多个组件
* entity.markDirty(pos, vel);
* ```
*/
public markDirty(...components: Component[]): void {
if (!this.scene) {
return;
}
const epoch = this.scene.epochManager.current;
for (const component of components) {
component.markDirty(epoch);
}
}
/**
* 移除指定的组件
*

View File

@@ -5,6 +5,7 @@ import { EntitySystem } from './Systems/EntitySystem';
import { ComponentStorageManager, ComponentType } from './Core/ComponentStorage';
import { QuerySystem } from './Core/QuerySystem';
import { TypeSafeEventSystem } from './Core/EventSystem';
import { EpochManager } from './Core/EpochManager';
import type { ReferenceTracker } from './Core/ReferenceTracker';
import type { ServiceContainer, ServiceType } from '../Core/ServiceContainer';
import type { TypedQueryBuilder } from './Core/Query/TypedQuery';
@@ -71,6 +72,16 @@ export interface IScene {
*/
readonly referenceTracker: ReferenceTracker;
/**
* Epoch 管理器
*
* 用于帧级变更检测,追踪组件修改。
*
* Epoch manager.
* Used for frame-level change detection, tracking component modifications.
*/
readonly epochManager: EpochManager;
/**
* 服务容器
*

View File

@@ -27,6 +27,10 @@ import { AutoProfiler } from '../Utils/Profiler/AutoProfiler';
import { ServiceContainer, type ServiceType, type IService } from '../Core/ServiceContainer';
import { createInstance, isInjectable, injectProperties } from '../Core/DI';
import { createLogger } from '../Utils/Logger';
import { SystemScheduler, CycleDependencyError } from './Core/SystemScheduler';
import { EntityHandleManager } from './Core/EntityHandleManager';
import { EntityHandle, isValidHandle } from './Core/EntityHandle';
import { EpochManager } from './Core/EpochManager';
/**
* 游戏场景默认实现类
@@ -92,6 +96,38 @@ export class Scene implements IScene {
*/
public readonly referenceTracker: ReferenceTracker;
/**
* 实体句柄管理器
*
* 管理轻量级实体句柄的生命周期,包括创建、销毁和状态查询。
* 用于高性能场景下替代对象引用。
*
* Entity handle manager.
* Manages lifecycle of lightweight entity handles.
* Used for high-performance scenarios instead of object references.
*/
public readonly handleManager: EntityHandleManager;
/**
* 句柄到实体的映射
*
* 用于 O(1) 时间复杂度的句柄查找。
*
* Handle to entity mapping.
* Used for O(1) lookup by handle.
*/
private readonly _handleToEntity: Map<EntityHandle, Entity> = new Map();
/**
* Epoch 管理器
*
* 用于帧级变更检测,追踪组件的修改时间。
*
* Epoch manager.
* Used for frame-level change detection, tracking component modification time.
*/
public readonly epochManager: EpochManager = new EpochManager();
/**
* 服务容器
*
@@ -175,6 +211,18 @@ export class Scene implements IScene {
*/
private _systemAddCounter: number = 0;
/**
* 系统调度器
*
* 用于管理系统的执行阶段和依赖关系。
* 支持 before/after 依赖声明和拓扑排序。
*
* System scheduler.
* Manages system execution stages and dependencies.
* Supports before/after dependency declarations and topological sorting.
*/
private readonly _systemScheduler: SystemScheduler = new SystemScheduler();
/**
* 组件ID到系统的索引映射
*
@@ -220,12 +268,34 @@ export class Scene implements IScene {
/**
* 重新构建系统缓存
*
* 从服务容器中提取所有EntitySystem并排序
* 从服务容器中提取所有EntitySystem并排序
* 使用 SystemScheduler 进行依赖排序,支持 before/after 声明。
*
* Rebuild system cache.
* Extract all EntitySystems from service container and sort them.
* Uses SystemScheduler for dependency sorting, supports before/after declarations.
*/
private _rebuildSystemsCache(): EntitySystem[] {
const allServices = this._services.getAll();
const systems = this._filterEntitySystems(allServices);
return this._sortSystemsByUpdateOrder(systems);
try {
// 使用 SystemScheduler 进行依赖排序
this._systemScheduler.markDirty();
return this._systemScheduler.getAllSortedSystems(systems);
} catch (error) {
if (error instanceof CycleDependencyError) {
// 循环依赖错误,记录警告并回退到 updateOrder 排序
this.logger.error(
`[Scene] 系统存在循环依赖,回退到 updateOrder 排序 | Cycle dependency detected, falling back to updateOrder sort`,
error.involvedNodes
);
} else {
this.logger.error(`[Scene] 系统排序失败 | System sorting failed`, error);
}
// 回退到简单的 updateOrder 排序
return this._sortSystemsByUpdateOrder(systems);
}
}
/**
@@ -302,6 +372,7 @@ export class Scene implements IScene {
this.querySystem = new QuerySystem();
this.eventSystem = new TypeSafeEventSystem();
this.referenceTracker = new ReferenceTracker();
this.handleManager = new EntityHandleManager();
this._services = new ServiceContainer();
this.logger = createLogger('Scene');
@@ -429,12 +500,22 @@ export class Scene implements IScene {
// 清空组件索引 | Clear component indices
this._componentIdToSystems.clear();
this._globalNotifySystems.clear();
// 清空句柄映射并重置句柄管理器 | Clear handle mapping and reset handle manager
this._handleToEntity.clear();
this.handleManager.reset();
// 重置 epoch 管理器 | Reset epoch manager
this.epochManager.reset();
}
/**
* 更新场景
*/
public update() {
// 递增帧计数(用于变更检测) | Increment epoch (for change detection)
this.epochManager.increment();
// 开始性能采样帧
ProfilerSDK.beginFrame();
const frameHandle = ProfilerSDK.beginSample('Scene.update', ProfileCategory.ECS);
@@ -549,6 +630,13 @@ export class Scene implements IScene {
public createEntity(name: string) {
const entity = new Entity(name, this.identifierPool.checkOut());
// 分配轻量级句柄 | Assign lightweight handle
const handle = this.handleManager.create();
entity.setHandle(handle);
// 添加到句柄映射 | Add to handle mapping
this._handleToEntity.set(handle, entity);
this.eventSystem.emitSync('entity:created', { entityName: name, entity, scene: this });
return this.addEntity(entity);
@@ -736,6 +824,14 @@ export class Scene implements IScene {
for (let i = 0; i < count; i++) {
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
entity.scene = this;
// 分配轻量级句柄 | Assign lightweight handle
const handle = this.handleManager.create();
entity.setHandle(handle);
// 添加到句柄映射 | Add to handle mapping
this._handleToEntity.set(handle, entity);
entities.push(entity);
}
@@ -770,6 +866,12 @@ export class Scene implements IScene {
for (const entity of entities) {
this.entities.remove(entity);
this.querySystem.removeEntity(entity);
// 销毁句柄并从映射中移除 | Destroy handle and remove from mapping
if (isValidHandle(entity.handle)) {
this._handleToEntity.delete(entity.handle);
this.handleManager.destroy(entity.handle);
}
}
this.querySystem.clearCache();
@@ -801,6 +903,26 @@ export class Scene implements IScene {
return this.entities.findEntityById(id);
}
/**
* 根据句柄查找实体
*
* 通过轻量级句柄查找对应的实体O(1) 时间复杂度)。
* 如果句柄无效或实体已被销毁,返回 null。
*
* Find entity by handle (O(1) time complexity).
* Returns null if handle is invalid or entity has been destroyed.
*
* @param handle 实体句柄 | Entity handle
* @returns 实体实例或 null | Entity instance or null
*/
public findEntityByHandle(handle: EntityHandle): Entity | null {
if (!isValidHandle(handle) || !this.handleManager.isAlive(handle)) {
return null;
}
return this._handleToEntity.get(handle) ?? null;
}
/**
* 根据标签查找实体
* @param tag 实体标签

View File

@@ -11,6 +11,8 @@ import type { ComponentConstructor, ComponentInstance } from '../../Types/TypeHe
import type { IService } from '../../Core/ServiceContainer';
import { EntityCache } from './EntityCache';
import { CommandBuffer } from '../Core/CommandBuffer';
import type { SystemStage, SystemSchedulingMetadata } from '../Core/SystemScheduler';
import { getSchedulingMetadata } from '../Decorators/SystemScheduling';
/**
* 事件监听器记录
@@ -83,6 +85,12 @@ export abstract class EntitySystem implements ISystemBase, IService {
private _destroyed: boolean;
protected logger: ReturnType<typeof createLogger>;
/**
* 调度元数据
* Scheduling metadata
*/
protected _schedulingMetadata: SystemSchedulingMetadata;
/**
* 实体ID映射缓存
*/
@@ -118,6 +126,15 @@ export abstract class EntitySystem implements ISystemBase, IService {
*/
protected readonly commands: CommandBuffer = new CommandBuffer();
/**
* 上次处理的 epoch 检查点
* Last processed epoch checkpoint
*
* 用于变更检测,记录上次 forEachChanged 完成时的 epoch。
* Used for change detection, records epoch when forEachChanged last completed.
*/
private _lastProcessEpoch: number = 0;
/**
* 获取系统处理的实体列表
*/
@@ -205,6 +222,18 @@ export abstract class EntitySystem implements ISystemBase, IService {
this.logger = createLogger(this.getLoggerName());
this._entityCache = new EntityCache();
// 初始化调度元数据(优先使用装饰器元数据)
// Initialize scheduling metadata (prefer decorator metadata)
const decoratorMeta = getSchedulingMetadata(this);
this._schedulingMetadata = decoratorMeta
? { ...decoratorMeta }
: {
stage: 'update',
before: [],
after: [],
sets: []
};
}
/**
@@ -257,6 +286,135 @@ export abstract class EntitySystem implements ISystemBase, IService {
this._scene?.markSystemsOrderDirty();
}
// ========================================================================
// 调度配置方法 (Fluent API)
// Scheduling configuration methods (Fluent API)
// ========================================================================
/**
* 设置系统执行阶段
* Set system execution stage
*
* @param stage 执行阶段 | Execution stage
* @returns this (用于链式调用 | for chaining)
*
* @example
* ```typescript
* class MySystem extends EntitySystem {
* constructor() {
* super();
* this.stage('preUpdate');
* }
* }
* ```
*/
public stage(stage: SystemStage): this {
this._schedulingMetadata.stage = stage;
this._scene?.markSystemsOrderDirty();
return this;
}
/**
* 声明此系统在指定系统之前执行
* Declare this system executes before specified systems
*
* @param systems 系统名称列表 | System names
* @returns this (用于链式调用 | for chaining)
*
* @example
* ```typescript
* class MySystem extends EntitySystem {
* constructor() {
* super();
* this.before('RenderSystem');
* }
* }
* ```
*/
public before(...systems: string[]): this {
this._schedulingMetadata.before.push(...systems);
this._scene?.markSystemsOrderDirty();
return this;
}
/**
* 声明此系统在指定系统之后执行
* Declare this system executes after specified systems
*
* @param systems 系统名称列表(可使用 'set:' 前缀指定集合)| System names (use 'set:' prefix for sets)
* @returns this (用于链式调用 | for chaining)
*
* @example
* ```typescript
* class MySystem extends EntitySystem {
* constructor() {
* super();
* this.after('PhysicsSystem', 'set:CoreSystems');
* }
* }
* ```
*/
public after(...systems: string[]): this {
this._schedulingMetadata.after.push(...systems);
this._scene?.markSystemsOrderDirty();
return this;
}
/**
* 将系统加入指定集合
* Add system to specified sets
*
* @param sets 集合名称列表 | Set names
* @returns this (用于链式调用 | for chaining)
*
* @example
* ```typescript
* class MySystem extends EntitySystem {
* constructor() {
* super();
* this.inSet('PhysicsSystems');
* }
* }
* ```
*/
public inSet(...sets: string[]): this {
this._schedulingMetadata.sets.push(...sets);
this._scene?.markSystemsOrderDirty();
return this;
}
/**
* 获取系统的执行阶段
* Get system execution stage
*/
public getStage(): SystemStage {
return this._schedulingMetadata.stage;
}
/**
* 获取系统应该在哪些系统之前执行
* Get systems that this system should execute before
*/
public getBefore(): readonly string[] {
return this._schedulingMetadata.before;
}
/**
* 获取系统应该在哪些系统之后执行
* Get systems that this system should execute after
*/
public getAfter(): readonly string[] {
return this._schedulingMetadata.after;
}
/**
* 获取系统所属的集合
* Get sets that this system belongs to
*/
public getSets(): readonly string[] {
return this._schedulingMetadata.sets;
}
/**
* 系统初始化(框架调用)
*
@@ -1302,4 +1460,167 @@ export abstract class EntitySystem implements ISystemBase, IService {
}
return true;
}
// ========================================================================
// 变更检测方法 (Change Detection Methods)
// ========================================================================
/**
* 获取上次处理的 epoch 检查点
* Get the last processed epoch checkpoint
*
* @returns 上次处理完成时的 epoch | Epoch when last processing completed
*/
protected get lastProcessEpoch(): number {
return this._lastProcessEpoch;
}
/**
* 获取当前 epoch
* Get the current epoch
*
* @returns 当前的 epoch 值,如果 scene 不可用则返回 0 | Current epoch value, or 0 if scene unavailable
*/
protected get currentEpoch(): number {
return this._scene?.epochManager?.current ?? 0;
}
/**
* 保存当前 epoch 作为检查点
* Save current epoch as checkpoint
*
* 调用此方法后,下次 forEachChanged 将只处理此时间点之后变更的组件。
* After calling this, next forEachChanged will only process components changed after this point.
*/
protected saveEpoch(): void {
this._lastProcessEpoch = this.currentEpoch;
}
/**
* 遍历有变更组件的实体
* Iterate entities with changed components
*
* 只处理自上次 saveEpoch() 或指定 epoch 以来组件发生变更的实体。
* 处理完成后自动更新 lastProcessEpoch。
*
* Only processes entities whose components changed since last saveEpoch() or specified epoch.
* Automatically updates lastProcessEpoch after processing.
*
* @param entities 实体列表 | Entity list
* @param componentTypes 要检查变更的组件类型 | Component types to check for changes
* @param processor 处理函数 | Processor function
* @param sinceEpoch 可选的起始 epoch默认使用 lastProcessEpoch | Optional starting epoch, defaults to lastProcessEpoch
*
* @example
* ```typescript
* protected process(entities: readonly Entity[]): void {
* // 只处理 Velocity 组件发生变更的实体
* this.forEachChanged(entities, [Velocity], (entity) => {
* const vel = this.requireComponent(entity, Velocity);
* // 只有 velocity 改变时才重新计算
* this.updatePhysics(entity, vel);
* });
* }
* ```
*/
protected forEachChanged<T extends ComponentConstructor[]>(
entities: readonly Entity[],
componentTypes: T,
processor: (entity: Entity, index: number) => void,
sinceEpoch?: number
): void {
const checkEpoch = sinceEpoch ?? this._lastProcessEpoch;
for (let i = 0; i < entities.length; i++) {
const entity = entities[i]!;
let hasChanged = false;
// 检查任意指定组件是否发生变更
// Check if any specified component has changed
for (const componentType of componentTypes) {
const component = entity.getComponent(componentType);
if (component && component.lastWriteEpoch > checkEpoch) {
hasChanged = true;
break;
}
}
if (hasChanged) {
processor(entity, i);
}
}
// 自动更新检查点
// Automatically update checkpoint
this._lastProcessEpoch = this.currentEpoch;
}
/**
* 过滤出有变更组件的实体
* Filter entities with changed components
*
* @param entities 实体列表 | Entity list
* @param componentTypes 要检查变更的组件类型 | Component types to check for changes
* @param sinceEpoch 可选的起始 epoch | Optional starting epoch
* @returns 有变更的实体数组 | Array of entities with changes
*
* @example
* ```typescript
* protected process(entities: readonly Entity[]): void {
* const changedEntities = this.filterChanged(entities, [Position, Velocity]);
* for (const entity of changedEntities) {
* // 处理位置或速度变化的实体
* }
* this.saveEpoch();
* }
* ```
*/
protected filterChanged<T extends ComponentConstructor[]>(
entities: readonly Entity[],
componentTypes: T,
sinceEpoch?: number
): Entity[] {
const checkEpoch = sinceEpoch ?? this._lastProcessEpoch;
const result: Entity[] = [];
for (let i = 0; i < entities.length; i++) {
const entity = entities[i]!;
for (const componentType of componentTypes) {
const component = entity.getComponent(componentType);
if (component && component.lastWriteEpoch > checkEpoch) {
result.push(entity);
break;
}
}
}
return result;
}
/**
* 检查实体的指定组件是否发生变更
* Check if entity's specified components have changed
*
* @param entity 实体 | Entity
* @param componentTypes 要检查的组件类型 | Component types to check
* @param sinceEpoch 可选的起始 epoch | Optional starting epoch
* @returns 是否有变更 | Whether there are changes
*/
protected hasChanged<T extends ComponentConstructor[]>(
entity: Entity,
componentTypes: T,
sinceEpoch?: number
): boolean {
const checkEpoch = sinceEpoch ?? this._lastProcessEpoch;
for (const componentType of componentTypes) {
const component = entity.getComponent(componentType);
if (component && component.lastWriteEpoch > checkEpoch) {
return true;
}
}
return false;
}
}

View File

@@ -23,3 +23,35 @@ export type { ReactiveQueryChange, ReactiveQueryListener, ReactiveQueryConfig }
export { CommandBuffer, CommandType } from './Core/CommandBuffer';
export type { DeferredCommand } from './Core/CommandBuffer';
export * from './EntityTags';
// System Scheduling
export { SystemScheduler, CycleDependencyError, DEFAULT_STAGE_ORDER } from './Core/SystemScheduler';
export type { SystemStage, SystemSchedulingMetadata } from './Core/SystemScheduler';
export { SystemDependencyGraph } from './Core/SystemDependencyGraph';
export type { SystemDependencyInfo } from './Core/SystemDependencyGraph';
// Entity Handle
export {
makeHandle,
indexOf,
genOf,
isValidHandle,
handleEquals,
handleToString,
NULL_HANDLE,
INDEX_BITS,
GEN_BITS,
INDEX_MASK,
GEN_MASK,
MAX_ENTITIES,
MAX_GENERATION
} from './Core/EntityHandle';
export type { EntityHandle } from './Core/EntityHandle';
export { EntityHandleManager } from './Core/EntityHandleManager';
// Change Detection
export { EpochManager } from './Core/EpochManager';
// Compiled Query
export { CompiledQuery } from './Core/Query/CompiledQuery';
export type { InstanceTypes } from './Core/Query/CompiledQuery';

View File

@@ -16,9 +16,10 @@ export type ComponentInstance<T> = T extends new (...args: any[]) => infer R ? R
/**
* 组件构造函数类型
*
* 与 ComponentType 保持一致,避免类型转换
* 使用 Component 作为默认类型,与 ComponentType 保持一致
* 这确保类型兼容性,因为所有实际组件都继承自 Component 类。
*/
export type ComponentConstructor<T extends IComponent = IComponent> = new (...args: any[]) => T;
export type ComponentConstructor<T extends Component = Component> = new (...args: any[]) => T;
/**
* 组件类型的通用约束
@@ -237,7 +238,7 @@ export interface TypedQueryCondition<
/**
* 组件类型守卫
*/
export function isComponentType<T extends IComponent>(
export function isComponentType<T extends Component>(
value: any
): value is ComponentConstructor<T> {
return typeof value === 'function' && value.prototype instanceof Component;

View File

@@ -20,6 +20,22 @@ export interface IComponent {
/** 组件所属的实体ID */
entityId: number | null;
/**
* 最后写入的 epoch
*
* 用于帧级变更检测,记录组件最后一次被修改时的 epoch。
*
* Last write epoch for frame-level change detection.
*/
readonly lastWriteEpoch: number;
/**
* 标记组件为已修改
*
* Mark component as modified with current epoch.
*/
markDirty(epoch: number): void;
/** 组件添加到实体时的回调 */
onAddedToEntity(): void;
/** 组件从实体移除时的回调 */