feat(core):统一 Core 库的命名规范和代码风格 (#207)
This commit is contained in:
@@ -36,7 +36,7 @@ export abstract class Component implements IComponent {
|
|||||||
*
|
*
|
||||||
* 用于为每个组件分配唯一的ID。
|
* 用于为每个组件分配唯一的ID。
|
||||||
*/
|
*/
|
||||||
public static _idGenerator: number = 0;
|
private static idGenerator: number = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件唯一标识符
|
* 组件唯一标识符
|
||||||
@@ -58,7 +58,7 @@ export abstract class Component implements IComponent {
|
|||||||
* 自动分配唯一ID给组件。
|
* 自动分配唯一ID给组件。
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this.id = Component._idGenerator++;
|
this.id = Component.idGenerator++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -30,34 +30,34 @@ interface QueryCacheEntry {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 高性能实体查询系统
|
* 高性能实体查询系统
|
||||||
*
|
*
|
||||||
* 提供快速的实体查询功能,支持按组件类型、标签、名称等多种方式查询实体。
|
* 提供快速的实体查询功能,支持按组件类型、标签、名称等多种方式查询实体。
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* // 查询所有包含Position和Velocity组件的实体
|
* // 查询所有包含Position和Velocity组件的实体
|
||||||
* const movingEntities = querySystem.queryAll(PositionComponent, VelocityComponent);
|
* const movingEntities = querySystem.queryAll(PositionComponent, VelocityComponent);
|
||||||
*
|
*
|
||||||
* // 查询特定标签的实体
|
* // 查询特定标签的实体
|
||||||
* const playerEntities = querySystem.queryByTag(PLAYER_TAG);
|
* const playerEntities = querySystem.queryByTag(PLAYER_TAG);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export class QuerySystem {
|
export class QuerySystem {
|
||||||
private _logger = createLogger('QuerySystem');
|
private readonly _logger = createLogger('QuerySystem');
|
||||||
private entities: Entity[] = [];
|
private _entities: Entity[] = [];
|
||||||
private entityIndex: EntityIndex;
|
private _entityIndex: EntityIndex;
|
||||||
|
|
||||||
private _version = 0;
|
private _version = 0;
|
||||||
|
|
||||||
private queryCache = new Map<string, QueryCacheEntry>();
|
private _queryCache = new Map<string, QueryCacheEntry>();
|
||||||
private cacheMaxSize = 1000;
|
private _cacheMaxSize = 1000;
|
||||||
private cacheTimeout = 5000;
|
private _cacheTimeout = 5000;
|
||||||
|
|
||||||
private componentMaskCache = new Map<string, BitMask64Data>();
|
private _componentMaskCache = new Map<string, BitMask64Data>();
|
||||||
|
|
||||||
private archetypeSystem: ArchetypeSystem;
|
private _archetypeSystem: ArchetypeSystem;
|
||||||
|
|
||||||
private queryStats = {
|
private _queryStats = {
|
||||||
totalQueries: 0,
|
totalQueries: 0,
|
||||||
cacheHits: 0,
|
cacheHits: 0,
|
||||||
indexHits: 0,
|
indexHits: 0,
|
||||||
@@ -67,12 +67,12 @@ export class QuerySystem {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.entityIndex = {
|
this._entityIndex = {
|
||||||
byTag: new Map(),
|
byTag: new Map(),
|
||||||
byName: new Map()
|
byName: new Map()
|
||||||
};
|
};
|
||||||
|
|
||||||
this.archetypeSystem = new ArchetypeSystem();
|
this._archetypeSystem = new ArchetypeSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,7 +84,7 @@ export class QuerySystem {
|
|||||||
* @param entities 新的实体列表
|
* @param entities 新的实体列表
|
||||||
*/
|
*/
|
||||||
public setEntities(entities: Entity[]): void {
|
public setEntities(entities: Entity[]): void {
|
||||||
this.entities = entities;
|
this._entities = entities;
|
||||||
this.clearQueryCache();
|
this.clearQueryCache();
|
||||||
this.clearReactiveQueries();
|
this.clearReactiveQueries();
|
||||||
this.rebuildIndexes();
|
this.rebuildIndexes();
|
||||||
@@ -92,19 +92,19 @@ export class QuerySystem {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加单个实体到查询系统
|
* 添加单个实体到查询系统
|
||||||
*
|
*
|
||||||
* 将新实体添加到查询系统中,并自动更新相关索引。
|
* 将新实体添加到查询系统中,并自动更新相关索引。
|
||||||
* 为了提高批量添加性能,可以延迟缓存清理。
|
* 为了提高批量添加性能,可以延迟缓存清理。
|
||||||
*
|
*
|
||||||
* @param entity 要添加的实体
|
* @param entity 要添加的实体
|
||||||
* @param deferCacheClear 是否延迟缓存清理(用于批量操作)
|
* @param deferCacheClear 是否延迟缓存清理(用于批量操作)
|
||||||
*/
|
*/
|
||||||
public addEntity(entity: Entity, deferCacheClear: boolean = false): void {
|
public addEntity(entity: Entity, deferCacheClear: boolean = false): void {
|
||||||
if (!this.entities.includes(entity)) {
|
if (!this._entities.includes(entity)) {
|
||||||
this.entities.push(entity);
|
this._entities.push(entity);
|
||||||
this.addEntityToIndexes(entity);
|
this.addEntityToIndexes(entity);
|
||||||
|
|
||||||
this.archetypeSystem.addEntity(entity);
|
this._archetypeSystem.addEntity(entity);
|
||||||
|
|
||||||
// 通知响应式查询
|
// 通知响应式查询
|
||||||
this.notifyReactiveQueriesEntityAdded(entity);
|
this.notifyReactiveQueriesEntityAdded(entity);
|
||||||
@@ -121,26 +121,26 @@ export class QuerySystem {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量添加实体
|
* 批量添加实体
|
||||||
*
|
*
|
||||||
* 高效地批量添加多个实体,减少缓存清理次数。
|
* 高效地批量添加多个实体,减少缓存清理次数。
|
||||||
* 使用Set来避免O(n)的重复检查。
|
* 使用Set来避免O(n)的重复检查。
|
||||||
*
|
*
|
||||||
* @param entities 要添加的实体列表
|
* @param entities 要添加的实体列表
|
||||||
*/
|
*/
|
||||||
public addEntities(entities: Entity[]): void {
|
public addEntities(entities: Entity[]): void {
|
||||||
if (entities.length === 0) return;
|
if (entities.length === 0) return;
|
||||||
|
|
||||||
// 使用Set来快速检查重复
|
// 使用Set来快速检查重复
|
||||||
const existingIds = new Set(this.entities.map(e => e.id));
|
const existingIds = new Set(this._entities.map((e) => e.id));
|
||||||
let addedCount = 0;
|
let addedCount = 0;
|
||||||
|
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
if (!existingIds.has(entity.id)) {
|
if (!existingIds.has(entity.id)) {
|
||||||
this.entities.push(entity);
|
this._entities.push(entity);
|
||||||
this.addEntityToIndexes(entity);
|
this.addEntityToIndexes(entity);
|
||||||
|
|
||||||
// 更新索引管理器
|
// 更新索引管理器
|
||||||
this.archetypeSystem.addEntity(entity);
|
this._archetypeSystem.addEntity(entity);
|
||||||
|
|
||||||
existingIds.add(entity.id);
|
existingIds.add(entity.id);
|
||||||
addedCount++;
|
addedCount++;
|
||||||
@@ -155,10 +155,10 @@ export class QuerySystem {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量添加实体(无重复检查版本)
|
* 批量添加实体(无重复检查版本)
|
||||||
*
|
*
|
||||||
* 假设所有实体都是新的,跳过重复检查以获得最大性能。
|
* 假设所有实体都是新的,跳过重复检查以获得最大性能。
|
||||||
* 仅在确保没有重复实体时使用。
|
* 仅在确保没有重复实体时使用。
|
||||||
*
|
*
|
||||||
* @param entities 要添加的实体列表
|
* @param entities 要添加的实体列表
|
||||||
*/
|
*/
|
||||||
public addEntitiesUnchecked(entities: Entity[]): void {
|
public addEntitiesUnchecked(entities: Entity[]): void {
|
||||||
@@ -166,7 +166,7 @@ export class QuerySystem {
|
|||||||
|
|
||||||
// 避免调用栈溢出,分批添加
|
// 避免调用栈溢出,分批添加
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
this.entities.push(entity);
|
this._entities.push(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量更新索引
|
// 批量更新索引
|
||||||
@@ -174,7 +174,7 @@ export class QuerySystem {
|
|||||||
this.addEntityToIndexes(entity);
|
this.addEntityToIndexes(entity);
|
||||||
|
|
||||||
// 更新索引管理器
|
// 更新索引管理器
|
||||||
this.archetypeSystem.addEntity(entity);
|
this._archetypeSystem.addEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理缓存
|
// 清理缓存
|
||||||
@@ -189,17 +189,17 @@ export class QuerySystem {
|
|||||||
* @param entity 要移除的实体
|
* @param entity 要移除的实体
|
||||||
*/
|
*/
|
||||||
public removeEntity(entity: Entity): void {
|
public removeEntity(entity: Entity): void {
|
||||||
const index = this.entities.indexOf(entity);
|
const index = this._entities.indexOf(entity);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const componentTypes: ComponentType[] = [];
|
const componentTypes: ComponentType[] = [];
|
||||||
for (const component of entity.components) {
|
for (const component of entity.components) {
|
||||||
componentTypes.push(component.constructor as ComponentType);
|
componentTypes.push(component.constructor as ComponentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.entities.splice(index, 1);
|
this._entities.splice(index, 1);
|
||||||
this.removeEntityFromIndexes(entity);
|
this.removeEntityFromIndexes(entity);
|
||||||
|
|
||||||
this.archetypeSystem.removeEntity(entity);
|
this._archetypeSystem.removeEntity(entity);
|
||||||
|
|
||||||
if (componentTypes.length > 0) {
|
if (componentTypes.length > 0) {
|
||||||
this.notifyReactiveQueriesEntityRemoved(entity, componentTypes);
|
this.notifyReactiveQueriesEntityRemoved(entity, componentTypes);
|
||||||
@@ -222,7 +222,7 @@ export class QuerySystem {
|
|||||||
*/
|
*/
|
||||||
public updateEntity(entity: Entity): void {
|
public updateEntity(entity: Entity): void {
|
||||||
// 检查实体是否在查询系统中
|
// 检查实体是否在查询系统中
|
||||||
if (!this.entities.includes(entity)) {
|
if (!this._entities.includes(entity)) {
|
||||||
// 如果实体不在系统中,直接添加
|
// 如果实体不在系统中,直接添加
|
||||||
this.addEntity(entity);
|
this.addEntity(entity);
|
||||||
return;
|
return;
|
||||||
@@ -232,7 +232,7 @@ export class QuerySystem {
|
|||||||
this.removeEntityFromIndexes(entity);
|
this.removeEntityFromIndexes(entity);
|
||||||
|
|
||||||
// 更新ArchetypeSystem中的实体状态
|
// 更新ArchetypeSystem中的实体状态
|
||||||
this.archetypeSystem.updateEntity(entity);
|
this._archetypeSystem.updateEntity(entity);
|
||||||
// 重新添加实体到索引(基于新的组件状态)
|
// 重新添加实体到索引(基于新的组件状态)
|
||||||
this.addEntityToIndexes(entity);
|
this.addEntityToIndexes(entity);
|
||||||
|
|
||||||
@@ -253,28 +253,27 @@ export class QuerySystem {
|
|||||||
// 标签索引
|
// 标签索引
|
||||||
const tag = entity.tag;
|
const tag = entity.tag;
|
||||||
if (tag !== undefined) {
|
if (tag !== undefined) {
|
||||||
const tagSet = this.entityIndex.byTag.get(tag) || this.createAndSetTagIndex(tag);
|
const tagSet = this._entityIndex.byTag.get(tag) || this.createAndSetTagIndex(tag);
|
||||||
tagSet.add(entity);
|
tagSet.add(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 名称索引
|
// 名称索引
|
||||||
const name = entity.name;
|
const name = entity.name;
|
||||||
if (name) {
|
if (name) {
|
||||||
const nameSet = this.entityIndex.byName.get(name) || this.createAndSetNameIndex(name);
|
const nameSet = this._entityIndex.byName.get(name) || this.createAndSetNameIndex(name);
|
||||||
nameSet.add(entity);
|
nameSet.add(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private createAndSetTagIndex(tag: number): Set<Entity> {
|
private createAndSetTagIndex(tag: number): Set<Entity> {
|
||||||
const set = new Set<Entity>();
|
const set = new Set<Entity>();
|
||||||
this.entityIndex.byTag.set(tag, set);
|
this._entityIndex.byTag.set(tag, set);
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createAndSetNameIndex(name: string): Set<Entity> {
|
private createAndSetNameIndex(name: string): Set<Entity> {
|
||||||
const set = new Set<Entity>();
|
const set = new Set<Entity>();
|
||||||
this.entityIndex.byName.set(name, set);
|
this._entityIndex.byName.set(name, set);
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,22 +283,22 @@ export class QuerySystem {
|
|||||||
private removeEntityFromIndexes(entity: Entity): void {
|
private removeEntityFromIndexes(entity: Entity): void {
|
||||||
// 从标签索引移除
|
// 从标签索引移除
|
||||||
if (entity.tag !== undefined) {
|
if (entity.tag !== undefined) {
|
||||||
const tagSet = this.entityIndex.byTag.get(entity.tag);
|
const tagSet = this._entityIndex.byTag.get(entity.tag);
|
||||||
if (tagSet) {
|
if (tagSet) {
|
||||||
tagSet.delete(entity);
|
tagSet.delete(entity);
|
||||||
if (tagSet.size === 0) {
|
if (tagSet.size === 0) {
|
||||||
this.entityIndex.byTag.delete(entity.tag);
|
this._entityIndex.byTag.delete(entity.tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从名称索引移除
|
// 从名称索引移除
|
||||||
if (entity.name) {
|
if (entity.name) {
|
||||||
const nameSet = this.entityIndex.byName.get(entity.name);
|
const nameSet = this._entityIndex.byName.get(entity.name);
|
||||||
if (nameSet) {
|
if (nameSet) {
|
||||||
nameSet.delete(entity);
|
nameSet.delete(entity);
|
||||||
if (nameSet.size === 0) {
|
if (nameSet.size === 0) {
|
||||||
this.entityIndex.byName.delete(entity.name);
|
this._entityIndex.byName.delete(entity.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,15 +311,15 @@ export class QuerySystem {
|
|||||||
* 通常在大量实体变更后调用以确保索引一致性。
|
* 通常在大量实体变更后调用以确保索引一致性。
|
||||||
*/
|
*/
|
||||||
private rebuildIndexes(): void {
|
private rebuildIndexes(): void {
|
||||||
this.entityIndex.byTag.clear();
|
this._entityIndex.byTag.clear();
|
||||||
this.entityIndex.byName.clear();
|
this._entityIndex.byName.clear();
|
||||||
|
|
||||||
// 清理ArchetypeSystem和ComponentIndexManager
|
// 清理ArchetypeSystem和ComponentIndexManager
|
||||||
this.archetypeSystem.clear();
|
this._archetypeSystem.clear();
|
||||||
|
|
||||||
for (const entity of this.entities) {
|
for (const entity of this._entities) {
|
||||||
this.addEntityToIndexes(entity);
|
this.addEntityToIndexes(entity);
|
||||||
this.archetypeSystem.addEntity(entity);
|
this._archetypeSystem.addEntity(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +341,7 @@ export class QuerySystem {
|
|||||||
*/
|
*/
|
||||||
public queryAll(...componentTypes: ComponentType[]): QueryResult {
|
public queryAll(...componentTypes: ComponentType[]): QueryResult {
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
this.queryStats.totalQueries++;
|
this._queryStats.totalQueries++;
|
||||||
|
|
||||||
// 使用内部响应式查询作为智能缓存
|
// 使用内部响应式查询作为智能缓存
|
||||||
const reactiveQuery = this.getOrCreateReactiveQuery(QueryConditionType.ALL, componentTypes);
|
const reactiveQuery = this.getOrCreateReactiveQuery(QueryConditionType.ALL, componentTypes);
|
||||||
@@ -351,7 +350,7 @@ export class QuerySystem {
|
|||||||
const entities = reactiveQuery.getEntities();
|
const entities = reactiveQuery.getEntities();
|
||||||
|
|
||||||
// 统计为缓存命中(响应式查询本质上是永不过期的智能缓存)
|
// 统计为缓存命中(响应式查询本质上是永不过期的智能缓存)
|
||||||
this.queryStats.cacheHits++;
|
this._queryStats.cacheHits++;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entities,
|
entities,
|
||||||
@@ -379,7 +378,7 @@ export class QuerySystem {
|
|||||||
*/
|
*/
|
||||||
public queryAny(...componentTypes: ComponentType[]): QueryResult {
|
public queryAny(...componentTypes: ComponentType[]): QueryResult {
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
this.queryStats.totalQueries++;
|
this._queryStats.totalQueries++;
|
||||||
|
|
||||||
// 使用内部响应式查询作为智能缓存
|
// 使用内部响应式查询作为智能缓存
|
||||||
const reactiveQuery = this.getOrCreateReactiveQuery(QueryConditionType.ANY, componentTypes);
|
const reactiveQuery = this.getOrCreateReactiveQuery(QueryConditionType.ANY, componentTypes);
|
||||||
@@ -388,7 +387,7 @@ export class QuerySystem {
|
|||||||
const entities = reactiveQuery.getEntities();
|
const entities = reactiveQuery.getEntities();
|
||||||
|
|
||||||
// 统计为缓存命中(响应式查询本质上是永不过期的智能缓存)
|
// 统计为缓存命中(响应式查询本质上是永不过期的智能缓存)
|
||||||
this.queryStats.cacheHits++;
|
this._queryStats.cacheHits++;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entities,
|
entities,
|
||||||
@@ -416,7 +415,7 @@ export class QuerySystem {
|
|||||||
*/
|
*/
|
||||||
public queryNone(...componentTypes: ComponentType[]): QueryResult {
|
public queryNone(...componentTypes: ComponentType[]): QueryResult {
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
this.queryStats.totalQueries++;
|
this._queryStats.totalQueries++;
|
||||||
|
|
||||||
// 使用内部响应式查询作为智能缓存
|
// 使用内部响应式查询作为智能缓存
|
||||||
const reactiveQuery = this.getOrCreateReactiveQuery(QueryConditionType.NONE, componentTypes);
|
const reactiveQuery = this.getOrCreateReactiveQuery(QueryConditionType.NONE, componentTypes);
|
||||||
@@ -425,7 +424,7 @@ export class QuerySystem {
|
|||||||
const entities = reactiveQuery.getEntities();
|
const entities = reactiveQuery.getEntities();
|
||||||
|
|
||||||
// 统计为缓存命中(响应式查询本质上是永不过期的智能缓存)
|
// 统计为缓存命中(响应式查询本质上是永不过期的智能缓存)
|
||||||
this.queryStats.cacheHits++;
|
this._queryStats.cacheHits++;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entities,
|
entities,
|
||||||
@@ -437,13 +436,13 @@ export class QuerySystem {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 按标签查询实体
|
* 按标签查询实体
|
||||||
*
|
*
|
||||||
* 返回具有指定标签的所有实体。
|
* 返回具有指定标签的所有实体。
|
||||||
* 标签查询使用专用索引,具有很高的查询性能。
|
* 标签查询使用专用索引,具有很高的查询性能。
|
||||||
*
|
*
|
||||||
* @param tag 要查询的标签值
|
* @param tag 要查询的标签值
|
||||||
* @returns 查询结果,包含匹配的实体和性能信息
|
* @returns 查询结果,包含匹配的实体和性能信息
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* // 查询所有玩家实体
|
* // 查询所有玩家实体
|
||||||
@@ -452,14 +451,14 @@ export class QuerySystem {
|
|||||||
*/
|
*/
|
||||||
public queryByTag(tag: number): QueryResult {
|
public queryByTag(tag: number): QueryResult {
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
this.queryStats.totalQueries++;
|
this._queryStats.totalQueries++;
|
||||||
|
|
||||||
const cacheKey = `tag:${tag}`;
|
const cacheKey = `tag:${tag}`;
|
||||||
|
|
||||||
// 检查缓存
|
// 检查缓存
|
||||||
const cached = this.getFromCache(cacheKey);
|
const cached = this.getFromCache(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
this.queryStats.cacheHits++;
|
this._queryStats.cacheHits++;
|
||||||
return {
|
return {
|
||||||
entities: cached,
|
entities: cached,
|
||||||
count: cached.length,
|
count: cached.length,
|
||||||
@@ -469,8 +468,8 @@ export class QuerySystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用索引查询
|
// 使用索引查询
|
||||||
this.queryStats.indexHits++;
|
this._queryStats.indexHits++;
|
||||||
const entities = Array.from(this.entityIndex.byTag.get(tag) || []);
|
const entities = Array.from(this._entityIndex.byTag.get(tag) || []);
|
||||||
|
|
||||||
// 缓存结果
|
// 缓存结果
|
||||||
this.addToCache(cacheKey, entities);
|
this.addToCache(cacheKey, entities);
|
||||||
@@ -485,13 +484,13 @@ export class QuerySystem {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 按名称查询实体
|
* 按名称查询实体
|
||||||
*
|
*
|
||||||
* 返回具有指定名称的所有实体。
|
* 返回具有指定名称的所有实体。
|
||||||
* 名称查询使用专用索引,适用于查找特定的命名实体。
|
* 名称查询使用专用索引,适用于查找特定的命名实体。
|
||||||
*
|
*
|
||||||
* @param name 要查询的实体名称
|
* @param name 要查询的实体名称
|
||||||
* @returns 查询结果,包含匹配的实体和性能信息
|
* @returns 查询结果,包含匹配的实体和性能信息
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* // 查找名为"Player"的实体
|
* // 查找名为"Player"的实体
|
||||||
@@ -500,14 +499,14 @@ export class QuerySystem {
|
|||||||
*/
|
*/
|
||||||
public queryByName(name: string): QueryResult {
|
public queryByName(name: string): QueryResult {
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
this.queryStats.totalQueries++;
|
this._queryStats.totalQueries++;
|
||||||
|
|
||||||
const cacheKey = `name:${name}`;
|
const cacheKey = `name:${name}`;
|
||||||
|
|
||||||
// 检查缓存
|
// 检查缓存
|
||||||
const cached = this.getFromCache(cacheKey);
|
const cached = this.getFromCache(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
this.queryStats.cacheHits++;
|
this._queryStats.cacheHits++;
|
||||||
return {
|
return {
|
||||||
entities: cached,
|
entities: cached,
|
||||||
count: cached.length,
|
count: cached.length,
|
||||||
@@ -517,8 +516,8 @@ export class QuerySystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用索引查询
|
// 使用索引查询
|
||||||
this.queryStats.indexHits++;
|
this._queryStats.indexHits++;
|
||||||
const entities = Array.from(this.entityIndex.byName.get(name) || []);
|
const entities = Array.from(this._entityIndex.byName.get(name) || []);
|
||||||
|
|
||||||
// 缓存结果
|
// 缓存结果
|
||||||
this.addToCache(cacheKey, entities);
|
this.addToCache(cacheKey, entities);
|
||||||
@@ -533,13 +532,13 @@ export class QuerySystem {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 按单个组件类型查询实体
|
* 按单个组件类型查询实体
|
||||||
*
|
*
|
||||||
* 返回包含指定组件类型的所有实体。
|
* 返回包含指定组件类型的所有实体。
|
||||||
* 这是最基础的查询方法,具有最高的查询性能。
|
* 这是最基础的查询方法,具有最高的查询性能。
|
||||||
*
|
*
|
||||||
* @param componentType 要查询的组件类型
|
* @param componentType 要查询的组件类型
|
||||||
* @returns 查询结果,包含匹配的实体和性能信息
|
* @returns 查询结果,包含匹配的实体和性能信息
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* // 查询所有具有位置组件的实体
|
* // 查询所有具有位置组件的实体
|
||||||
@@ -548,14 +547,14 @@ export class QuerySystem {
|
|||||||
*/
|
*/
|
||||||
public queryByComponent<T extends Component>(componentType: ComponentType<T>): QueryResult {
|
public queryByComponent<T extends Component>(componentType: ComponentType<T>): QueryResult {
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
this.queryStats.totalQueries++;
|
this._queryStats.totalQueries++;
|
||||||
|
|
||||||
const cacheKey = this.generateCacheKey('component', [componentType]);
|
const cacheKey = this.generateCacheKey('component', [componentType]);
|
||||||
|
|
||||||
// 检查缓存
|
// 检查缓存
|
||||||
const cached = this.getFromCache(cacheKey);
|
const cached = this.getFromCache(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
this.queryStats.cacheHits++;
|
this._queryStats.cacheHits++;
|
||||||
return {
|
return {
|
||||||
entities: cached,
|
entities: cached,
|
||||||
count: cached.length,
|
count: cached.length,
|
||||||
@@ -564,8 +563,8 @@ export class QuerySystem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.queryStats.indexHits++;
|
this._queryStats.indexHits++;
|
||||||
const entities = this.archetypeSystem.getEntitiesByComponent(componentType);
|
const entities = this._archetypeSystem.getEntitiesByComponent(componentType);
|
||||||
|
|
||||||
// 缓存结果
|
// 缓存结果
|
||||||
this.addToCache(cacheKey, entities);
|
this.addToCache(cacheKey, entities);
|
||||||
@@ -582,12 +581,12 @@ export class QuerySystem {
|
|||||||
* 从缓存获取查询结果
|
* 从缓存获取查询结果
|
||||||
*/
|
*/
|
||||||
private getFromCache(cacheKey: string): readonly Entity[] | null {
|
private getFromCache(cacheKey: string): readonly Entity[] | null {
|
||||||
const entry = this.queryCache.get(cacheKey);
|
const entry = this._queryCache.get(cacheKey);
|
||||||
if (!entry) return null;
|
if (!entry) return null;
|
||||||
|
|
||||||
// 检查缓存是否过期或版本过期
|
// 检查缓存是否过期或版本过期
|
||||||
if (Date.now() - entry.timestamp > this.cacheTimeout || entry.version !== this._version) {
|
if (Date.now() - entry.timestamp > this._cacheTimeout || entry.version !== this._version) {
|
||||||
this.queryCache.delete(cacheKey);
|
this._queryCache.delete(cacheKey);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,11 +599,11 @@ export class QuerySystem {
|
|||||||
*/
|
*/
|
||||||
private addToCache(cacheKey: string, entities: Entity[]): void {
|
private addToCache(cacheKey: string, entities: Entity[]): void {
|
||||||
// 如果缓存已满,清理最少使用的条目
|
// 如果缓存已满,清理最少使用的条目
|
||||||
if (this.queryCache.size >= this.cacheMaxSize) {
|
if (this._queryCache.size >= this._cacheMaxSize) {
|
||||||
this.cleanupCache();
|
this.cleanupCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.queryCache.set(cacheKey, {
|
this._queryCache.set(cacheKey, {
|
||||||
entities: entities, // 直接使用引用,通过版本号控制失效
|
entities: entities, // 直接使用引用,通过版本号控制失效
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
hitCount: 0,
|
hitCount: 0,
|
||||||
@@ -618,22 +617,24 @@ export class QuerySystem {
|
|||||||
private cleanupCache(): void {
|
private cleanupCache(): void {
|
||||||
// 移除过期的缓存条目
|
// 移除过期的缓存条目
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
for (const [key, entry] of this.queryCache.entries()) {
|
for (const [key, entry] of this._queryCache.entries()) {
|
||||||
if (now - entry.timestamp > this.cacheTimeout) {
|
if (now - entry.timestamp > this._cacheTimeout) {
|
||||||
this.queryCache.delete(key);
|
this._queryCache.delete(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果还是太满,移除最少使用的条目
|
// 如果还是太满,移除最少使用的条目
|
||||||
if (this.queryCache.size >= this.cacheMaxSize) {
|
if (this._queryCache.size >= this._cacheMaxSize) {
|
||||||
let minHitCount = Infinity;
|
let minHitCount = Infinity;
|
||||||
let oldestKey = '';
|
let oldestKey = '';
|
||||||
let oldestTimestamp = Infinity;
|
let oldestTimestamp = Infinity;
|
||||||
|
|
||||||
// 单次遍历找到最少使用或最旧的条目
|
// 单次遍历找到最少使用或最旧的条目
|
||||||
for (const [key, entry] of this.queryCache.entries()) {
|
for (const [key, entry] of this._queryCache.entries()) {
|
||||||
if (entry.hitCount < minHitCount ||
|
if (
|
||||||
(entry.hitCount === minHitCount && entry.timestamp < oldestTimestamp)) {
|
entry.hitCount < minHitCount ||
|
||||||
|
(entry.hitCount === minHitCount && entry.timestamp < oldestTimestamp)
|
||||||
|
) {
|
||||||
minHitCount = entry.hitCount;
|
minHitCount = entry.hitCount;
|
||||||
oldestKey = key;
|
oldestKey = key;
|
||||||
oldestTimestamp = entry.timestamp;
|
oldestTimestamp = entry.timestamp;
|
||||||
@@ -641,7 +642,7 @@ export class QuerySystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (oldestKey) {
|
if (oldestKey) {
|
||||||
this.queryCache.delete(oldestKey);
|
this._queryCache.delete(oldestKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -650,8 +651,8 @@ export class QuerySystem {
|
|||||||
* 清除所有查询缓存
|
* 清除所有查询缓存
|
||||||
*/
|
*/
|
||||||
private clearQueryCache(): void {
|
private clearQueryCache(): void {
|
||||||
this.queryCache.clear();
|
this._queryCache.clear();
|
||||||
this.componentMaskCache.clear();
|
this._componentMaskCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -679,10 +680,13 @@ export class QuerySystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 多组件查询:使用排序后的类型名称创建键
|
// 多组件查询:使用排序后的类型名称创建键
|
||||||
const sortKey = componentTypes.map(t => {
|
const sortKey = componentTypes
|
||||||
const name = getComponentTypeName(t);
|
.map((t) => {
|
||||||
return name;
|
const name = getComponentTypeName(t);
|
||||||
}).sort().join(',');
|
return name;
|
||||||
|
})
|
||||||
|
.sort()
|
||||||
|
.join(',');
|
||||||
|
|
||||||
const fullKey = `${prefix}:${sortKey}`;
|
const fullKey = `${prefix}:${sortKey}`;
|
||||||
|
|
||||||
@@ -724,10 +728,7 @@ export class QuerySystem {
|
|||||||
* });
|
* });
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
public createReactiveQuery(
|
public createReactiveQuery(componentTypes: ComponentType[], config?: ReactiveQueryConfig): ReactiveQuery {
|
||||||
componentTypes: ComponentType[],
|
|
||||||
config?: ReactiveQueryConfig
|
|
||||||
): ReactiveQuery {
|
|
||||||
if (!componentTypes || componentTypes.length === 0) {
|
if (!componentTypes || componentTypes.length === 0) {
|
||||||
throw new Error('组件类型列表不能为空');
|
throw new Error('组件类型列表不能为空');
|
||||||
}
|
}
|
||||||
@@ -741,10 +742,7 @@ export class QuerySystem {
|
|||||||
|
|
||||||
const query = new ReactiveQuery(condition, config);
|
const query = new ReactiveQuery(condition, config);
|
||||||
|
|
||||||
const initialEntities = this.executeTraditionalQuery(
|
const initialEntities = this.executeTraditionalQuery(QueryConditionType.ALL, componentTypes);
|
||||||
QueryConditionType.ALL,
|
|
||||||
componentTypes
|
|
||||||
);
|
|
||||||
query.initializeWith(initialEntities);
|
query.initializeWith(initialEntities);
|
||||||
|
|
||||||
const cacheKey = this.generateCacheKey('all', componentTypes);
|
const cacheKey = this.generateCacheKey('all', componentTypes);
|
||||||
@@ -810,18 +808,21 @@ export class QuerySystem {
|
|||||||
*/
|
*/
|
||||||
private createComponentMask(componentTypes: ComponentType[]): BitMask64Data {
|
private createComponentMask(componentTypes: ComponentType[]): BitMask64Data {
|
||||||
// 生成缓存键
|
// 生成缓存键
|
||||||
const cacheKey = componentTypes.map(t => {
|
const cacheKey = componentTypes
|
||||||
return getComponentTypeName(t);
|
.map((t) => {
|
||||||
}).sort().join(',');
|
return getComponentTypeName(t);
|
||||||
|
})
|
||||||
|
.sort()
|
||||||
|
.join(',');
|
||||||
|
|
||||||
// 检查缓存
|
// 检查缓存
|
||||||
const cached = this.componentMaskCache.get(cacheKey);
|
const cached = this._componentMaskCache.get(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用ComponentRegistry而不是ComponentTypeManager,确保bitIndex一致
|
// 使用ComponentRegistry而不是ComponentTypeManager,确保bitIndex一致
|
||||||
let mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||||
for (const type of componentTypes) {
|
for (const type of componentTypes) {
|
||||||
// 确保组件已注册
|
// 确保组件已注册
|
||||||
if (!ComponentRegistry.isRegistered(type)) {
|
if (!ComponentRegistry.isRegistered(type)) {
|
||||||
@@ -832,7 +833,7 @@ export class QuerySystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 缓存结果
|
// 缓存结果
|
||||||
this.componentMaskCache.set(cacheKey, mask);
|
this._componentMaskCache.set(cacheKey, mask);
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -842,20 +843,20 @@ export class QuerySystem {
|
|||||||
public get version(): number {
|
public get version(): number {
|
||||||
return this._version;
|
return this._version;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有实体
|
* 获取所有实体
|
||||||
*/
|
*/
|
||||||
public getAllEntities(): readonly Entity[] {
|
public getAllEntities(): readonly Entity[] {
|
||||||
return this.entities;
|
return this._entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取系统统计信息
|
* 获取系统统计信息
|
||||||
*
|
*
|
||||||
* 返回查询系统的详细统计信息,包括实体数量、索引状态、
|
* 返回查询系统的详细统计信息,包括实体数量、索引状态、
|
||||||
* 查询性能统计等,用于性能监控和调试。
|
* 查询性能统计等,用于性能监控和调试。
|
||||||
*
|
*
|
||||||
* @returns 系统统计信息对象
|
* @returns 系统统计信息对象
|
||||||
*/
|
*/
|
||||||
public getStats(): {
|
public getStats(): {
|
||||||
@@ -883,28 +884,32 @@ export class QuerySystem {
|
|||||||
};
|
};
|
||||||
} {
|
} {
|
||||||
return {
|
return {
|
||||||
entityCount: this.entities.length,
|
entityCount: this._entities.length,
|
||||||
indexStats: {
|
indexStats: {
|
||||||
componentIndexSize: this.archetypeSystem.getAllArchetypes().length,
|
componentIndexSize: this._archetypeSystem.getAllArchetypes().length,
|
||||||
tagIndexSize: this.entityIndex.byTag.size,
|
tagIndexSize: this._entityIndex.byTag.size,
|
||||||
nameIndexSize: this.entityIndex.byName.size
|
nameIndexSize: this._entityIndex.byName.size
|
||||||
},
|
},
|
||||||
queryStats: {
|
queryStats: {
|
||||||
...this.queryStats,
|
...this._queryStats,
|
||||||
cacheHitRate: this.queryStats.totalQueries > 0 ?
|
cacheHitRate:
|
||||||
(this.queryStats.cacheHits / this.queryStats.totalQueries * 100).toFixed(2) + '%' : '0%'
|
this._queryStats.totalQueries > 0
|
||||||
|
? ((this._queryStats.cacheHits / this._queryStats.totalQueries) * 100).toFixed(2) + '%'
|
||||||
|
: '0%'
|
||||||
},
|
},
|
||||||
optimizationStats: {
|
optimizationStats: {
|
||||||
archetypeSystem: this.archetypeSystem.getAllArchetypes().map(a => ({
|
archetypeSystem: this._archetypeSystem.getAllArchetypes().map((a) => ({
|
||||||
id: a.id,
|
id: a.id,
|
||||||
componentTypes: a.componentTypes.map(t => getComponentTypeName(t)),
|
componentTypes: a.componentTypes.map((t) => getComponentTypeName(t)),
|
||||||
entityCount: a.entities.size
|
entityCount: a.entities.size
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
cacheStats: {
|
cacheStats: {
|
||||||
size: this._reactiveQueries.size,
|
size: this._reactiveQueries.size,
|
||||||
hitRate: this.queryStats.totalQueries > 0 ?
|
hitRate:
|
||||||
(this.queryStats.cacheHits / this.queryStats.totalQueries * 100).toFixed(2) + '%' : '0%'
|
this._queryStats.totalQueries > 0
|
||||||
|
? ((this._queryStats.cacheHits / this._queryStats.totalQueries) * 100).toFixed(2) + '%'
|
||||||
|
: '0%'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -915,7 +920,7 @@ export class QuerySystem {
|
|||||||
* @param entity 要查询的实体
|
* @param entity 要查询的实体
|
||||||
*/
|
*/
|
||||||
public getEntityArchetype(entity: Entity): Archetype | undefined {
|
public getEntityArchetype(entity: Entity): Archetype | undefined {
|
||||||
return this.archetypeSystem.getEntityArchetype(entity);
|
return this._archetypeSystem.getEntityArchetype(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -941,10 +946,7 @@ export class QuerySystem {
|
|||||||
* @param componentTypes 组件类型列表
|
* @param componentTypes 组件类型列表
|
||||||
* @returns 响应式查询实例
|
* @returns 响应式查询实例
|
||||||
*/
|
*/
|
||||||
private getOrCreateReactiveQuery(
|
private getOrCreateReactiveQuery(queryType: QueryConditionType, componentTypes: ComponentType[]): ReactiveQuery {
|
||||||
queryType: QueryConditionType,
|
|
||||||
componentTypes: ComponentType[]
|
|
||||||
): ReactiveQuery {
|
|
||||||
// 生成缓存键(与传统缓存键格式一致)
|
// 生成缓存键(与传统缓存键格式一致)
|
||||||
const cacheKey = this.generateCacheKey(queryType, componentTypes);
|
const cacheKey = this.generateCacheKey(queryType, componentTypes);
|
||||||
|
|
||||||
@@ -996,13 +998,10 @@ export class QuerySystem {
|
|||||||
* @param componentTypes 组件类型列表
|
* @param componentTypes 组件类型列表
|
||||||
* @returns 匹配的实体列表
|
* @returns 匹配的实体列表
|
||||||
*/
|
*/
|
||||||
private executeTraditionalQuery(
|
private executeTraditionalQuery(queryType: QueryConditionType, componentTypes: ComponentType[]): Entity[] {
|
||||||
queryType: QueryConditionType,
|
|
||||||
componentTypes: ComponentType[]
|
|
||||||
): Entity[] {
|
|
||||||
switch (queryType) {
|
switch (queryType) {
|
||||||
case QueryConditionType.ALL: {
|
case QueryConditionType.ALL: {
|
||||||
const archetypeResult = this.archetypeSystem.queryArchetypes(componentTypes, 'AND');
|
const archetypeResult = this._archetypeSystem.queryArchetypes(componentTypes, 'AND');
|
||||||
const entities: Entity[] = [];
|
const entities: Entity[] = [];
|
||||||
for (const archetype of archetypeResult.archetypes) {
|
for (const archetype of archetypeResult.archetypes) {
|
||||||
for (const entity of archetype.entities) {
|
for (const entity of archetype.entities) {
|
||||||
@@ -1012,7 +1011,7 @@ export class QuerySystem {
|
|||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
case QueryConditionType.ANY: {
|
case QueryConditionType.ANY: {
|
||||||
const archetypeResult = this.archetypeSystem.queryArchetypes(componentTypes, 'OR');
|
const archetypeResult = this._archetypeSystem.queryArchetypes(componentTypes, 'OR');
|
||||||
const entities: Entity[] = [];
|
const entities: Entity[] = [];
|
||||||
for (const archetype of archetypeResult.archetypes) {
|
for (const archetype of archetypeResult.archetypes) {
|
||||||
for (const entity of archetype.entities) {
|
for (const entity of archetype.entities) {
|
||||||
@@ -1023,9 +1022,7 @@ export class QuerySystem {
|
|||||||
}
|
}
|
||||||
case QueryConditionType.NONE: {
|
case QueryConditionType.NONE: {
|
||||||
const mask = this.createComponentMask(componentTypes);
|
const mask = this.createComponentMask(componentTypes);
|
||||||
return this.entities.filter(entity =>
|
return this._entities.filter((entity) => BitMask64Utils.hasNone(entity.componentMask, mask));
|
||||||
BitMask64Utils.hasNone(entity.componentMask, mask)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return [];
|
return [];
|
||||||
@@ -1139,10 +1136,10 @@ export class QuerySystem {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询构建器
|
* 查询构建器
|
||||||
*
|
*
|
||||||
* 提供链式API来构建复杂的实体查询条件。
|
* 提供链式API来构建复杂的实体查询条件。
|
||||||
* 支持组合多种查询条件,创建灵活的查询表达式。
|
* 支持组合多种查询条件,创建灵活的查询表达式。
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* const result = new QueryBuilder(querySystem)
|
* const result = new QueryBuilder(querySystem)
|
||||||
@@ -1162,7 +1159,7 @@ export class QueryBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加"必须包含所有组件"条件
|
* 添加"必须包含所有组件"条件
|
||||||
*
|
*
|
||||||
* @param componentTypes 必须包含的组件类型
|
* @param componentTypes 必须包含的组件类型
|
||||||
* @returns 查询构建器实例,支持链式调用
|
* @returns 查询构建器实例,支持链式调用
|
||||||
*/
|
*/
|
||||||
@@ -1177,7 +1174,7 @@ export class QueryBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加"必须包含任意组件"条件
|
* 添加"必须包含任意组件"条件
|
||||||
*
|
*
|
||||||
* @param componentTypes 必须包含其中任意一个的组件类型
|
* @param componentTypes 必须包含其中任意一个的组件类型
|
||||||
* @returns 查询构建器实例,支持链式调用
|
* @returns 查询构建器实例,支持链式调用
|
||||||
*/
|
*/
|
||||||
@@ -1192,7 +1189,7 @@ export class QueryBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加"不能包含任何组件"条件
|
* 添加"不能包含任何组件"条件
|
||||||
*
|
*
|
||||||
* @param componentTypes 不能包含的组件类型
|
* @param componentTypes 不能包含的组件类型
|
||||||
* @returns 查询构建器实例,支持链式调用
|
* @returns 查询构建器实例,支持链式调用
|
||||||
*/
|
*/
|
||||||
@@ -1207,9 +1204,9 @@ export class QueryBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行查询并返回结果
|
* 执行查询并返回结果
|
||||||
*
|
*
|
||||||
* 根据已添加的查询条件执行实体查询。
|
* 根据已添加的查询条件执行实体查询。
|
||||||
*
|
*
|
||||||
* @returns 查询结果,包含匹配的实体和性能信息
|
* @returns 查询结果,包含匹配的实体和性能信息
|
||||||
*/
|
*/
|
||||||
public execute(): QueryResult {
|
public execute(): QueryResult {
|
||||||
@@ -1241,7 +1238,7 @@ export class QueryBuilder {
|
|||||||
* 创建组件掩码
|
* 创建组件掩码
|
||||||
*/
|
*/
|
||||||
private createComponentMask(componentTypes: ComponentType[]): BitMask64Data {
|
private createComponentMask(componentTypes: ComponentType[]): BitMask64Data {
|
||||||
let mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||||
for (const type of componentTypes) {
|
for (const type of componentTypes) {
|
||||||
try {
|
try {
|
||||||
const bitMask = ComponentRegistry.getBitMask(type);
|
const bitMask = ComponentRegistry.getBitMask(type);
|
||||||
@@ -1255,13 +1252,13 @@ export class QueryBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置查询构建器
|
* 重置查询构建器
|
||||||
*
|
*
|
||||||
* 清除所有已添加的查询条件,重新开始构建查询。
|
* 清除所有已添加的查询条件,重新开始构建查询。
|
||||||
*
|
*
|
||||||
* @returns 查询构建器实例,支持链式调用
|
* @returns 查询构建器实例,支持链式调用
|
||||||
*/
|
*/
|
||||||
public reset(): QueryBuilder {
|
public reset(): QueryBuilder {
|
||||||
this.conditions = [];
|
this.conditions = [];
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,48 +8,45 @@ import type { IScene } from './IScene';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体比较器
|
* 实体比较器
|
||||||
*
|
*
|
||||||
* 用于比较两个实体的优先级,首先按更新顺序比较,然后按ID比较。
|
* 用于比较两个实体的优先级,首先按更新顺序比较,然后按ID比较。
|
||||||
*/
|
*/
|
||||||
export class EntityComparer {
|
export class EntityComparer {
|
||||||
/**
|
/**
|
||||||
* 比较两个实体
|
* 比较两个实体
|
||||||
*
|
*
|
||||||
* @param self - 第一个实体
|
* @param self - 第一个实体
|
||||||
* @param other - 第二个实体
|
* @param other - 第二个实体
|
||||||
* @returns 比较结果,负数表示self优先级更高,正数表示other优先级更高,0表示相等
|
* @returns 比较结果,负数表示self优先级更高,正数表示other优先级更高,0表示相等
|
||||||
*/
|
*/
|
||||||
public compare(self: Entity, other: Entity): number {
|
public compare(self: Entity, other: Entity): number {
|
||||||
let compare = self.updateOrder - other.updateOrder;
|
let compare = self.updateOrder - other.updateOrder;
|
||||||
if (compare == 0)
|
if (compare == 0) compare = self.id - other.id;
|
||||||
compare = self.id - other.id;
|
|
||||||
return compare;
|
return compare;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 游戏实体类
|
* 游戏实体类
|
||||||
*
|
*
|
||||||
* ECS架构中的实体(Entity),作为组件的容器。
|
* ECS架构中的实体(Entity),作为组件的容器。
|
||||||
* 实体本身不包含游戏逻辑,所有功能都通过组件来实现。
|
* 实体本身不包含游戏逻辑,所有功能都通过组件来实现。
|
||||||
* 支持父子关系,可以构建实体层次结构。
|
* 支持父子关系,可以构建实体层次结构。
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* // 创建实体
|
* // 创建实体
|
||||||
* const entity = new Entity("Player", 1);
|
* const entity = new Entity("Player", 1);
|
||||||
*
|
*
|
||||||
* // 添加组件
|
* // 添加组件
|
||||||
* const healthComponent = entity.addComponent(new HealthComponent(100));
|
* const healthComponent = entity.addComponent(new HealthComponent(100));
|
||||||
*
|
*
|
||||||
* // 获取组件
|
* // 获取组件
|
||||||
* const health = entity.getComponent(HealthComponent);
|
* const health = entity.getComponent(HealthComponent);
|
||||||
*
|
*
|
||||||
* // 添加位置组件
|
* // 添加位置组件
|
||||||
* entity.addComponent(new PositionComponent(100, 200));
|
* entity.addComponent(new PositionComponent(100, 200));
|
||||||
*
|
*
|
||||||
* // 添加子实体
|
* // 添加子实体
|
||||||
* const weapon = new Entity("Weapon", 2);
|
* const weapon = new Entity("Weapon", 2);
|
||||||
* entity.addChild(weapon);
|
* entity.addChild(weapon);
|
||||||
@@ -60,12 +57,12 @@ export class Entity {
|
|||||||
* Entity专用日志器
|
* Entity专用日志器
|
||||||
*/
|
*/
|
||||||
private static _logger = createLogger('Entity');
|
private static _logger = createLogger('Entity');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体比较器实例
|
* 实体比较器实例
|
||||||
*/
|
*/
|
||||||
public static entityComparer: EntityComparer = new EntityComparer();
|
public static entityComparer: EntityComparer = new EntityComparer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局事件总线实例
|
* 全局事件总线实例
|
||||||
* 用于发射组件相关事件
|
* 用于发射组件相关事件
|
||||||
@@ -84,17 +81,17 @@ export class Entity {
|
|||||||
entity.scene.clearSystemEntityCaches();
|
entity.scene.clearSystemEntityCaches();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体名称
|
* 实体名称
|
||||||
*/
|
*/
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体唯一标识符
|
* 实体唯一标识符
|
||||||
*/
|
*/
|
||||||
public readonly id: number;
|
public readonly id: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所属场景引用
|
* 所属场景引用
|
||||||
*/
|
*/
|
||||||
@@ -103,7 +100,7 @@ export class Entity {
|
|||||||
/**
|
/**
|
||||||
* 销毁状态标志
|
* 销毁状态标志
|
||||||
*/
|
*/
|
||||||
public _isDestroyed: boolean = false;
|
private _isDestroyed: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 父实体引用
|
* 父实体引用
|
||||||
@@ -164,6 +161,18 @@ export class Entity {
|
|||||||
return this._isDestroyed;
|
return this._isDestroyed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置销毁状态(内部使用)
|
||||||
|
*
|
||||||
|
* 此方法供Scene和批量操作使用,以提高性能。
|
||||||
|
* 不应在普通业务逻辑中调用,应使用destroy()方法。
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public setDestroyedState(destroyed: boolean): void {
|
||||||
|
this._isDestroyed = destroyed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取组件数组(懒加载)
|
* 获取组件数组(懒加载)
|
||||||
* @returns 只读的组件数组
|
* @returns 只读的组件数组
|
||||||
@@ -193,10 +202,7 @@ export class Entity {
|
|||||||
if (BitMask64Utils.getBit(mask, bitIndex)) {
|
if (BitMask64Utils.getBit(mask, bitIndex)) {
|
||||||
const componentType = ComponentRegistry.getTypeByBitIndex(bitIndex);
|
const componentType = ComponentRegistry.getTypeByBitIndex(bitIndex);
|
||||||
if (componentType) {
|
if (componentType) {
|
||||||
const component = this.scene.componentStorageManager.getComponent(
|
const component = this.scene.componentStorageManager.getComponent(this.id, componentType);
|
||||||
this.id,
|
|
||||||
componentType
|
|
||||||
);
|
|
||||||
|
|
||||||
if (component) {
|
if (component) {
|
||||||
components.push(component);
|
components.push(component);
|
||||||
@@ -218,7 +224,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取子实体数组的只读副本
|
* 获取子实体数组的只读副本
|
||||||
*
|
*
|
||||||
* @returns 子实体数组的副本
|
* @returns 子实体数组的副本
|
||||||
*/
|
*/
|
||||||
public get children(): readonly Entity[] {
|
public get children(): readonly Entity[] {
|
||||||
@@ -227,7 +233,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取子实体数量
|
* 获取子实体数量
|
||||||
*
|
*
|
||||||
* @returns 子实体的数量
|
* @returns 子实体的数量
|
||||||
*/
|
*/
|
||||||
public get childCount(): number {
|
public get childCount(): number {
|
||||||
@@ -236,7 +242,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取活跃状态
|
* 获取活跃状态
|
||||||
*
|
*
|
||||||
* @returns 如果实体处于活跃状态则返回true
|
* @returns 如果实体处于活跃状态则返回true
|
||||||
*/
|
*/
|
||||||
public get active(): boolean {
|
public get active(): boolean {
|
||||||
@@ -245,9 +251,9 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置活跃状态
|
* 设置活跃状态
|
||||||
*
|
*
|
||||||
* 设置实体的活跃状态,会影响子实体的有效活跃状态。
|
* 设置实体的活跃状态,会影响子实体的有效活跃状态。
|
||||||
*
|
*
|
||||||
* @param value - 新的活跃状态
|
* @param value - 新的活跃状态
|
||||||
*/
|
*/
|
||||||
public set active(value: boolean) {
|
public set active(value: boolean) {
|
||||||
@@ -259,9 +265,9 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取实体的有效活跃状态
|
* 获取实体的有效活跃状态
|
||||||
*
|
*
|
||||||
* 考虑父实体的活跃状态,只有当实体本身和所有父实体都处于活跃状态时才返回true。
|
* 考虑父实体的活跃状态,只有当实体本身和所有父实体都处于活跃状态时才返回true。
|
||||||
*
|
*
|
||||||
* @returns 有效的活跃状态
|
* @returns 有效的活跃状态
|
||||||
*/
|
*/
|
||||||
public get activeInHierarchy(): boolean {
|
public get activeInHierarchy(): boolean {
|
||||||
@@ -272,7 +278,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取实体标签
|
* 获取实体标签
|
||||||
*
|
*
|
||||||
* @returns 实体的数字标签
|
* @returns 实体的数字标签
|
||||||
*/
|
*/
|
||||||
public get tag(): number {
|
public get tag(): number {
|
||||||
@@ -281,7 +287,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置实体标签
|
* 设置实体标签
|
||||||
*
|
*
|
||||||
* @param value - 新的标签值
|
* @param value - 新的标签值
|
||||||
*/
|
*/
|
||||||
public set tag(value: number) {
|
public set tag(value: number) {
|
||||||
@@ -290,7 +296,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取启用状态
|
* 获取启用状态
|
||||||
*
|
*
|
||||||
* @returns 如果实体已启用则返回true
|
* @returns 如果实体已启用则返回true
|
||||||
*/
|
*/
|
||||||
public get enabled(): boolean {
|
public get enabled(): boolean {
|
||||||
@@ -299,7 +305,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置启用状态
|
* 设置启用状态
|
||||||
*
|
*
|
||||||
* @param value - 新的启用状态
|
* @param value - 新的启用状态
|
||||||
*/
|
*/
|
||||||
public set enabled(value: boolean) {
|
public set enabled(value: boolean) {
|
||||||
@@ -308,7 +314,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取更新顺序
|
* 获取更新顺序
|
||||||
*
|
*
|
||||||
* @returns 实体的更新顺序值
|
* @returns 实体的更新顺序值
|
||||||
*/
|
*/
|
||||||
public get updateOrder(): number {
|
public get updateOrder(): number {
|
||||||
@@ -317,7 +323,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置更新顺序
|
* 设置更新顺序
|
||||||
*
|
*
|
||||||
* @param value - 新的更新顺序值
|
* @param value - 新的更新顺序值
|
||||||
*/
|
*/
|
||||||
public set updateOrder(value: number) {
|
public set updateOrder(value: number) {
|
||||||
@@ -326,7 +332,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取组件位掩码
|
* 获取组件位掩码
|
||||||
*
|
*
|
||||||
* @returns 实体的组件位掩码
|
* @returns 实体的组件位掩码
|
||||||
*/
|
*/
|
||||||
public get componentMask(): BitMask64Data {
|
public get componentMask(): BitMask64Data {
|
||||||
@@ -346,10 +352,7 @@ export class Entity {
|
|||||||
* const health = entity.createComponent(Health, 100);
|
* const health = entity.createComponent(Health, 100);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
public createComponent<T extends Component>(
|
public createComponent<T extends Component>(componentType: ComponentType<T>, ...args: any[]): T {
|
||||||
componentType: ComponentType<T>,
|
|
||||||
...args: any[]
|
|
||||||
): T {
|
|
||||||
const component = new componentType(...args);
|
const component = new componentType(...args);
|
||||||
return this.addComponent(component);
|
return this.addComponent(component);
|
||||||
}
|
}
|
||||||
@@ -394,11 +397,13 @@ export class Entity {
|
|||||||
const componentType = component.constructor as ComponentType<T>;
|
const componentType = component.constructor as ComponentType<T>;
|
||||||
|
|
||||||
if (!this.scene) {
|
if (!this.scene) {
|
||||||
throw new Error(`Entity must be added to Scene before adding components. Use scene.createEntity() instead of new Entity()`);
|
throw new Error(
|
||||||
|
'Entity must be added to Scene before adding components. Use scene.createEntity() instead of new Entity()'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.scene.componentStorageManager) {
|
if (!this.scene.componentStorageManager) {
|
||||||
throw new Error(`Scene does not have componentStorageManager`);
|
throw new Error('Scene does not have componentStorageManager');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasComponent(componentType)) {
|
if (this.hasComponent(componentType)) {
|
||||||
@@ -427,7 +432,6 @@ export class Entity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 通知所有相关的QuerySystem组件已变动
|
// 通知所有相关的QuerySystem组件已变动
|
||||||
Entity.notifyQuerySystems(this);
|
Entity.notifyQuerySystems(this);
|
||||||
|
|
||||||
@@ -464,9 +468,6 @@ export class Entity {
|
|||||||
return component as T | null;
|
return component as T | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查实体是否拥有指定类型的组件
|
* 检查实体是否拥有指定类型的组件
|
||||||
*
|
*
|
||||||
@@ -485,7 +486,7 @@ export class Entity {
|
|||||||
if (!ComponentRegistry.isRegistered(type)) {
|
if (!ComponentRegistry.isRegistered(type)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mask = ComponentRegistry.getBitMask(type);
|
const mask = ComponentRegistry.getBitMask(type);
|
||||||
return BitMask64Utils.hasAny(this._componentMask, mask);
|
return BitMask64Utils.hasAny(this._componentMask, mask);
|
||||||
}
|
}
|
||||||
@@ -506,10 +507,7 @@ export class Entity {
|
|||||||
* position.x = 100;
|
* position.x = 100;
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
public getOrCreateComponent<T extends Component>(
|
public getOrCreateComponent<T extends Component>(type: ComponentType<T>, ...args: any[]): T {
|
||||||
type: ComponentType<T>,
|
|
||||||
...args: any[]
|
|
||||||
): T {
|
|
||||||
let component = this.getComponent(type);
|
let component = this.getComponent(type);
|
||||||
if (!component) {
|
if (!component) {
|
||||||
component = this.createComponent(type, ...args);
|
component = this.createComponent(type, ...args);
|
||||||
@@ -570,7 +568,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除指定类型的组件
|
* 移除指定类型的组件
|
||||||
*
|
*
|
||||||
* @param type - 组件类型
|
* @param type - 组件类型
|
||||||
* @returns 被移除的组件实例或null
|
* @returns 被移除的组件实例或null
|
||||||
*/
|
*/
|
||||||
@@ -611,13 +609,13 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量添加组件
|
* 批量添加组件
|
||||||
*
|
*
|
||||||
* @param components - 要添加的组件数组
|
* @param components - 要添加的组件数组
|
||||||
* @returns 添加的组件数组
|
* @returns 添加的组件数组
|
||||||
*/
|
*/
|
||||||
public addComponents<T extends Component>(components: T[]): T[] {
|
public addComponents<T extends Component>(components: T[]): T[] {
|
||||||
const addedComponents: T[] = [];
|
const addedComponents: T[] = [];
|
||||||
|
|
||||||
for (const component of components) {
|
for (const component of components) {
|
||||||
try {
|
try {
|
||||||
addedComponents.push(this.addComponent(component));
|
addedComponents.push(this.addComponent(component));
|
||||||
@@ -625,28 +623,26 @@ export class Entity {
|
|||||||
Entity._logger.warn(`添加组件失败 ${getComponentInstanceTypeName(component)}:`, error);
|
Entity._logger.warn(`添加组件失败 ${getComponentInstanceTypeName(component)}:`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return addedComponents;
|
return addedComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量移除组件类型
|
* 批量移除组件类型
|
||||||
*
|
*
|
||||||
* @param componentTypes - 要移除的组件类型数组
|
* @param componentTypes - 要移除的组件类型数组
|
||||||
* @returns 被移除的组件数组
|
* @returns 被移除的组件数组
|
||||||
*/
|
*/
|
||||||
public removeComponentsByTypes<T extends Component>(componentTypes: ComponentType<T>[]): (T | null)[] {
|
public removeComponentsByTypes<T extends Component>(componentTypes: ComponentType<T>[]): (T | null)[] {
|
||||||
const removedComponents: (T | null)[] = [];
|
const removedComponents: (T | null)[] = [];
|
||||||
|
|
||||||
for (const componentType of componentTypes) {
|
for (const componentType of componentTypes) {
|
||||||
removedComponents.push(this.removeComponentByType(componentType));
|
removedComponents.push(this.removeComponentByType(componentType));
|
||||||
}
|
}
|
||||||
|
|
||||||
return removedComponents;
|
return removedComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有指定类型的组件
|
* 获取所有指定类型的组件
|
||||||
*
|
*
|
||||||
@@ -694,13 +690,13 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加子实体
|
* 添加子实体
|
||||||
*
|
*
|
||||||
* @param child - 要添加的子实体
|
* @param child - 要添加的子实体
|
||||||
* @returns 添加的子实体
|
* @returns 添加的子实体
|
||||||
*/
|
*/
|
||||||
public addChild(child: Entity): Entity {
|
public addChild(child: Entity): Entity {
|
||||||
if (child === this) {
|
if (child === this) {
|
||||||
throw new Error("Entity cannot be its own child");
|
throw new Error('Entity cannot be its own child');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (child._parent === this) {
|
if (child._parent === this) {
|
||||||
@@ -724,7 +720,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除子实体
|
* 移除子实体
|
||||||
*
|
*
|
||||||
* @param child - 要移除的子实体
|
* @param child - 要移除的子实体
|
||||||
* @returns 是否成功移除
|
* @returns 是否成功移除
|
||||||
*/
|
*/
|
||||||
@@ -745,7 +741,7 @@ export class Entity {
|
|||||||
*/
|
*/
|
||||||
public removeAllChildren(): void {
|
public removeAllChildren(): void {
|
||||||
const childrenToRemove = [...this._children];
|
const childrenToRemove = [...this._children];
|
||||||
|
|
||||||
for (const child of childrenToRemove) {
|
for (const child of childrenToRemove) {
|
||||||
this.removeChild(child);
|
this.removeChild(child);
|
||||||
}
|
}
|
||||||
@@ -753,7 +749,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据名称查找子实体
|
* 根据名称查找子实体
|
||||||
*
|
*
|
||||||
* @param name - 子实体名称
|
* @param name - 子实体名称
|
||||||
* @param recursive - 是否递归查找
|
* @param recursive - 是否递归查找
|
||||||
* @returns 找到的子实体或null
|
* @returns 找到的子实体或null
|
||||||
@@ -779,7 +775,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据标签查找子实体
|
* 根据标签查找子实体
|
||||||
*
|
*
|
||||||
* @param tag - 标签
|
* @param tag - 标签
|
||||||
* @param recursive - 是否递归查找
|
* @param recursive - 是否递归查找
|
||||||
* @returns 找到的子实体数组
|
* @returns 找到的子实体数组
|
||||||
@@ -804,7 +800,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取根实体
|
* 获取根实体
|
||||||
*
|
*
|
||||||
* @returns 层次结构的根实体
|
* @returns 层次结构的根实体
|
||||||
*/
|
*/
|
||||||
public getRoot(): Entity {
|
public getRoot(): Entity {
|
||||||
@@ -817,7 +813,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否是指定实体的祖先
|
* 检查是否是指定实体的祖先
|
||||||
*
|
*
|
||||||
* @param entity - 要检查的实体
|
* @param entity - 要检查的实体
|
||||||
* @returns 如果是祖先则返回true
|
* @returns 如果是祖先则返回true
|
||||||
*/
|
*/
|
||||||
@@ -834,7 +830,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否是指定实体的后代
|
* 检查是否是指定实体的后代
|
||||||
*
|
*
|
||||||
* @param entity - 要检查的实体
|
* @param entity - 要检查的实体
|
||||||
* @returns 如果是后代则返回true
|
* @returns 如果是后代则返回true
|
||||||
*/
|
*/
|
||||||
@@ -844,7 +840,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取层次深度
|
* 获取层次深度
|
||||||
*
|
*
|
||||||
* @returns 在层次结构中的深度(根实体为0)
|
* @returns 在层次结构中的深度(根实体为0)
|
||||||
*/
|
*/
|
||||||
public getDepth(): number {
|
public getDepth(): number {
|
||||||
@@ -859,7 +855,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 遍历所有子实体(深度优先)
|
* 遍历所有子实体(深度优先)
|
||||||
*
|
*
|
||||||
* @param callback - 对每个子实体执行的回调函数
|
* @param callback - 对每个子实体执行的回调函数
|
||||||
* @param recursive - 是否递归遍历
|
* @param recursive - 是否递归遍历
|
||||||
*/
|
*/
|
||||||
@@ -883,15 +879,14 @@ export class Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.scene && this.scene.eventSystem) {
|
if (this.scene && this.scene.eventSystem) {
|
||||||
this.scene.eventSystem.emitSync('entity:activeChanged', {
|
this.scene.eventSystem.emitSync('entity:activeChanged', {
|
||||||
entity: this,
|
entity: this,
|
||||||
active: this._active,
|
active: this._active,
|
||||||
activeInHierarchy: this.activeInHierarchy
|
activeInHierarchy: this.activeInHierarchy
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 销毁实体
|
* 销毁实体
|
||||||
*
|
*
|
||||||
@@ -949,7 +944,7 @@ export class Entity {
|
|||||||
collectChildren(this);
|
collectChildren(this);
|
||||||
|
|
||||||
for (const entity of toDestroy) {
|
for (const entity of toDestroy) {
|
||||||
entity._isDestroyed = true;
|
entity.setDestroyedState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entity of toDestroy) {
|
for (const entity of toDestroy) {
|
||||||
@@ -970,7 +965,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 比较实体
|
* 比较实体
|
||||||
*
|
*
|
||||||
* @param other - 另一个实体
|
* @param other - 另一个实体
|
||||||
* @returns 比较结果
|
* @returns 比较结果
|
||||||
*/
|
*/
|
||||||
@@ -980,7 +975,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取实体的字符串表示
|
* 获取实体的字符串表示
|
||||||
*
|
*
|
||||||
* @returns 实体的字符串描述
|
* @returns 实体的字符串描述
|
||||||
*/
|
*/
|
||||||
public toString(): string {
|
public toString(): string {
|
||||||
@@ -989,7 +984,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取实体的调试信息(包含组件缓存信息)
|
* 获取实体的调试信息(包含组件缓存信息)
|
||||||
*
|
*
|
||||||
* @returns 包含实体详细信息的对象
|
* @returns 包含实体详细信息的对象
|
||||||
*/
|
*/
|
||||||
public getDebugInfo(): {
|
public getDebugInfo(): {
|
||||||
@@ -1016,11 +1011,11 @@ export class Entity {
|
|||||||
activeInHierarchy: this.activeInHierarchy,
|
activeInHierarchy: this.activeInHierarchy,
|
||||||
destroyed: this._isDestroyed,
|
destroyed: this._isDestroyed,
|
||||||
componentCount: this.components.length,
|
componentCount: this.components.length,
|
||||||
componentTypes: this.components.map(c => getComponentInstanceTypeName(c)),
|
componentTypes: this.components.map((c) => getComponentInstanceTypeName(c)),
|
||||||
componentMask: BitMask64Utils.toString(this._componentMask, 2), // 二进制表示
|
componentMask: BitMask64Utils.toString(this._componentMask, 2), // 二进制表示
|
||||||
parentId: this._parent?.id || null,
|
parentId: this._parent?.id || null,
|
||||||
childCount: this._children.length,
|
childCount: this._children.length,
|
||||||
childIds: this._children.map(c => c.id),
|
childIds: this._children.map((c) => c.id),
|
||||||
depth: this.getDepth(),
|
depth: this.getDepth(),
|
||||||
cacheBuilt: this._componentCache !== null
|
cacheBuilt: this._componentCache !== null
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,10 +8,18 @@ import { TypeSafeEventSystem } from './Core/EventSystem';
|
|||||||
import { EventBus } from './Core/EventBus';
|
import { EventBus } from './Core/EventBus';
|
||||||
import { ReferenceTracker } from './Core/ReferenceTracker';
|
import { ReferenceTracker } from './Core/ReferenceTracker';
|
||||||
import { IScene, ISceneConfig } from './IScene';
|
import { IScene, ISceneConfig } from './IScene';
|
||||||
import { getComponentInstanceTypeName, getSystemInstanceTypeName, getSystemMetadata } from "./Decorators";
|
import { getComponentInstanceTypeName, getSystemInstanceTypeName, getSystemMetadata } from './Decorators';
|
||||||
import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
||||||
import { SceneSerializer, SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer';
|
import {
|
||||||
import { IncrementalSerializer, IncrementalSnapshot, IncrementalSerializationOptions } from './Serialization/IncrementalSerializer';
|
SceneSerializer,
|
||||||
|
SceneSerializationOptions,
|
||||||
|
SceneDeserializationOptions
|
||||||
|
} from './Serialization/SceneSerializer';
|
||||||
|
import {
|
||||||
|
IncrementalSerializer,
|
||||||
|
IncrementalSnapshot,
|
||||||
|
IncrementalSerializationOptions
|
||||||
|
} from './Serialization/IncrementalSerializer';
|
||||||
import { ComponentPoolManager } from './Core/ComponentPool';
|
import { ComponentPoolManager } from './Core/ComponentPool';
|
||||||
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
|
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
|
||||||
import { ServiceContainer, type ServiceType } from '../Core/ServiceContainer';
|
import { ServiceContainer, type ServiceType } from '../Core/ServiceContainer';
|
||||||
@@ -30,7 +38,7 @@ export class Scene implements IScene {
|
|||||||
*
|
*
|
||||||
* 用于标识和调试的友好名称。
|
* 用于标识和调试的友好名称。
|
||||||
*/
|
*/
|
||||||
public name: string = "";
|
public name: string = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 场景自定义数据
|
* 场景自定义数据
|
||||||
@@ -45,25 +53,24 @@ export class Scene implements IScene {
|
|||||||
* 管理场景内所有实体的生命周期。
|
* 管理场景内所有实体的生命周期。
|
||||||
*/
|
*/
|
||||||
public readonly entities: EntityList;
|
public readonly entities: EntityList;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体ID池
|
* 实体ID池
|
||||||
*
|
*
|
||||||
* 用于分配和回收实体的唯一标识符。
|
* 用于分配和回收实体的唯一标识符。
|
||||||
*/
|
*/
|
||||||
public readonly identifierPool: IdentifierPool;
|
public readonly identifierPool: IdentifierPool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件存储管理器
|
* 组件存储管理器
|
||||||
*
|
*
|
||||||
* 高性能的组件存储和查询系统。
|
* 高性能的组件存储和查询系统。
|
||||||
*/
|
*/
|
||||||
public readonly componentStorageManager: ComponentStorageManager;
|
public readonly componentStorageManager: ComponentStorageManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询系统
|
* 查询系统
|
||||||
*
|
*
|
||||||
* 基于位掩码的高性能实体查询系统。
|
* 基于位掩码的高性能实体查询系统。
|
||||||
*/
|
*/
|
||||||
public readonly querySystem: QuerySystem;
|
public readonly querySystem: QuerySystem;
|
||||||
@@ -231,35 +238,31 @@ export class Scene implements IScene {
|
|||||||
*/
|
*/
|
||||||
private get performanceMonitor(): PerformanceMonitor {
|
private get performanceMonitor(): PerformanceMonitor {
|
||||||
if (!this._performanceMonitor) {
|
if (!this._performanceMonitor) {
|
||||||
this._performanceMonitor = this._services.tryResolve(PerformanceMonitor)
|
this._performanceMonitor = this._services.tryResolve(PerformanceMonitor) ?? new PerformanceMonitor();
|
||||||
?? new PerformanceMonitor();
|
|
||||||
}
|
}
|
||||||
return this._performanceMonitor;
|
return this._performanceMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化场景
|
* 初始化场景
|
||||||
*
|
*
|
||||||
* 在场景创建时调用,子类可以重写此方法来设置初始实体和组件。
|
* 在场景创建时调用,子类可以重写此方法来设置初始实体和组件。
|
||||||
*/
|
*/
|
||||||
public initialize(): void {
|
public initialize(): void {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 场景开始运行时的回调
|
* 场景开始运行时的回调
|
||||||
*
|
*
|
||||||
* 在场景开始运行时调用,可以在此方法中执行场景启动逻辑。
|
* 在场景开始运行时调用,可以在此方法中执行场景启动逻辑。
|
||||||
*/
|
*/
|
||||||
public onStart(): void {
|
public onStart(): void {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 场景卸载时的回调
|
* 场景卸载时的回调
|
||||||
*
|
*
|
||||||
* 在场景被销毁时调用,可以在此方法中执行清理工作。
|
* 在场景被销毁时调用,可以在此方法中执行清理工作。
|
||||||
*/
|
*/
|
||||||
public unload(): void {
|
public unload(): void {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始场景,启动实体处理器等
|
* 开始场景,启动实体处理器等
|
||||||
@@ -338,10 +341,10 @@ export class Scene implements IScene {
|
|||||||
* @param name 实体名称
|
* @param name 实体名称
|
||||||
*/
|
*/
|
||||||
public createEntity(name: string) {
|
public createEntity(name: string) {
|
||||||
let entity = new Entity(name, this.identifierPool.checkOut());
|
const entity = new Entity(name, this.identifierPool.checkOut());
|
||||||
|
|
||||||
this.eventSystem.emitSync('entity:created', { entityName: name, entity, scene: this });
|
this.eventSystem.emitSync('entity:created', { entityName: name, entity, scene: this });
|
||||||
|
|
||||||
return this.addEntity(entity);
|
return this.addEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,31 +387,30 @@ export class Scene implements IScene {
|
|||||||
* @param namePrefix 实体名称前缀
|
* @param namePrefix 实体名称前缀
|
||||||
* @returns 创建的实体列表
|
* @returns 创建的实体列表
|
||||||
*/
|
*/
|
||||||
public createEntities(count: number, namePrefix: string = "Entity"): Entity[] {
|
public createEntities(count: number, namePrefix: string = 'Entity'): Entity[] {
|
||||||
const entities: Entity[] = [];
|
const entities: Entity[] = [];
|
||||||
|
|
||||||
// 批量创建实体对象,不立即添加到系统
|
// 批量创建实体对象,不立即添加到系统
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
|
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
|
||||||
entity.scene = this;
|
entity.scene = this;
|
||||||
entities.push(entity);
|
entities.push(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量添加到实体列表
|
// 批量添加到实体列表
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
this.entities.add(entity);
|
this.entities.add(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量添加到查询系统(无重复检查,性能最优)
|
// 批量添加到查询系统(无重复检查,性能最优)
|
||||||
this.querySystem.addEntitiesUnchecked(entities);
|
this.querySystem.addEntitiesUnchecked(entities);
|
||||||
|
|
||||||
// 批量触发事件(可选,减少事件开销)
|
// 批量触发事件(可选,减少事件开销)
|
||||||
this.eventSystem.emitSync('entities:batch_added', { entities, scene: this, count });
|
this.eventSystem.emitSync('entities:batch_added', { entities, scene: this, count });
|
||||||
|
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量销毁实体
|
* 批量销毁实体
|
||||||
*/
|
*/
|
||||||
@@ -416,7 +418,7 @@ export class Scene implements IScene {
|
|||||||
if (entities.length === 0) return;
|
if (entities.length === 0) return;
|
||||||
|
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
entity._isDestroyed = true;
|
entity.setDestroyedState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
@@ -473,7 +475,9 @@ export class Scene implements IScene {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据名称查找实体(别名方法)
|
* 根据名称查找实体(别名方法)
|
||||||
|
*
|
||||||
* @param name 实体名称
|
* @param name 实体名称
|
||||||
|
* @deprecated 请使用 findEntity() 代替此方法
|
||||||
*/
|
*/
|
||||||
public getEntityByName(name: string): Entity | null {
|
public getEntityByName(name: string): Entity | null {
|
||||||
return this.findEntity(name);
|
return this.findEntity(name);
|
||||||
@@ -481,7 +485,9 @@ export class Scene implements IScene {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据标签查找实体(别名方法)
|
* 根据标签查找实体(别名方法)
|
||||||
|
*
|
||||||
* @param tag 实体标签
|
* @param tag 实体标签
|
||||||
|
* @deprecated 请使用 findEntitiesByTag() 代替此方法
|
||||||
*/
|
*/
|
||||||
public getEntitiesByTag(tag: number): Entity[] {
|
public getEntitiesByTag(tag: number): Entity[] {
|
||||||
return this.findEntitiesByTag(tag);
|
return this.findEntitiesByTag(tag);
|
||||||
@@ -577,9 +583,7 @@ export class Scene implements IScene {
|
|||||||
* scene.addEntityProcessor(system);
|
* scene.addEntityProcessor(system);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
public addEntityProcessor<T extends EntitySystem>(
|
public addEntityProcessor<T extends EntitySystem>(systemTypeOrInstance: ServiceType<T> | T): T {
|
||||||
systemTypeOrInstance: ServiceType<T> | T
|
|
||||||
): T {
|
|
||||||
let system: T;
|
let system: T;
|
||||||
let constructor: any;
|
let constructor: any;
|
||||||
|
|
||||||
@@ -609,7 +613,7 @@ export class Scene implements IScene {
|
|||||||
} else {
|
} else {
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
`Attempting to register a different instance of ${constructor.name}, ` +
|
`Attempting to register a different instance of ${constructor.name}, ` +
|
||||||
`but type is already registered. Returning existing instance.`
|
'but type is already registered. Returning existing instance.'
|
||||||
);
|
);
|
||||||
return existingSystem as T;
|
return existingSystem as T;
|
||||||
}
|
}
|
||||||
@@ -758,7 +762,6 @@ export class Scene implements IScene {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取场景的调试信息
|
* 获取场景的调试信息
|
||||||
*/
|
*/
|
||||||
@@ -786,13 +789,13 @@ export class Scene implements IScene {
|
|||||||
entityCount: this.entities.count,
|
entityCount: this.entities.count,
|
||||||
processorCount: systems.length,
|
processorCount: systems.length,
|
||||||
isRunning: this._didSceneBegin,
|
isRunning: this._didSceneBegin,
|
||||||
entities: this.entities.buffer.map(entity => ({
|
entities: this.entities.buffer.map((entity) => ({
|
||||||
name: entity.name,
|
name: entity.name,
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
componentCount: entity.components.length,
|
componentCount: entity.components.length,
|
||||||
componentTypes: entity.components.map(c => getComponentInstanceTypeName(c))
|
componentTypes: entity.components.map((c) => getComponentInstanceTypeName(c))
|
||||||
})),
|
})),
|
||||||
processors: systems.map(processor => ({
|
processors: systems.map((processor) => ({
|
||||||
name: getSystemInstanceTypeName(processor),
|
name: getSystemInstanceTypeName(processor),
|
||||||
updateOrder: processor.updateOrder,
|
updateOrder: processor.updateOrder,
|
||||||
entityCount: (processor as any)._entities?.length || 0
|
entityCount: (processor as any)._entities?.length || 0
|
||||||
@@ -910,11 +913,7 @@ export class Scene implements IScene {
|
|||||||
throw new Error('必须先调用 createIncrementalSnapshot() 创建基础快照');
|
throw new Error('必须先调用 createIncrementalSnapshot() 创建基础快照');
|
||||||
}
|
}
|
||||||
|
|
||||||
return IncrementalSerializer.computeIncremental(
|
return IncrementalSerializer.computeIncremental(this, this._incrementalBaseSnapshot, options);
|
||||||
this,
|
|
||||||
this._incrementalBaseSnapshot,
|
|
||||||
options
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -941,14 +940,13 @@ export class Scene implements IScene {
|
|||||||
incremental: IncrementalSnapshot | string | Uint8Array,
|
incremental: IncrementalSnapshot | string | Uint8Array,
|
||||||
componentRegistry?: Map<string, any>
|
componentRegistry?: Map<string, any>
|
||||||
): void {
|
): void {
|
||||||
const isSerializedData = typeof incremental === 'string' ||
|
const isSerializedData = typeof incremental === 'string' || incremental instanceof Uint8Array;
|
||||||
incremental instanceof Uint8Array;
|
|
||||||
|
|
||||||
const snapshot = isSerializedData
|
const snapshot = isSerializedData
|
||||||
? IncrementalSerializer.deserializeIncremental(incremental as string | Uint8Array)
|
? IncrementalSerializer.deserializeIncremental(incremental as string | Uint8Array)
|
||||||
: incremental as IncrementalSnapshot;
|
: (incremental as IncrementalSnapshot);
|
||||||
|
|
||||||
const registry = componentRegistry || ComponentRegistry.getAllComponentNames() as Map<string, any>;
|
const registry = componentRegistry || (ComponentRegistry.getAllComponentNames() as Map<string, any>);
|
||||||
|
|
||||||
IncrementalSerializer.applyIncremental(this, snapshot, registry);
|
IncrementalSerializer.applyIncremental(this, snapshot, registry);
|
||||||
}
|
}
|
||||||
@@ -996,4 +994,4 @@ export class Scene implements IScene {
|
|||||||
public hasIncrementalSnapshot(): boolean {
|
public hasIncrementalSnapshot(): boolean {
|
||||||
return this._incrementalBaseSnapshot !== undefined;
|
return this._incrementalBaseSnapshot !== undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ interface EventListenerRecord {
|
|||||||
listenerRef: string;
|
listenerRef: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体系统的基类
|
* 实体系统的基类
|
||||||
*
|
*
|
||||||
@@ -64,9 +63,9 @@ interface EventListenerRecord {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export abstract class EntitySystem<
|
export abstract class EntitySystem<_TComponents extends readonly ComponentConstructor[] = []>
|
||||||
_TComponents extends readonly ComponentConstructor[] = []
|
implements ISystemBase, IService
|
||||||
> implements ISystemBase, IService {
|
{
|
||||||
private _updateOrder: number;
|
private _updateOrder: number;
|
||||||
private _enabled: boolean;
|
private _enabled: boolean;
|
||||||
private _performanceMonitor: PerformanceMonitor | null;
|
private _performanceMonitor: PerformanceMonitor | null;
|
||||||
@@ -77,7 +76,6 @@ export abstract class EntitySystem<
|
|||||||
private _scene: Scene | null;
|
private _scene: Scene | null;
|
||||||
protected logger: ReturnType<typeof createLogger>;
|
protected logger: ReturnType<typeof createLogger>;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体ID映射缓存
|
* 实体ID映射缓存
|
||||||
*/
|
*/
|
||||||
@@ -161,7 +159,6 @@ export abstract class EntitySystem<
|
|||||||
// 初始化logger
|
// 初始化logger
|
||||||
this.logger = createLogger(this.getLoggerName());
|
this.logger = createLogger(this.getLoggerName());
|
||||||
|
|
||||||
|
|
||||||
this._entityCache = {
|
this._entityCache = {
|
||||||
frame: null,
|
frame: null,
|
||||||
persistent: null,
|
persistent: null,
|
||||||
@@ -203,7 +200,9 @@ export abstract class EntitySystem<
|
|||||||
*/
|
*/
|
||||||
private getPerformanceMonitor(): PerformanceMonitor {
|
private getPerformanceMonitor(): PerformanceMonitor {
|
||||||
if (!this._performanceMonitor) {
|
if (!this._performanceMonitor) {
|
||||||
throw new Error(`${this._systemName}: PerformanceMonitor未注入,请确保在Core.create()之后再添加System到Scene`);
|
throw new Error(
|
||||||
|
`${this._systemName}: PerformanceMonitor未注入,请确保在Core.create()之后再添加System到Scene`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return this._performanceMonitor;
|
return this._performanceMonitor;
|
||||||
}
|
}
|
||||||
@@ -251,7 +250,7 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统初始化回调
|
* 系统初始化回调
|
||||||
*
|
*
|
||||||
* 子类可以重写此方法进行初始化操作。
|
* 子类可以重写此方法进行初始化操作。
|
||||||
*/
|
*/
|
||||||
protected onInitialize(): void {
|
protected onInitialize(): void {
|
||||||
@@ -316,16 +315,28 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否为单一条件查询
|
* 检查是否为单一条件查询
|
||||||
|
*
|
||||||
|
* 使用位运算优化多条件检测。将每种查询条件映射到不同的位:
|
||||||
|
* - all: 第0位 (1)
|
||||||
|
* - any: 第1位 (2)
|
||||||
|
* - none: 第2位 (4)
|
||||||
|
* - tag: 第3位 (8)
|
||||||
|
* - name: 第4位 (16)
|
||||||
|
* - component: 第5位 (32)
|
||||||
*/
|
*/
|
||||||
private isSingleCondition(condition: QueryCondition): boolean {
|
private isSingleCondition(condition: QueryCondition): boolean {
|
||||||
|
// 使用位OR运算合并所有条件标记
|
||||||
const flags =
|
const flags =
|
||||||
((condition.all.length > 0) ? 1 : 0) |
|
(condition.all.length > 0 ? 1 : 0) |
|
||||||
((condition.any.length > 0) ? 2 : 0) |
|
(condition.any.length > 0 ? 2 : 0) |
|
||||||
((condition.none.length > 0) ? 4 : 0) |
|
(condition.none.length > 0 ? 4 : 0) |
|
||||||
((condition.tag !== undefined) ? 8 : 0) |
|
(condition.tag !== undefined ? 8 : 0) |
|
||||||
((condition.name !== undefined) ? 16 : 0) |
|
(condition.name !== undefined ? 16 : 0) |
|
||||||
((condition.component !== undefined) ? 32 : 0);
|
(condition.component !== undefined ? 32 : 0);
|
||||||
|
|
||||||
|
// 位运算技巧:如果只有一个位被设置,则 flags & (flags - 1) == 0
|
||||||
|
// 例如:flags=4 (100), flags-1=3 (011), 4&3=0
|
||||||
|
// 但如果 flags=6 (110), flags-1=5 (101), 6&5=4≠0
|
||||||
return flags !== 0 && (flags & (flags - 1)) === 0;
|
return flags !== 0 && (flags & (flags - 1)) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,8 +483,7 @@ export abstract class EntitySystem<
|
|||||||
*/
|
*/
|
||||||
private getEntityIdMap(allEntities: readonly Entity[]): Map<number, Entity> {
|
private getEntityIdMap(allEntities: readonly Entity[]): Map<number, Entity> {
|
||||||
const currentVersion = this.scene?.querySystem?.version ?? 0;
|
const currentVersion = this.scene?.querySystem?.version ?? 0;
|
||||||
if (this._entityIdMap !== null &&
|
if (this._entityIdMap !== null && this._entityIdMapVersion === currentVersion) {
|
||||||
this._entityIdMapVersion === currentVersion) {
|
|
||||||
return this._entityIdMap;
|
return this._entityIdMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +541,7 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行复合查询
|
* 执行复合查询
|
||||||
*
|
*
|
||||||
* 使用基于ID集合的单次扫描算法进行复杂查询
|
* 使用基于ID集合的单次扫描算法进行复杂查询
|
||||||
*/
|
*/
|
||||||
private executeComplexQuery(condition: QueryCondition, querySystem: QuerySystem): readonly Entity[] {
|
private executeComplexQuery(condition: QueryCondition, querySystem: QuerySystem): readonly Entity[] {
|
||||||
@@ -590,7 +600,7 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 在系统处理开始前调用
|
* 在系统处理开始前调用
|
||||||
*
|
*
|
||||||
* 子类可以重写此方法进行预处理操作。
|
* 子类可以重写此方法进行预处理操作。
|
||||||
*/
|
*/
|
||||||
protected onBegin(): void {
|
protected onBegin(): void {
|
||||||
@@ -599,9 +609,9 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理实体列表
|
* 处理实体列表
|
||||||
*
|
*
|
||||||
* 系统的核心逻辑,子类必须实现此方法来定义具体的处理逻辑。
|
* 系统的核心逻辑,子类必须实现此方法来定义具体的处理逻辑。
|
||||||
*
|
*
|
||||||
* @param entities 要处理的实体列表
|
* @param entities 要处理的实体列表
|
||||||
*/
|
*/
|
||||||
protected process(_entities: readonly Entity[]): void {
|
protected process(_entities: readonly Entity[]): void {
|
||||||
@@ -610,9 +620,9 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 后期处理实体列表
|
* 后期处理实体列表
|
||||||
*
|
*
|
||||||
* 在主要处理逻辑之后执行,子类可以重写此方法。
|
* 在主要处理逻辑之后执行,子类可以重写此方法。
|
||||||
*
|
*
|
||||||
* @param entities 要处理的实体列表
|
* @param entities 要处理的实体列表
|
||||||
*/
|
*/
|
||||||
protected lateProcess(_entities: readonly Entity[]): void {
|
protected lateProcess(_entities: readonly Entity[]): void {
|
||||||
@@ -621,7 +631,7 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统处理完毕后调用
|
* 系统处理完毕后调用
|
||||||
*
|
*
|
||||||
* 子类可以重写此方法进行后处理操作。
|
* 子类可以重写此方法进行后处理操作。
|
||||||
*/
|
*/
|
||||||
protected onEnd(): void {
|
protected onEnd(): void {
|
||||||
@@ -630,10 +640,10 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查系统是否需要处理
|
* 检查系统是否需要处理
|
||||||
*
|
*
|
||||||
* 在启用系统时有用,但仅偶尔需要处理。
|
* 在启用系统时有用,但仅偶尔需要处理。
|
||||||
* 这只影响处理,不影响事件或订阅列表。
|
* 这只影响处理,不影响事件或订阅列表。
|
||||||
*
|
*
|
||||||
* @returns 如果系统应该处理,则为true,如果不处理则为false
|
* @returns 如果系统应该处理,则为true,如果不处理则为false
|
||||||
*/
|
*/
|
||||||
protected onCheckProcessing(): boolean {
|
protected onCheckProcessing(): boolean {
|
||||||
@@ -667,7 +677,7 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取系统信息的字符串表示
|
* 获取系统信息的字符串表示
|
||||||
*
|
*
|
||||||
* @returns 系统信息字符串
|
* @returns 系统信息字符串
|
||||||
*/
|
*/
|
||||||
public toString(): string {
|
public toString(): string {
|
||||||
@@ -711,9 +721,9 @@ export abstract class EntitySystem<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 当实体被添加到系统时调用
|
* 当实体被添加到系统时调用
|
||||||
*
|
*
|
||||||
* 子类可以重写此方法来处理实体添加事件。
|
* 子类可以重写此方法来处理实体添加事件。
|
||||||
*
|
*
|
||||||
* @param entity 被添加的实体
|
* @param entity 被添加的实体
|
||||||
*/
|
*/
|
||||||
protected onAdded(_entity: Entity): void {
|
protected onAdded(_entity: Entity): void {
|
||||||
@@ -798,12 +808,9 @@ export abstract class EntitySystem<
|
|||||||
* @param eventType 事件类型
|
* @param eventType 事件类型
|
||||||
* @param handler 事件处理函数
|
* @param handler 事件处理函数
|
||||||
*/
|
*/
|
||||||
protected removeEventListener<T = any>(
|
protected removeEventListener<T = any>(eventType: string, handler: EventHandler<T>): void {
|
||||||
eventType: string,
|
|
||||||
handler: EventHandler<T>
|
|
||||||
): void {
|
|
||||||
const listenerIndex = this._eventListeners.findIndex(
|
const listenerIndex = this._eventListeners.findIndex(
|
||||||
listener => listener.eventType === eventType && listener.handler === handler
|
(listener) => listener.eventType === eventType && listener.handler === handler
|
||||||
);
|
);
|
||||||
|
|
||||||
if (listenerIndex >= 0) {
|
if (listenerIndex >= 0) {
|
||||||
@@ -887,15 +894,10 @@ export abstract class EntitySystem<
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
protected requireComponent<T extends ComponentConstructor>(
|
protected requireComponent<T extends ComponentConstructor>(entity: Entity, componentType: T): ComponentInstance<T> {
|
||||||
entity: Entity,
|
|
||||||
componentType: T
|
|
||||||
): ComponentInstance<T> {
|
|
||||||
const component = entity.getComponent(componentType as any);
|
const component = entity.getComponent(componentType as any);
|
||||||
if (!component) {
|
if (!component) {
|
||||||
throw new Error(
|
throw new Error(`Component ${componentType.name} not found on entity ${entity.name} in ${this.systemName}`);
|
||||||
`Component ${componentType.name} not found on entity ${entity.name} in ${this.systemName}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return component as ComponentInstance<T>;
|
return component as ComponentInstance<T>;
|
||||||
}
|
}
|
||||||
@@ -927,9 +929,7 @@ export abstract class EntitySystem<
|
|||||||
entity: Entity,
|
entity: Entity,
|
||||||
...components: T
|
...components: T
|
||||||
): { [K in keyof T]: ComponentInstance<T[K]> } {
|
): { [K in keyof T]: ComponentInstance<T[K]> } {
|
||||||
return components.map((type) =>
|
return components.map((type) => this.requireComponent(entity, type)) as any;
|
||||||
this.requireComponent(entity, type)
|
|
||||||
) as any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -950,10 +950,7 @@ export abstract class EntitySystem<
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
protected forEach(
|
protected forEach(entities: readonly Entity[], processor: (entity: Entity, index: number) => void): void {
|
||||||
entities: readonly Entity[],
|
|
||||||
processor: (entity: Entity, index: number) => void
|
|
||||||
): void {
|
|
||||||
for (let i = 0; i < entities.length; i++) {
|
for (let i = 0; i < entities.length; i++) {
|
||||||
processor(entities[i]!, i);
|
processor(entities[i]!, i);
|
||||||
}
|
}
|
||||||
@@ -1000,10 +997,7 @@ export abstract class EntitySystem<
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
protected mapEntities<R>(
|
protected mapEntities<R>(entities: readonly Entity[], mapper: (entity: Entity, index: number) => R): R[] {
|
||||||
entities: readonly Entity[],
|
|
||||||
mapper: (entity: Entity, index: number) => R
|
|
||||||
): R[] {
|
|
||||||
return Array.from(entities).map(mapper);
|
return Array.from(entities).map(mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1052,10 +1046,7 @@ export abstract class EntitySystem<
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
protected someEntity(
|
protected someEntity(entities: readonly Entity[], predicate: (entity: Entity, index: number) => boolean): boolean {
|
||||||
entities: readonly Entity[],
|
|
||||||
predicate: (entity: Entity, index: number) => boolean
|
|
||||||
): boolean {
|
|
||||||
for (let i = 0; i < entities.length; i++) {
|
for (let i = 0; i < entities.length; i++) {
|
||||||
if (predicate(entities[i]!, i)) {
|
if (predicate(entities[i]!, i)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -1081,10 +1072,7 @@ export abstract class EntitySystem<
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
protected everyEntity(
|
protected everyEntity(entities: readonly Entity[], predicate: (entity: Entity, index: number) => boolean): boolean {
|
||||||
entities: readonly Entity[],
|
|
||||||
predicate: (entity: Entity, index: number) => boolean
|
|
||||||
): boolean {
|
|
||||||
for (let i = 0; i < entities.length; i++) {
|
for (let i = 0; i < entities.length; i++) {
|
||||||
if (!predicate(entities[i]!, i)) {
|
if (!predicate(entities[i]!, i)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export class EntityDataCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const archetypeData = this.collectArchetypeData(scene);
|
const archetypeData = this.collectArchetypeData(scene);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalEntities: stats.totalEntities,
|
totalEntities: stats.totalEntities,
|
||||||
activeEntities: stats.activeEntities,
|
activeEntities: stats.activeEntities,
|
||||||
@@ -52,7 +52,6 @@ export class EntityDataCollector {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取原始实体列表
|
* 获取原始实体列表
|
||||||
* @param scene 场景实例
|
* @param scene 场景实例
|
||||||
@@ -92,7 +91,6 @@ export class EntityDataCollector {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取实体详细信息
|
* 获取实体详细信息
|
||||||
* @param entityId 实体ID
|
* @param entityId 实体ID
|
||||||
@@ -108,9 +106,9 @@ export class EntityDataCollector {
|
|||||||
const entity = entityList.buffer.find((e: any) => e.id === entityId);
|
const entity = entityList.buffer.find((e: any) => e.id === entityId);
|
||||||
if (!entity) return null;
|
if (!entity) return null;
|
||||||
|
|
||||||
const baseDebugInfo = entity.getDebugInfo ?
|
const baseDebugInfo = entity.getDebugInfo
|
||||||
entity.getDebugInfo() :
|
? entity.getDebugInfo()
|
||||||
this.buildFallbackEntityInfo(entity, scene);
|
: this.buildFallbackEntityInfo(entity, scene);
|
||||||
|
|
||||||
const componentDetails = this.extractComponentDetails(entity.components);
|
const componentDetails = this.extractComponentDetails(entity.components);
|
||||||
|
|
||||||
@@ -140,7 +138,7 @@ export class EntityDataCollector {
|
|||||||
private getSceneInfo(scene: any): { name: string; type: string } {
|
private getSceneInfo(scene: any): { name: string; type: string } {
|
||||||
let sceneName = '当前场景';
|
let sceneName = '当前场景';
|
||||||
let sceneType = 'Scene';
|
let sceneType = 'Scene';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (scene.name && typeof scene.name === 'string' && scene.name.trim()) {
|
if (scene.name && typeof scene.name === 'string' && scene.name.trim()) {
|
||||||
sceneName = scene.name.trim();
|
sceneName = scene.name.trim();
|
||||||
@@ -159,11 +157,10 @@ export class EntityDataCollector {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
sceneName = '场景名获取失败';
|
sceneName = '场景名获取失败';
|
||||||
}
|
}
|
||||||
|
|
||||||
return { name: sceneName, type: sceneType };
|
return { name: sceneName, type: sceneType };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收集实体数据(包含内存信息)
|
* 收集实体数据(包含内存信息)
|
||||||
* @param scene 场景实例
|
* @param scene 场景实例
|
||||||
@@ -182,20 +179,20 @@ export class EntityDataCollector {
|
|||||||
try {
|
try {
|
||||||
stats = entityList.getStats ? entityList.getStats() : this.calculateFallbackEntityStats(entityList);
|
stats = entityList.getStats ? entityList.getStats() : this.calculateFallbackEntityStats(entityList);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
totalEntities: 0,
|
totalEntities: 0,
|
||||||
activeEntities: 0,
|
activeEntities: 0,
|
||||||
pendingAdd: 0,
|
pendingAdd: 0,
|
||||||
pendingRemove: 0,
|
pendingRemove: 0,
|
||||||
entitiesPerArchetype: [],
|
entitiesPerArchetype: [],
|
||||||
topEntitiesByComponents: [],
|
topEntitiesByComponents: [],
|
||||||
entityHierarchy: [],
|
entityHierarchy: [],
|
||||||
entityDetailsMap: {}
|
entityDetailsMap: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const archetypeData = this.collectArchetypeDataWithMemory(scene);
|
const archetypeData = this.collectArchetypeDataWithMemory(scene);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalEntities: stats.totalEntities,
|
totalEntities: stats.totalEntities,
|
||||||
activeEntities: stats.activeEntities,
|
activeEntities: stats.activeEntities,
|
||||||
@@ -208,7 +205,6 @@ export class EntityDataCollector {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private collectArchetypeData(scene: any): {
|
private collectArchetypeData(scene: any): {
|
||||||
distribution: Array<{ signature: string; count: number; memory: number }>;
|
distribution: Array<{ signature: string; count: number; memory: number }>;
|
||||||
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
||||||
@@ -224,7 +220,9 @@ export class EntityDataCollector {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private getArchetypeDistributionFast(entityContainer: any): Array<{ signature: string; count: number; memory: number }> {
|
private getArchetypeDistributionFast(
|
||||||
|
entityContainer: any
|
||||||
|
): Array<{ signature: string; count: number; memory: number }> {
|
||||||
const distribution = new Map<string, { count: number; componentTypes: string[] }>();
|
const distribution = new Map<string, { count: number; componentTypes: string[] }>();
|
||||||
|
|
||||||
if (entityContainer && entityContainer.entities) {
|
if (entityContainer && entityContainer.entities) {
|
||||||
@@ -251,7 +249,9 @@ export class EntityDataCollector {
|
|||||||
.slice(0, 20);
|
.slice(0, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTopEntitiesByComponentsFast(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> {
|
private getTopEntitiesByComponentsFast(
|
||||||
|
entityContainer: any
|
||||||
|
): Array<{ id: string; name: string; componentCount: number; memory: number }> {
|
||||||
if (!entityContainer || !entityContainer.entities) {
|
if (!entityContainer || !entityContainer.entities) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -266,7 +266,6 @@ export class EntityDataCollector {
|
|||||||
.sort((a: any, b: any) => b.componentCount - a.componentCount);
|
.sort((a: any, b: any) => b.componentCount - a.componentCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private collectArchetypeDataWithMemory(scene: any): {
|
private collectArchetypeDataWithMemory(scene: any): {
|
||||||
distribution: Array<{ signature: string; count: number; memory: number }>;
|
distribution: Array<{ signature: string; count: number; memory: number }>;
|
||||||
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
||||||
@@ -282,7 +281,6 @@ export class EntityDataCollector {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private extractArchetypeStatistics(archetypeSystem: any): {
|
private extractArchetypeStatistics(archetypeSystem: any): {
|
||||||
distribution: Array<{ signature: string; count: number; memory: number }>;
|
distribution: Array<{ signature: string; count: number; memory: number }>;
|
||||||
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
||||||
@@ -294,7 +292,7 @@ export class EntityDataCollector {
|
|||||||
archetypes.forEach((archetype: any) => {
|
archetypes.forEach((archetype: any) => {
|
||||||
const signature = archetype.componentTypes?.map((type: any) => type.name).join(',') || 'Unknown';
|
const signature = archetype.componentTypes?.map((type: any) => type.name).join(',') || 'Unknown';
|
||||||
const entityCount = archetype.entities?.length || 0;
|
const entityCount = archetype.entities?.length || 0;
|
||||||
|
|
||||||
distribution.push({
|
distribution.push({
|
||||||
signature,
|
signature,
|
||||||
count: entityCount,
|
count: entityCount,
|
||||||
@@ -319,7 +317,6 @@ export class EntityDataCollector {
|
|||||||
return { distribution, topEntities };
|
return { distribution, topEntities };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private extractArchetypeStatisticsWithMemory(archetypeSystem: any): {
|
private extractArchetypeStatisticsWithMemory(archetypeSystem: any): {
|
||||||
distribution: Array<{ signature: string; count: number; memory: number }>;
|
distribution: Array<{ signature: string; count: number; memory: number }>;
|
||||||
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
||||||
@@ -336,11 +333,11 @@ export class EntityDataCollector {
|
|||||||
if (archetype.entities && archetype.entities.length > 0) {
|
if (archetype.entities && archetype.entities.length > 0) {
|
||||||
const sampleSize = Math.min(5, archetype.entities.length);
|
const sampleSize = Math.min(5, archetype.entities.length);
|
||||||
let sampleMemory = 0;
|
let sampleMemory = 0;
|
||||||
|
|
||||||
for (let i = 0; i < sampleSize; i++) {
|
for (let i = 0; i < sampleSize; i++) {
|
||||||
sampleMemory += this.estimateEntityMemoryUsage(archetype.entities[i]);
|
sampleMemory += this.estimateEntityMemoryUsage(archetype.entities[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
actualMemory = (sampleMemory / sampleSize) * entityCount;
|
actualMemory = (sampleMemory / sampleSize) * entityCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,9 +365,9 @@ export class EntityDataCollector {
|
|||||||
return { distribution, topEntities };
|
return { distribution, topEntities };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getArchetypeDistributionWithMemory(
|
||||||
|
entityContainer: any
|
||||||
private getArchetypeDistributionWithMemory(entityContainer: any): Array<{ signature: string; count: number; memory: number }> {
|
): Array<{ signature: string; count: number; memory: number }> {
|
||||||
const distribution = new Map<string, { count: number; memory: number; componentTypes: string[] }>();
|
const distribution = new Map<string, { count: number; memory: number; componentTypes: string[] }>();
|
||||||
|
|
||||||
if (entityContainer && entityContainer.entities) {
|
if (entityContainer && entityContainer.entities) {
|
||||||
@@ -403,8 +400,9 @@ export class EntityDataCollector {
|
|||||||
.sort((a, b) => b.count - a.count);
|
.sort((a, b) => b.count - a.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getTopEntitiesByComponentsWithMemory(
|
||||||
private getTopEntitiesByComponentsWithMemory(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> {
|
entityContainer: any
|
||||||
|
): Array<{ id: string; name: string; componentCount: number; memory: number }> {
|
||||||
if (!entityContainer || !entityContainer.entities) {
|
if (!entityContainer || !entityContainer.entities) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -419,7 +417,6 @@ export class EntityDataCollector {
|
|||||||
.sort((a: any, b: any) => b.componentCount - a.componentCount);
|
.sort((a: any, b: any) => b.componentCount - a.componentCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private getEmptyEntityDebugData(): IEntityDebugData {
|
private getEmptyEntityDebugData(): IEntityDebugData {
|
||||||
return {
|
return {
|
||||||
totalEntities: 0,
|
totalEntities: 0,
|
||||||
@@ -433,20 +430,20 @@ export class EntityDataCollector {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private calculateFallbackEntityStats(entityList: any): any {
|
private calculateFallbackEntityStats(entityList: any): any {
|
||||||
const allEntities = entityList.buffer || [];
|
const allEntities = entityList.buffer || [];
|
||||||
const activeEntities = allEntities.filter((entity: any) =>
|
const activeEntities = allEntities.filter((entity: any) => entity.enabled && !entity.isDestroyed);
|
||||||
entity.enabled && !entity._isDestroyed
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalEntities: allEntities.length,
|
totalEntities: allEntities.length,
|
||||||
activeEntities: activeEntities.length,
|
activeEntities: activeEntities.length,
|
||||||
pendingAdd: 0,
|
pendingAdd: 0,
|
||||||
pendingRemove: 0,
|
pendingRemove: 0,
|
||||||
averageComponentsPerEntity: activeEntities.length > 0 ?
|
averageComponentsPerEntity:
|
||||||
allEntities.reduce((sum: number, e: any) => sum + (e.components?.length || 0), 0) / activeEntities.length : 0
|
activeEntities.length > 0
|
||||||
|
? allEntities.reduce((sum: number, e: any) => sum + (e.components?.length || 0), 0) /
|
||||||
|
activeEntities.length
|
||||||
|
: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,37 +473,40 @@ export class EntityDataCollector {
|
|||||||
|
|
||||||
public calculateObjectSize(obj: any, excludeKeys: string[] = []): number {
|
public calculateObjectSize(obj: any, excludeKeys: string[] = []): number {
|
||||||
if (!obj || typeof obj !== 'object') return 0;
|
if (!obj || typeof obj !== 'object') return 0;
|
||||||
|
|
||||||
const visited = new WeakSet();
|
const visited = new WeakSet();
|
||||||
const maxDepth = 2;
|
const maxDepth = 2;
|
||||||
|
|
||||||
const calculate = (item: any, depth: number = 0): number => {
|
const calculate = (item: any, depth: number = 0): number => {
|
||||||
if (!item || typeof item !== 'object' || depth >= maxDepth) {
|
if (!item || typeof item !== 'object' || depth >= maxDepth) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visited.has(item)) return 0;
|
if (visited.has(item)) return 0;
|
||||||
visited.add(item);
|
visited.add(item);
|
||||||
|
|
||||||
let itemSize = 32;
|
let itemSize = 32;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const keys = Object.keys(item);
|
const keys = Object.keys(item);
|
||||||
const maxKeys = Math.min(keys.length, 20);
|
const maxKeys = Math.min(keys.length, 20);
|
||||||
|
|
||||||
for (let i = 0; i < maxKeys; i++) {
|
for (let i = 0; i < maxKeys; i++) {
|
||||||
const key = keys[i];
|
const key = keys[i];
|
||||||
if (!key || excludeKeys.includes(key) ||
|
if (
|
||||||
|
!key ||
|
||||||
|
excludeKeys.includes(key) ||
|
||||||
key === 'constructor' ||
|
key === 'constructor' ||
|
||||||
key === '__proto__' ||
|
key === '__proto__' ||
|
||||||
key.startsWith('_cc_') ||
|
key.startsWith('_cc_') ||
|
||||||
key.startsWith('__')) {
|
key.startsWith('__')
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = item[key];
|
const value = item[key];
|
||||||
itemSize += key.length * 2;
|
itemSize += key.length * 2;
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
itemSize += Math.min(value.length * 2, 200);
|
itemSize += Math.min(value.length * 2, 200);
|
||||||
} else if (typeof value === 'number') {
|
} else if (typeof value === 'number') {
|
||||||
@@ -522,10 +522,10 @@ export class EntityDataCollector {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return 64;
|
return 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
return itemSize;
|
return itemSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const size = calculate(obj);
|
const size = calculate(obj);
|
||||||
return Math.max(size, 32);
|
return Math.max(size, 32);
|
||||||
@@ -534,7 +534,6 @@ export class EntityDataCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private buildEntityHierarchyTree(entityList: { buffer?: Entity[] }): Array<{
|
private buildEntityHierarchyTree(entityList: { buffer?: Entity[] }): Array<{
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -553,7 +552,6 @@ export class EntityDataCollector {
|
|||||||
|
|
||||||
const rootEntities: any[] = [];
|
const rootEntities: any[] = [];
|
||||||
|
|
||||||
|
|
||||||
entityList.buffer.forEach((entity: Entity) => {
|
entityList.buffer.forEach((entity: Entity) => {
|
||||||
if (!entity.parent) {
|
if (!entity.parent) {
|
||||||
const hierarchyNode = this.buildEntityHierarchyNode(entity);
|
const hierarchyNode = this.buildEntityHierarchyNode(entity);
|
||||||
@@ -626,12 +624,13 @@ export class EntityDataCollector {
|
|||||||
const batch = entities.slice(i, i + batchSize);
|
const batch = entities.slice(i, i + batchSize);
|
||||||
|
|
||||||
batch.forEach((entity: Entity) => {
|
batch.forEach((entity: Entity) => {
|
||||||
const baseDebugInfo = entity.getDebugInfo ?
|
const baseDebugInfo = entity.getDebugInfo
|
||||||
entity.getDebugInfo() :
|
? entity.getDebugInfo()
|
||||||
this.buildFallbackEntityInfo(entity, scene);
|
: this.buildFallbackEntityInfo(entity, scene);
|
||||||
|
|
||||||
const componentCacheStats = (entity as any).getComponentCacheStats ?
|
const componentCacheStats = (entity as any).getComponentCacheStats
|
||||||
(entity as any).getComponentCacheStats() : null;
|
? (entity as any).getComponentCacheStats()
|
||||||
|
: null;
|
||||||
|
|
||||||
const componentDetails = this.extractComponentDetails(entity.components);
|
const componentDetails = this.extractComponentDetails(entity.components);
|
||||||
|
|
||||||
@@ -639,13 +638,14 @@ export class EntityDataCollector {
|
|||||||
...baseDebugInfo,
|
...baseDebugInfo,
|
||||||
parentName: entity.parent?.name || null,
|
parentName: entity.parent?.name || null,
|
||||||
components: componentDetails,
|
components: componentDetails,
|
||||||
componentTypes: baseDebugInfo.componentTypes ||
|
componentTypes: baseDebugInfo.componentTypes || componentDetails.map((comp) => comp.typeName),
|
||||||
componentDetails.map((comp) => comp.typeName),
|
cachePerformance: componentCacheStats
|
||||||
cachePerformance: componentCacheStats ? {
|
? {
|
||||||
hitRate: componentCacheStats.cacheStats.hitRate,
|
hitRate: componentCacheStats.cacheStats.hitRate,
|
||||||
size: componentCacheStats.cacheStats.size,
|
size: componentCacheStats.cacheStats.size,
|
||||||
maxSize: componentCacheStats.cacheStats.maxSize
|
maxSize: componentCacheStats.cacheStats.maxSize
|
||||||
} : null
|
}
|
||||||
|
: null
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -658,7 +658,7 @@ export class EntityDataCollector {
|
|||||||
*/
|
*/
|
||||||
private buildFallbackEntityInfo(entity: Entity, scene?: IScene | null): any {
|
private buildFallbackEntityInfo(entity: Entity, scene?: IScene | null): any {
|
||||||
const sceneInfo = this.getSceneInfo(scene);
|
const sceneInfo = this.getSceneInfo(scene);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: entity.name || `Entity_${entity.id}`,
|
name: entity.name || `Entity_${entity.id}`,
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
@@ -691,10 +691,10 @@ export class EntityDataCollector {
|
|||||||
return components.map((component: Component) => {
|
return components.map((component: Component) => {
|
||||||
const typeName = getComponentInstanceTypeName(component);
|
const typeName = getComponentInstanceTypeName(component);
|
||||||
const properties: Record<string, any> = {};
|
const properties: Record<string, any> = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const propertyKeys = Object.keys(component);
|
const propertyKeys = Object.keys(component);
|
||||||
propertyKeys.forEach(propertyKey => {
|
propertyKeys.forEach((propertyKey) => {
|
||||||
if (!propertyKey.startsWith('_') && propertyKey !== 'entity' && propertyKey !== 'constructor') {
|
if (!propertyKey.startsWith('_') && propertyKey !== 'entity' && propertyKey !== 'constructor') {
|
||||||
const propertyValue = (component as any)[propertyKey];
|
const propertyValue = (component as any)[propertyKey];
|
||||||
if (propertyValue !== undefined && propertyValue !== null) {
|
if (propertyValue !== undefined && propertyValue !== null) {
|
||||||
@@ -702,7 +702,7 @@ export class EntityDataCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 如果没有找到任何属性,添加一些调试信息
|
// 如果没有找到任何属性,添加一些调试信息
|
||||||
if (Object.keys(properties).length === 0) {
|
if (Object.keys(properties).length === 0) {
|
||||||
properties['_info'] = '该组件没有公开属性';
|
properties['_info'] = '该组件没有公开属性';
|
||||||
@@ -712,7 +712,7 @@ export class EntityDataCollector {
|
|||||||
properties['_error'] = '属性提取失败';
|
properties['_error'] = '属性提取失败';
|
||||||
properties['_componentId'] = getComponentInstanceTypeName(component);
|
properties['_componentId'] = getComponentInstanceTypeName(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
typeName: typeName,
|
typeName: typeName,
|
||||||
properties: properties
|
properties: properties
|
||||||
@@ -726,7 +726,11 @@ export class EntityDataCollector {
|
|||||||
* @param componentIndex 组件索引
|
* @param componentIndex 组件索引
|
||||||
* @param scene 场景实例
|
* @param scene 场景实例
|
||||||
*/
|
*/
|
||||||
public getComponentProperties(entityId: number, componentIndex: number, scene?: IScene | null): Record<string, any> {
|
public getComponentProperties(
|
||||||
|
entityId: number,
|
||||||
|
componentIndex: number,
|
||||||
|
scene?: IScene | null
|
||||||
|
): Record<string, any> {
|
||||||
try {
|
try {
|
||||||
if (!scene) return {};
|
if (!scene) return {};
|
||||||
|
|
||||||
@@ -739,20 +743,20 @@ export class EntityDataCollector {
|
|||||||
const component = entity.components[componentIndex];
|
const component = entity.components[componentIndex];
|
||||||
const properties: Record<string, any> = {};
|
const properties: Record<string, any> = {};
|
||||||
|
|
||||||
const propertyKeys = Object.keys(component);
|
const propertyKeys = Object.keys(component);
|
||||||
propertyKeys.forEach(propertyKey => {
|
propertyKeys.forEach((propertyKey) => {
|
||||||
if (!propertyKey.startsWith('_') && propertyKey !== 'entity') {
|
if (!propertyKey.startsWith('_') && propertyKey !== 'entity') {
|
||||||
const propertyValue = (component as any)[propertyKey];
|
const propertyValue = (component as any)[propertyKey];
|
||||||
if (propertyValue !== undefined && propertyValue !== null) {
|
if (propertyValue !== undefined && propertyValue !== null) {
|
||||||
properties[propertyKey] = this.formatPropertyValue(propertyValue);
|
properties[propertyKey] = this.formatPropertyValue(propertyValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return properties;
|
return properties;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { _error: '属性提取失败' };
|
return { _error: '属性提取失败' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -786,7 +790,7 @@ export class EntityDataCollector {
|
|||||||
if (obj.length === 0) return [];
|
if (obj.length === 0) return [];
|
||||||
|
|
||||||
if (obj.length > 10) {
|
if (obj.length > 10) {
|
||||||
const sample = obj.slice(0, 3).map(item => this.formatPropertyValue(item, 1));
|
const sample = obj.slice(0, 3).map((item) => this.formatPropertyValue(item, 1));
|
||||||
return {
|
return {
|
||||||
_isLazyArray: true,
|
_isLazyArray: true,
|
||||||
_arrayLength: obj.length,
|
_arrayLength: obj.length,
|
||||||
@@ -795,7 +799,7 @@ export class EntityDataCollector {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj.map(item => this.formatPropertyValue(item, 1));
|
return obj.map((item) => this.formatPropertyValue(item, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = Object.keys(obj);
|
const keys = Object.keys(obj);
|
||||||
@@ -842,8 +846,8 @@ export class EntityDataCollector {
|
|||||||
try {
|
try {
|
||||||
const typeName = obj.constructor?.name || 'Object';
|
const typeName = obj.constructor?.name || 'Object';
|
||||||
const summary = this.getObjectSummary(obj, typeName);
|
const summary = this.getObjectSummary(obj, typeName);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_isLazyObject: true,
|
_isLazyObject: true,
|
||||||
_typeName: typeName,
|
_typeName: typeName,
|
||||||
_summary: summary,
|
_summary: summary,
|
||||||
@@ -922,7 +926,12 @@ export class EntityDataCollector {
|
|||||||
* @param propertyPath 属性路径
|
* @param propertyPath 属性路径
|
||||||
* @param scene 场景实例
|
* @param scene 场景实例
|
||||||
*/
|
*/
|
||||||
public expandLazyObject(entityId: number, componentIndex: number, propertyPath: string, scene?: IScene | null): any {
|
public expandLazyObject(
|
||||||
|
entityId: number,
|
||||||
|
componentIndex: number,
|
||||||
|
propertyPath: string,
|
||||||
|
scene?: IScene | null
|
||||||
|
): any {
|
||||||
try {
|
try {
|
||||||
if (!scene) return null;
|
if (!scene) return null;
|
||||||
|
|
||||||
@@ -983,4 +992,4 @@ export class EntityDataCollector {
|
|||||||
|
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ describe('Component - 组件基类测试', () => {
|
|||||||
let scene: Scene;
|
let scene: Scene;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
Component._idGenerator = 0;
|
|
||||||
component = new TestComponent();
|
component = new TestComponent();
|
||||||
scene = new Scene();
|
scene = new Scene();
|
||||||
entity = scene.createEntity('TestEntity');
|
entity = scene.createEntity('TestEntity');
|
||||||
@@ -51,12 +50,11 @@ describe('Component - 组件基类测试', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('组件ID应该递增分配', () => {
|
test('组件ID应该递增分配', () => {
|
||||||
const startId = Component._idGenerator;
|
|
||||||
const component1 = new TestComponent();
|
const component1 = new TestComponent();
|
||||||
const component2 = new TestComponent();
|
const component2 = new TestComponent();
|
||||||
|
|
||||||
expect(component2.id).toBe(component1.id + 1);
|
expect(component2.id).toBe(component1.id + 1);
|
||||||
expect(component1.id).toBeGreaterThanOrEqual(startId);
|
expect(component1.id).toBeGreaterThanOrEqual(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user