* feat(asset-system): 实现路径稳定 ID 生成器 使用 FNV-1a hash 算法为纹理生成稳定的运行时 ID: - 新增 _pathIdCache 静态缓存,跨 Play/Stop 循环保持稳定 - 新增 getStableIdForPath() 方法,相同路径永远返回相同 ID - 修改 loadTextureForComponent/loadTextureByGuid 使用稳定 ID - clearTextureMappings() 不再清除 _pathIdCache 这解决了 Play/Stop 后纹理 ID 失效的根本问题。 * fix(runtime-core): 移除 Play/Stop 循环中的 clearTextureMappings 调用 使用路径稳定 ID 后,不再需要在快照保存/恢复时清除纹理缓存: - saveSceneSnapshot() 移除 clearTextureMappings() 调用 - restoreSceneSnapshot() 移除 clearTextureMappings() 调用 - 组件保存的 textureId 在 Play/Stop 后仍然有效 * fix(editor-core): 修复场景切换时的资源泄漏 在 openScene() 加载新场景前先卸载旧场景资源: - 调用 sceneResourceManager.unloadSceneResources() 释放旧资源 - 使用引用计数机制,仅卸载不再被引用的资源 - 路径稳定 ID 缓存不受影响,保持 ID 稳定性 * fix(runtime-core): 修复 PluginManager 组件注册类型错误 将 ComponentRegistry 类改为 GlobalComponentRegistry 实例: - registerComponents() 期望 IComponentRegistry 接口实例 - GlobalComponentRegistry 是 ComponentRegistry 的全局实例 * refactor(core): 提取 IComponentRegistry 接口 将组件注册表抽象为接口,支持场景级组件注册: - 新增 IComponentRegistry 接口定义 - Scene 持有独立的 componentRegistry 实例 - 支持从 GlobalComponentRegistry 克隆 - 各系统支持传入自定义注册表 * refactor(engine-core): 改进插件服务注册机制 - 更新 IComponentRegistry 类型引用 - 优化 PluginServiceRegistry 服务管理 * refactor(modules): 适配新的组件注册接口 更新各模块 RuntimeModule 使用 IComponentRegistry 接口: - audio, behavior-tree, camera - sprite, tilemap, world-streaming * fix(physics-rapier2d): 修复物理插件组件注册 - PhysicsEditorPlugin 添加 runtimeModule 引用 - 适配 IComponentRegistry 接口 - 修复物理组件在场景加载时未注册的问题 * feat(editor-core): 添加 UserCodeService 就绪信号机制 - 新增 waitForReady()/signalReady() API - 支持等待用户脚本编译完成 - 解决场景加载时组件未注册的时序问题 * fix(editor-app): 在编译完成后调用 signalReady() 确保用户脚本编译完成后发出就绪信号: - 编译成功后调用 userCodeService.signalReady() - 编译失败也要发出信号,避免阻塞场景加载 * feat(editor-core): 改进编辑器核心服务 - EntityStoreService 添加调试日志 - AssetRegistryService 优化资产注册 - PluginManager 改进插件管理 - IFileAPI 添加 getFileMtime 接口 * feat(engine): 改进 Rust 纹理管理器 - 支持任意 ID 的纹理加载(非递增) - 添加纹理状态追踪 API - 优化纹理缓存清理机制 - 更新 TypeScript 绑定 * feat(ui): 添加场景切换和文本闪烁组件 新增组件: - SceneLoadTriggerComponent: 场景切换触发器 - TextBlinkComponent: 文本闪烁效果 新增系统: - SceneLoadTriggerSystem: 处理场景切换逻辑 - TextBlinkSystem: 处理文本闪烁动画 其他改进: - UIRuntimeModule 适配新组件注册接口 - UI 渲染系统优化 * feat(editor-app): 添加外部文件修改检测 - 新增 ExternalModificationDialog 组件 - TauriFileAPI 支持 getFileMtime - 场景文件被外部修改时提示用户 * feat(editor-app): 添加渲染调试面板 - 新增 RenderDebugService 和调试面板 UI - App/ContentBrowser 添加调试日志 - TitleBar/Viewport 优化 - DialogManager 改进 * refactor(editor-app): 编辑器服务和组件优化 - EngineService 改进引擎集成 - EditorEngineSync 同步优化 - AssetFileInspector 改进 - VectorFieldEditors 优化 - InstantiatePrefabCommand 改进 * feat(i18n): 更新国际化翻译 - 添加新功能相关翻译 - 更新中文、英文、西班牙文 * feat(tauri): 添加文件修改时间查询命令 - 新增 get_file_mtime 命令 - 支持检测文件外部修改 * refactor(particle): 粒子系统改进 - 适配新的组件注册接口 - ParticleSystem 优化 - 添加单元测试 * refactor(platform): 平台适配层优化 - BrowserRuntime 改进 - 新增 RuntimeSceneManager 服务 - 导出优化 * refactor(asset-system-editor): 资产元数据改进 - AssetMetaFile 优化 - 导出调整 * fix(asset-system): 移除未使用的 TextureLoader 导入 * fix(tests): 更新测试以使用 GlobalComponentRegistry 实例 修复多个测试文件以适配 ComponentRegistry 从静态类变为实例类的变更: - ComponentStorage.test.ts: 使用 GlobalComponentRegistry.reset() - EntitySerializer.test.ts: 使用 GlobalComponentRegistry 实例 - IncrementalSerialization.test.ts: 使用 GlobalComponentRegistry 实例 - SceneSerializer.test.ts: 使用 GlobalComponentRegistry 实例 - ComponentRegistry.extended.test.ts: 使用 GlobalComponentRegistry,同时注册到 scene.componentRegistry - SystemTypes.test.ts: 在 Scene 创建前注册组件 - QuerySystem.test.ts: mockScene 添加 componentRegistry
911 lines
26 KiB
TypeScript
911 lines
26 KiB
TypeScript
import { Component } from './Component';
|
||
import { ComponentType, GlobalComponentRegistry } from './Core/ComponentStorage';
|
||
import { EEntityLifecyclePolicy } from './Core/EntityLifecyclePolicy';
|
||
import { BitMask64Utils, BitMask64Data } from './Utils/BigIntCompatibility';
|
||
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';
|
||
|
||
/**
|
||
* 组件活跃状态变化接口
|
||
*/
|
||
interface IActiveChangeable {
|
||
onActiveChanged(): void;
|
||
}
|
||
|
||
/**
|
||
* 实体比较器
|
||
*
|
||
* 用于比较两个实体的优先级,首先按更新顺序比较,然后按ID比较。
|
||
*/
|
||
export class EntityComparer {
|
||
/**
|
||
* 比较两个实体
|
||
*
|
||
* @param self - 第一个实体
|
||
* @param other - 第二个实体
|
||
* @returns 比较结果,负数表示self优先级更高,正数表示other优先级更高,0表示相等
|
||
*/
|
||
public compare(self: Entity, other: Entity): number {
|
||
let compare = self.updateOrder - other.updateOrder;
|
||
if (compare == 0) compare = self.id - other.id;
|
||
return compare;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 游戏实体类
|
||
*
|
||
* ECS架构中的实体(Entity),作为组件的容器。
|
||
* 实体本身不包含游戏逻辑,所有功能都通过组件来实现。
|
||
*
|
||
* 层级关系通过 HierarchyComponent 和 HierarchySystem 管理,
|
||
* 而非 Entity 内置属性,符合 ECS 组合原则。
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 创建实体
|
||
* const entity = scene.createEntity("Player");
|
||
*
|
||
* // 添加组件
|
||
* const healthComponent = entity.addComponent(new HealthComponent(100));
|
||
*
|
||
* // 获取组件
|
||
* const health = entity.getComponent(HealthComponent);
|
||
*
|
||
* // 层级关系使用 HierarchySystem
|
||
* const hierarchySystem = scene.getSystem(HierarchySystem);
|
||
* hierarchySystem.setParent(childEntity, parentEntity);
|
||
* ```
|
||
*/
|
||
export class Entity {
|
||
/**
|
||
* Entity专用日志器
|
||
*/
|
||
private static _logger = createLogger('Entity');
|
||
|
||
/**
|
||
* 实体比较器实例
|
||
*/
|
||
public static entityComparer: EntityComparer = new EntityComparer();
|
||
|
||
/**
|
||
* 实体名称
|
||
*/
|
||
public name: string;
|
||
|
||
/**
|
||
* 实体唯一标识符(运行时 ID)
|
||
*
|
||
* Runtime identifier for fast lookups.
|
||
*/
|
||
public readonly id: number;
|
||
|
||
/**
|
||
* 持久化唯一标识符(GUID)
|
||
*
|
||
* 用于序列化/反序列化时保持实体引用一致性。
|
||
* 在场景保存和加载时保持不变。
|
||
*
|
||
* Persistent identifier for serialization.
|
||
* Remains stable across save/load cycles.
|
||
*/
|
||
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;
|
||
|
||
/**
|
||
* 所属场景引用
|
||
*/
|
||
public scene: IScene | null = null;
|
||
|
||
/**
|
||
* 销毁状态标志
|
||
*/
|
||
private _isDestroyed: boolean = false;
|
||
|
||
/**
|
||
* 激活状态
|
||
*/
|
||
private _active: boolean = true;
|
||
|
||
/**
|
||
* 实体标签
|
||
*/
|
||
private _tag: number = 0;
|
||
|
||
/**
|
||
* 启用状态
|
||
*/
|
||
private _enabled: boolean = true;
|
||
|
||
/**
|
||
* 更新顺序
|
||
*/
|
||
private _updateOrder: number = 0;
|
||
|
||
/**
|
||
* 组件位掩码(用于快速 hasComponent 检查)
|
||
*/
|
||
private _componentMask: BitMask64Data = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||
|
||
/**
|
||
* 懒加载的组件数组缓存
|
||
*/
|
||
private _componentCache: Component[] | null = null;
|
||
|
||
/**
|
||
* 生命周期策略
|
||
*
|
||
* Lifecycle policy for scene transitions.
|
||
*/
|
||
private _lifecyclePolicy: EEntityLifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
|
||
|
||
/**
|
||
* 构造函数
|
||
*
|
||
* @param name - 实体名称
|
||
* @param id - 实体唯一标识符(运行时 ID)
|
||
* @param persistentId - 持久化标识符(可选,用于反序列化时恢复)
|
||
*/
|
||
constructor(name: string, id: number, persistentId?: string) {
|
||
this.name = name;
|
||
this.id = id;
|
||
this.persistentId = persistentId ?? generateGUID();
|
||
}
|
||
|
||
/**
|
||
* 获取生命周期策略
|
||
*
|
||
* Get lifecycle policy.
|
||
*/
|
||
public get lifecyclePolicy(): EEntityLifecyclePolicy {
|
||
return this._lifecyclePolicy;
|
||
}
|
||
|
||
/**
|
||
* 检查实体是否为持久化实体
|
||
*
|
||
* Check if entity is persistent (survives scene transitions).
|
||
*/
|
||
public get isPersistent(): boolean {
|
||
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;
|
||
}
|
||
|
||
/**
|
||
* 设置实体为持久化(跨场景保留)
|
||
*
|
||
* 标记后的实体在场景切换时不会被销毁,会自动迁移到新场景。
|
||
*
|
||
* Mark entity as persistent (survives scene transitions).
|
||
* Persistent entities are automatically migrated to the new scene.
|
||
*
|
||
* @returns this,支持链式调用 | Returns this for chaining
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const player = scene.createEntity('Player')
|
||
* .setPersistent()
|
||
* .addComponent(new PlayerComponent());
|
||
* ```
|
||
*/
|
||
public setPersistent(): this {
|
||
this._lifecyclePolicy = EEntityLifecyclePolicy.Persistent;
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 设置实体为场景本地(随场景销毁)
|
||
*
|
||
* 将实体恢复为默认行为。
|
||
*
|
||
* Mark entity as scene-local (destroyed with scene).
|
||
* Restores default behavior.
|
||
*
|
||
* @returns this,支持链式调用 | Returns this for chaining
|
||
*/
|
||
public setSceneLocal(): this {
|
||
this._lifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 获取销毁状态
|
||
* @returns 如果实体已被销毁则返回true
|
||
*/
|
||
public get isDestroyed(): boolean {
|
||
return this._isDestroyed;
|
||
}
|
||
|
||
/**
|
||
* 设置销毁状态(内部使用)
|
||
*
|
||
* 此方法供Scene和批量操作使用,以提高性能。
|
||
* 不应在普通业务逻辑中调用,应使用destroy()方法。
|
||
*
|
||
* @internal
|
||
*/
|
||
public setDestroyedState(destroyed: boolean): void {
|
||
this._isDestroyed = destroyed;
|
||
}
|
||
|
||
/**
|
||
* 获取组件数组(懒加载)
|
||
* @returns 只读的组件数组
|
||
*/
|
||
public get components(): readonly Component[] {
|
||
if (this._componentCache === null) {
|
||
this._rebuildComponentCache();
|
||
}
|
||
return this._componentCache!;
|
||
}
|
||
|
||
/**
|
||
* 从存储重建组件缓存
|
||
*/
|
||
private _rebuildComponentCache(): void {
|
||
const components: Component[] = [];
|
||
|
||
if (!this.scene?.componentStorageManager) {
|
||
this._componentCache = components;
|
||
return;
|
||
}
|
||
|
||
const mask = this._componentMask;
|
||
const registry = this.scene.componentRegistry;
|
||
const maxBitIndex = registry.getRegisteredCount();
|
||
|
||
for (let bitIndex = 0; bitIndex < maxBitIndex; bitIndex++) {
|
||
if (BitMask64Utils.getBit(mask, bitIndex)) {
|
||
const componentType = registry.getTypeByBitIndex(bitIndex);
|
||
if (componentType) {
|
||
const component = this.scene.componentStorageManager.getComponent(this.id, componentType);
|
||
|
||
if (component) {
|
||
components.push(component);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
this._componentCache = components;
|
||
}
|
||
|
||
/**
|
||
* 获取活跃状态
|
||
*
|
||
* @returns 如果实体处于活跃状态则返回true
|
||
*/
|
||
public get active(): boolean {
|
||
return this._active;
|
||
}
|
||
|
||
/**
|
||
* 设置活跃状态
|
||
*
|
||
* @param value - 新的活跃状态
|
||
*/
|
||
public set active(value: boolean) {
|
||
if (this._active !== value) {
|
||
this._active = value;
|
||
this.onActiveChanged();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取实体标签
|
||
*
|
||
* @returns 实体的数字标签
|
||
*/
|
||
public get tag(): number {
|
||
return this._tag;
|
||
}
|
||
|
||
/**
|
||
* 设置实体标签
|
||
*
|
||
* @param value - 新的标签值
|
||
*/
|
||
public set tag(value: number) {
|
||
this._tag = value;
|
||
}
|
||
|
||
/**
|
||
* 获取启用状态
|
||
*
|
||
* @returns 如果实体已启用则返回true
|
||
*/
|
||
public get enabled(): boolean {
|
||
return this._enabled;
|
||
}
|
||
|
||
/**
|
||
* 设置启用状态
|
||
*
|
||
* @param value - 新的启用状态
|
||
*/
|
||
public set enabled(value: boolean) {
|
||
this._enabled = value;
|
||
}
|
||
|
||
/**
|
||
* 获取更新顺序
|
||
*
|
||
* @returns 实体的更新顺序值
|
||
*/
|
||
public get updateOrder(): number {
|
||
return this._updateOrder;
|
||
}
|
||
|
||
/**
|
||
* 设置更新顺序
|
||
*
|
||
* @param value - 新的更新顺序值
|
||
*/
|
||
public set updateOrder(value: number) {
|
||
this._updateOrder = value;
|
||
}
|
||
|
||
/**
|
||
* 获取组件位掩码
|
||
*
|
||
* @returns 实体的组件位掩码
|
||
*/
|
||
public get componentMask(): BitMask64Data {
|
||
return this._componentMask;
|
||
}
|
||
|
||
/**
|
||
* 创建并添加组件
|
||
*
|
||
* @param componentType - 组件类型构造函数
|
||
* @param args - 组件构造函数参数
|
||
* @returns 创建的组件实例
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const position = entity.createComponent(Position, 100, 200);
|
||
* const health = entity.createComponent(Health, 100);
|
||
* ```
|
||
*/
|
||
public createComponent<T extends Component>(
|
||
componentType: ComponentType<T>,
|
||
...args: ConstructorParameters<ComponentType<T>>
|
||
): T {
|
||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||
const component = new componentType(...args);
|
||
return this.addComponent(component);
|
||
}
|
||
|
||
/**
|
||
* 内部添加组件方法(不进行重复检查,用于初始化)
|
||
*
|
||
* @param component - 要添加的组件实例
|
||
* @returns 添加的组件实例
|
||
*/
|
||
private addComponentInternal<T extends Component>(component: T): T {
|
||
const componentType = component.constructor as ComponentType<T>;
|
||
|
||
// 更新位掩码(组件已通过 @ECSComponent 装饰器自动注册)
|
||
// Update bitmask (component already registered via @ECSComponent decorator)
|
||
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
|
||
const componentMask = registry.getBitMask(componentType);
|
||
BitMask64Utils.orInPlace(this._componentMask, componentMask);
|
||
|
||
// 使缓存失效
|
||
this._componentCache = null;
|
||
|
||
return component;
|
||
}
|
||
|
||
/**
|
||
* 通知Scene中的QuerySystem实体组件发生变动
|
||
*
|
||
* Notify the QuerySystem in Scene that entity components have changed
|
||
*
|
||
* @param changedComponentType 变化的组件类型(可选,用于优化通知) | Changed component type (optional, for optimized notification)
|
||
*/
|
||
private notifyQuerySystems(changedComponentType?: ComponentType): void {
|
||
if (this.scene && this.scene.querySystem) {
|
||
this.scene.querySystem.updateEntity(this);
|
||
this.scene.clearSystemEntityCaches();
|
||
// 事件驱动:立即通知关心该组件的系统 | Event-driven: notify systems that care about this component
|
||
if (this.scene.notifyEntityComponentChanged) {
|
||
this.scene.notifyEntityComponentChanged(this, changedComponentType);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 添加组件到实体
|
||
*
|
||
* @param component - 要添加的组件实例
|
||
* @returns 添加的组件实例
|
||
* @throws {Error} 如果实体已存在该类型的组件
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const position = new Position(100, 200);
|
||
* entity.addComponent(position);
|
||
* ```
|
||
*/
|
||
public addComponent<T extends Component>(component: T): T {
|
||
const componentType = component.constructor as ComponentType<T>;
|
||
|
||
if (!this.scene) {
|
||
throw new Error(
|
||
'Entity must be added to Scene before adding components. Use scene.createEntity() instead of new Entity()'
|
||
);
|
||
}
|
||
|
||
if (!this.scene.componentStorageManager) {
|
||
throw new Error('Scene does not have componentStorageManager');
|
||
}
|
||
|
||
if (this.hasComponent(componentType)) {
|
||
throw new Error(`Entity ${this.name} already has component ${getComponentTypeName(componentType)}`);
|
||
}
|
||
|
||
this.addComponentInternal(component);
|
||
|
||
this.scene.componentStorageManager.addComponent(this.id, component);
|
||
|
||
component.entityId = this.id;
|
||
if (this.scene.referenceTracker) {
|
||
this.scene.referenceTracker.registerEntityScene(this.id, this.scene);
|
||
}
|
||
|
||
// 编辑器模式下延迟执行 onAddedToEntity | Defer onAddedToEntity in editor mode
|
||
if (this.scene.isEditorMode) {
|
||
this.scene.queueDeferredComponentCallback(() => {
|
||
component.onAddedToEntity();
|
||
});
|
||
} else {
|
||
component.onAddedToEntity();
|
||
}
|
||
|
||
if (this.scene && this.scene.eventSystem) {
|
||
this.scene.eventSystem.emitSync('component:added', {
|
||
timestamp: Date.now(),
|
||
source: 'Entity',
|
||
entityId: this.id,
|
||
entityName: this.name,
|
||
entityTag: this.tag?.toString(),
|
||
componentType: getComponentTypeName(componentType),
|
||
component: component
|
||
});
|
||
}
|
||
|
||
this.notifyQuerySystems(componentType);
|
||
|
||
return component;
|
||
}
|
||
|
||
/**
|
||
* 获取指定类型的组件
|
||
*
|
||
* @param type - 组件类型构造函数
|
||
* @returns 组件实例,如果不存在则返回null
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const position = entity.getComponent(Position);
|
||
* if (position) {
|
||
* position.x += 10;
|
||
* position.y += 20;
|
||
* }
|
||
* ```
|
||
*/
|
||
public getComponent<T extends Component>(type: ComponentType<T>): T | null {
|
||
// 快速检查:位掩码
|
||
if (!this.hasComponent(type)) {
|
||
return null;
|
||
}
|
||
|
||
// 从Scene存储获取
|
||
if (!this.scene?.componentStorageManager) {
|
||
return null;
|
||
}
|
||
|
||
const component = this.scene.componentStorageManager.getComponent(this.id, type);
|
||
return component as T | null;
|
||
}
|
||
|
||
/**
|
||
* 检查实体是否拥有指定类型的组件
|
||
*
|
||
* @param type - 组件类型构造函数
|
||
* @returns 如果实体拥有该组件返回true,否则返回false
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* if (entity.hasComponent(Position)) {
|
||
* const position = entity.getComponent(Position)!;
|
||
* position.x += 10;
|
||
* }
|
||
* ```
|
||
*/
|
||
public hasComponent<T extends Component>(type: ComponentType<T>): boolean {
|
||
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
|
||
if (!registry.isRegistered(type)) {
|
||
return false;
|
||
}
|
||
|
||
const mask = registry.getBitMask(type);
|
||
return BitMask64Utils.hasAny(this._componentMask, mask);
|
||
}
|
||
|
||
/**
|
||
* 获取或创建指定类型的组件
|
||
*
|
||
* 如果组件已存在则返回现有组件,否则创建新组件并添加到实体
|
||
*
|
||
* @param type - 组件类型构造函数
|
||
* @param args - 组件构造函数参数(仅在创建新组件时使用)
|
||
* @returns 组件实例
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 确保实体拥有Position组件
|
||
* const position = entity.getOrCreateComponent(Position, 0, 0);
|
||
* position.x = 100;
|
||
* ```
|
||
*/
|
||
public getOrCreateComponent<T extends Component>(
|
||
type: ComponentType<T>,
|
||
...args: ConstructorParameters<ComponentType<T>>
|
||
): T {
|
||
let component = this.getComponent(type);
|
||
if (!component) {
|
||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||
component = this.createComponent(type, ...args);
|
||
}
|
||
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);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 移除指定的组件
|
||
*
|
||
* @param component - 要移除的组件实例
|
||
*/
|
||
public removeComponent(component: Component): void {
|
||
const componentType = component.constructor as ComponentType;
|
||
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
|
||
|
||
if (!registry.isRegistered(componentType)) {
|
||
return;
|
||
}
|
||
|
||
const bitIndex = registry.getBitIndex(componentType);
|
||
|
||
// 更新位掩码
|
||
BitMask64Utils.clearBit(this._componentMask, bitIndex);
|
||
|
||
// 使缓存失效
|
||
this._componentCache = null;
|
||
|
||
// 从Scene存储移除
|
||
if (this.scene?.componentStorageManager) {
|
||
this.scene.componentStorageManager.removeComponent(this.id, componentType);
|
||
}
|
||
|
||
if (this.scene?.referenceTracker) {
|
||
this.scene.referenceTracker.clearComponentReferences(component);
|
||
}
|
||
|
||
if (component.onRemovedFromEntity) {
|
||
component.onRemovedFromEntity();
|
||
}
|
||
|
||
component.entityId = null;
|
||
|
||
if (this.scene && this.scene.eventSystem) {
|
||
this.scene.eventSystem.emitSync('component:removed', {
|
||
timestamp: Date.now(),
|
||
source: 'Entity',
|
||
entityId: this.id,
|
||
entityName: this.name,
|
||
entityTag: this.tag?.toString(),
|
||
componentType: getComponentTypeName(componentType),
|
||
component: component
|
||
});
|
||
}
|
||
|
||
this.notifyQuerySystems(componentType);
|
||
}
|
||
|
||
/**
|
||
* 移除指定类型的组件
|
||
*
|
||
* @param type - 组件类型
|
||
* @returns 被移除的组件实例或null
|
||
*/
|
||
public removeComponentByType<T extends Component>(type: ComponentType<T>): T | null {
|
||
const component = this.getComponent(type);
|
||
if (component) {
|
||
this.removeComponent(component);
|
||
return component;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 移除所有组件
|
||
*/
|
||
public removeAllComponents(): void {
|
||
const componentsToRemove = [...this.components];
|
||
|
||
// 清除位掩码
|
||
BitMask64Utils.clear(this._componentMask);
|
||
|
||
// 使缓存失效
|
||
this._componentCache = null;
|
||
|
||
for (const component of componentsToRemove) {
|
||
const componentType = component.constructor as ComponentType;
|
||
|
||
if (this.scene?.componentStorageManager) {
|
||
this.scene.componentStorageManager.removeComponent(this.id, componentType);
|
||
}
|
||
|
||
component.onRemovedFromEntity();
|
||
}
|
||
|
||
this.notifyQuerySystems();
|
||
}
|
||
|
||
/**
|
||
* 批量添加组件
|
||
*
|
||
* @param components - 要添加的组件数组
|
||
* @returns 添加的组件数组
|
||
*/
|
||
public addComponents<T extends Component>(components: T[]): T[] {
|
||
const addedComponents: T[] = [];
|
||
|
||
for (const component of components) {
|
||
try {
|
||
addedComponents.push(this.addComponent(component));
|
||
} catch (error) {
|
||
Entity._logger.warn(`添加组件失败 ${getComponentInstanceTypeName(component)}:`, error);
|
||
}
|
||
}
|
||
|
||
return addedComponents;
|
||
}
|
||
|
||
/**
|
||
* 批量移除组件类型
|
||
*
|
||
* @param componentTypes - 要移除的组件类型数组
|
||
* @returns 被移除的组件数组
|
||
*/
|
||
public removeComponentsByTypes<T extends Component>(componentTypes: ComponentType<T>[]): (T | null)[] {
|
||
const removedComponents: (T | null)[] = [];
|
||
|
||
for (const componentType of componentTypes) {
|
||
removedComponents.push(this.removeComponentByType(componentType));
|
||
}
|
||
|
||
return removedComponents;
|
||
}
|
||
|
||
/**
|
||
* 获取所有指定类型的组件
|
||
*
|
||
* @param type - 组件类型
|
||
* @returns 组件实例数组
|
||
*/
|
||
public getComponents<T extends Component>(type: ComponentType<T>): T[] {
|
||
const result: T[] = [];
|
||
|
||
for (const component of this.components) {
|
||
if (component instanceof type) {
|
||
result.push(component as T);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 获取指定基类的组件(支持继承查找)
|
||
*
|
||
* 与 getComponent() 不同,此方法使用 instanceof 检查,支持子类查找。
|
||
* 性能比位掩码查询稍慢,但支持继承层次结构。
|
||
*
|
||
* @param baseType - 组件基类类型
|
||
* @returns 第一个匹配的组件实例,如果不存在则返回 null
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 查找 CompositeNodeComponent 或其子类
|
||
* const composite = entity.getComponentByType(CompositeNodeComponent);
|
||
* if (composite) {
|
||
* // composite 可能是 SequenceNode, SelectorNode 等
|
||
* }
|
||
* ```
|
||
*/
|
||
public getComponentByType<T extends Component>(baseType: ComponentType<T>): T | null {
|
||
for (const component of this.components) {
|
||
if (component instanceof baseType) {
|
||
return component as T;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 活跃状态改变时的回调
|
||
*/
|
||
private onActiveChanged(): void {
|
||
for (const component of this.components) {
|
||
if ('onActiveChanged' in component && typeof component.onActiveChanged === 'function') {
|
||
(component as IActiveChangeable).onActiveChanged();
|
||
}
|
||
}
|
||
|
||
if (this.scene && this.scene.eventSystem) {
|
||
this.scene.eventSystem.emitSync('entity:activeChanged', {
|
||
entity: this,
|
||
active: this._active
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 销毁实体
|
||
*
|
||
* 移除所有组件并标记为已销毁。
|
||
* 层级关系的清理由 HierarchySystem 处理。
|
||
*/
|
||
public destroy(): void {
|
||
if (this._isDestroyed) {
|
||
return;
|
||
}
|
||
|
||
this._isDestroyed = true;
|
||
|
||
if (this.scene && this.scene.referenceTracker) {
|
||
this.scene.referenceTracker.clearReferencesTo(this.id);
|
||
this.scene.referenceTracker.unregisterEntityScene(this.id);
|
||
}
|
||
|
||
this.removeAllComponents();
|
||
|
||
if (this.scene) {
|
||
if (this.scene.querySystem) {
|
||
this.scene.querySystem.removeEntity(this);
|
||
}
|
||
|
||
if (this.scene.entities) {
|
||
this.scene.entities.remove(this);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 比较实体
|
||
*
|
||
* @param other - 另一个实体
|
||
* @returns 比较结果
|
||
*/
|
||
public compareTo(other: Entity): number {
|
||
return EntityComparer.prototype.compare(this, other);
|
||
}
|
||
|
||
/**
|
||
* 获取实体的字符串表示
|
||
*
|
||
* @returns 实体的字符串描述
|
||
*/
|
||
public toString(): string {
|
||
return `Entity[${this.name}:${this.id}:${this.persistentId.slice(0, 8)}]`;
|
||
}
|
||
|
||
/**
|
||
* 获取实体的调试信息(包含组件缓存信息)
|
||
*
|
||
* @returns 包含实体详细信息的对象
|
||
*/
|
||
public getDebugInfo(): {
|
||
name: string;
|
||
id: number;
|
||
persistentId: string;
|
||
enabled: boolean;
|
||
active: boolean;
|
||
destroyed: boolean;
|
||
componentCount: number;
|
||
componentTypes: string[];
|
||
componentMask: string;
|
||
cacheBuilt: boolean;
|
||
} {
|
||
return {
|
||
name: this.name,
|
||
id: this.id,
|
||
persistentId: this.persistentId,
|
||
enabled: this._enabled,
|
||
active: this._active,
|
||
destroyed: this._isDestroyed,
|
||
componentCount: this.components.length,
|
||
componentTypes: this.components.map((c) => getComponentInstanceTypeName(c)),
|
||
componentMask: BitMask64Utils.toString(this._componentMask, 2),
|
||
cacheBuilt: this._componentCache !== null
|
||
};
|
||
}
|
||
}
|