移除过时类并标记组件和实体的update为过时方法
This commit is contained in:
@@ -64,12 +64,7 @@ function generatePackageJson() {
|
||||
'index.umd.js.map',
|
||||
'index.es5.js',
|
||||
'index.es5.js.map',
|
||||
'index.d.ts',
|
||||
'README.md',
|
||||
'LICENSE',
|
||||
'SECURITY.md',
|
||||
'COCOS_USAGE.md',
|
||||
'.npmignore'
|
||||
'index.d.ts'
|
||||
],
|
||||
keywords: [
|
||||
'ecs',
|
||||
@@ -96,11 +91,7 @@ function generatePackageJson() {
|
||||
|
||||
function copyFiles() {
|
||||
const filesToCopy = [
|
||||
{ src: './README.md', dest: './dist/README.md' },
|
||||
{ src: './LICENSE', dest: './dist/LICENSE' },
|
||||
{ src: './SECURITY.md', dest: './dist/SECURITY.md' },
|
||||
{ src: './COCOS_USAGE.md', dest: './dist/COCOS_USAGE.md' },
|
||||
{ src: './.npmignore', dest: './dist/.npmignore' }
|
||||
// 移除不存在的文件以避免警告
|
||||
];
|
||||
|
||||
filesToCopy.forEach(({ src, dest }) => {
|
||||
@@ -111,6 +102,10 @@ function copyFiles() {
|
||||
console.log(` ⚠️ 文件不存在: ${src}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (filesToCopy.length === 0) {
|
||||
console.log(' ℹ️ 没有需要复制的文件');
|
||||
}
|
||||
}
|
||||
|
||||
function showBuildResults() {
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"bin/**/*",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
"bin/**/*"
|
||||
],
|
||||
"keywords": [
|
||||
"ecs",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { IComponent } from '../Types';
|
||||
import type { Entity } from './Entity';
|
||||
|
||||
/**
|
||||
* 游戏组件基类
|
||||
@@ -146,14 +147,10 @@ export abstract class Component implements IComponent {
|
||||
|
||||
/**
|
||||
* 更新组件
|
||||
*
|
||||
* 每帧调用,用于更新组件的逻辑。
|
||||
* 子类应该重写此方法来实现具体的更新逻辑。
|
||||
*
|
||||
* @deprecated 不符合ECS架构规范,建议使用EntitySystem来处理更新逻辑
|
||||
*/
|
||||
public update(): void {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 避免循环引用,在文件末尾导入Entity
|
||||
import type { Entity } from './Entity';
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 可更新接口
|
||||
* 当添加到组件时,只要组件和实体被启用,就会在每帧调用update方法
|
||||
* @deprecated 不符合ECS架构规范,建议使用EntitySystem来处理更新逻辑而非在组件中实现
|
||||
*/
|
||||
export interface IUpdatable {
|
||||
enabled: boolean;
|
||||
|
||||
@@ -49,6 +49,7 @@ export class SceneComponent {
|
||||
|
||||
/**
|
||||
* 每帧更新
|
||||
* @deprecated 不符合ECS架构规范,建议使用EntitySystem来处理场景级更新逻辑
|
||||
*/
|
||||
public update(): void {
|
||||
}
|
||||
|
||||
@@ -1,759 +0,0 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from './ComponentStorage';
|
||||
import { IdentifierPool } from '../Utils/IdentifierPool';
|
||||
import { ComponentIndexManager } from './ComponentIndex';
|
||||
import { ArchetypeSystem } from './ArchetypeSystem';
|
||||
import { DirtyTrackingSystem, DirtyFlag } from './DirtyTrackingSystem';
|
||||
import { EventBus } from './EventBus';
|
||||
|
||||
/**
|
||||
* 实体查询构建器
|
||||
*
|
||||
* 提供流式API来构建复杂的实体查询条件。支持组件过滤、标签过滤、状态过滤和自定义条件。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const results = entityManager.query()
|
||||
* .withAll(PositionComponent, HealthComponent)
|
||||
* .without(VelocityComponent)
|
||||
* .withTag(1)
|
||||
* .active()
|
||||
* .where(entity => entity.name.startsWith("Player"))
|
||||
* .execute();
|
||||
* ```
|
||||
*/
|
||||
export class EntityQueryBuilder {
|
||||
/** 必须包含的组件类型 */
|
||||
private _allComponents: ComponentType[] = [];
|
||||
/** 至少包含一个的组件类型 */
|
||||
private _anyComponents: ComponentType[] = [];
|
||||
/** 不能包含的组件类型 */
|
||||
private _withoutComponents: ComponentType[] = [];
|
||||
/** 必须包含的标签 */
|
||||
private _withTags: number[] = [];
|
||||
/** 不能包含的标签 */
|
||||
private _withoutTags: number[] = [];
|
||||
/** 是否只查询激活状态的实体 */
|
||||
private _activeOnly: boolean = false;
|
||||
/** 是否只查询启用状态的实体 */
|
||||
private _enabledOnly: boolean = false;
|
||||
/** 自定义过滤条件 */
|
||||
private _customPredicates: Array<(entity: Entity) => boolean> = [];
|
||||
|
||||
/**
|
||||
* 创建查询构建器实例
|
||||
* @param entityManager 实体管理器实例
|
||||
*/
|
||||
constructor(private entityManager: EntityManager) {}
|
||||
|
||||
/**
|
||||
* 添加必须包含的组件条件
|
||||
*
|
||||
* 返回的实体必须包含所有指定的组件类型。
|
||||
*
|
||||
* @param componentTypes 组件类型列表
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public withAll(...componentTypes: ComponentType[]): EntityQueryBuilder {
|
||||
this._allComponents.push(...componentTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加至少包含一个的组件条件
|
||||
*
|
||||
* 返回的实体必须至少包含其中一个指定的组件类型。
|
||||
*
|
||||
* @param componentTypes 组件类型列表
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public withAny(...componentTypes: ComponentType[]): EntityQueryBuilder {
|
||||
this._anyComponents.push(...componentTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加不能包含的组件条件
|
||||
*
|
||||
* 返回的实体不能包含任何指定的组件类型。
|
||||
*
|
||||
* @param componentTypes 组件类型列表
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public without(...componentTypes: ComponentType[]): EntityQueryBuilder {
|
||||
this._withoutComponents.push(...componentTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加必须包含的标签条件
|
||||
*
|
||||
* 返回的实体必须具有指定的标签。
|
||||
*
|
||||
* @param tag 标签值
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public withTag(tag: number): EntityQueryBuilder {
|
||||
this._withTags.push(tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加不能包含的标签条件
|
||||
*
|
||||
* 返回的实体不能具有指定的标签。
|
||||
*
|
||||
* @param tag 标签值
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public withoutTag(tag: number): EntityQueryBuilder {
|
||||
this._withoutTags.push(tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加激活状态过滤条件
|
||||
*
|
||||
* 返回的实体必须处于激活状态(active = true)。
|
||||
*
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public active(): EntityQueryBuilder {
|
||||
this._activeOnly = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加启用状态过滤条件
|
||||
*
|
||||
* 返回的实体必须处于启用状态(enabled = true)。
|
||||
*
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public enabled(): EntityQueryBuilder {
|
||||
this._enabledOnly = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自定义过滤条件
|
||||
*
|
||||
* 允许用户定义复杂的过滤逻辑。
|
||||
*
|
||||
* @param predicate 自定义过滤函数,接收实体作为参数,返回布尔值
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* .where(entity => entity.name.startsWith("Player"))
|
||||
* .where(entity => entity.components.length > 5)
|
||||
* ```
|
||||
*/
|
||||
public where(predicate: (entity: Entity) => boolean): EntityQueryBuilder {
|
||||
this._customPredicates.push(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询并返回所有匹配的实体
|
||||
*
|
||||
* @returns 符合所有查询条件的实体数组
|
||||
*/
|
||||
public execute(): Entity[] {
|
||||
let candidates: Entity[] = [];
|
||||
|
||||
if (this._allComponents.length > 0) {
|
||||
const indexResult = this.entityManager.queryWithComponentIndex(this._allComponents, 'AND');
|
||||
candidates = Array.from(indexResult);
|
||||
} else if (this._anyComponents.length > 0) {
|
||||
const indexResult = this.entityManager.queryWithComponentIndex(this._anyComponents, 'OR');
|
||||
candidates = Array.from(indexResult);
|
||||
} else {
|
||||
candidates = this.entityManager.getAllEntities();
|
||||
}
|
||||
|
||||
return candidates.filter(entity => this.matchesEntity(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询并返回第一个匹配的实体
|
||||
*
|
||||
* @returns 第一个符合查询条件的实体,如果没有找到则返回null
|
||||
*/
|
||||
public first(): Entity | null {
|
||||
const entities = this.entityManager.getAllEntities();
|
||||
return entities.find(entity => this.matchesEntity(entity)) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询并返回匹配实体的数量
|
||||
*
|
||||
* @returns 符合查询条件的实体数量
|
||||
*/
|
||||
public count(): number {
|
||||
const entities = this.entityManager.getAllEntities();
|
||||
return entities.filter(entity => this.matchesEntity(entity)).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对所有匹配的实体执行指定操作
|
||||
*
|
||||
* @param action 要执行的操作函数,接收匹配的实体作为参数
|
||||
*/
|
||||
public forEach(action: (entity: Entity) => void): void {
|
||||
const entities = this.entityManager.getAllEntities();
|
||||
entities.forEach(entity => {
|
||||
if (this.matchesEntity(entity)) {
|
||||
action(entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否匹配所有查询条件
|
||||
*
|
||||
* 按优先级顺序检查各种过滤条件,一旦发现不匹配立即返回false。
|
||||
*
|
||||
* @param entity 要检查的实体
|
||||
* @returns 实体是否匹配所有查询条件
|
||||
*/
|
||||
private matchesEntity(entity: Entity): boolean {
|
||||
// 检查激活状态
|
||||
if (this._activeOnly && !entity.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查启用状态
|
||||
if (this._enabledOnly && !entity.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查必须包含的组件
|
||||
if (this._allComponents.length > 0) {
|
||||
for (const componentType of this._allComponents) {
|
||||
if (!entity.hasComponent(componentType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查至少包含一个的组件
|
||||
if (this._anyComponents.length > 0) {
|
||||
let hasAny = false;
|
||||
for (const componentType of this._anyComponents) {
|
||||
if (entity.hasComponent(componentType)) {
|
||||
hasAny = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasAny) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查不能包含的组件
|
||||
if (this._withoutComponents.length > 0) {
|
||||
for (const componentType of this._withoutComponents) {
|
||||
if (entity.hasComponent(componentType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查必须包含的标签
|
||||
if (this._withTags.length > 0) {
|
||||
if (!this._withTags.includes(entity.tag)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查不能包含的标签
|
||||
if (this._withoutTags.length > 0) {
|
||||
if (this._withoutTags.includes(entity.tag)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查自定义条件
|
||||
if (this._customPredicates.length > 0) {
|
||||
for (const predicate of this._customPredicates) {
|
||||
if (!predicate(entity)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体管理器
|
||||
*
|
||||
* 提供统一的实体管理和查询机制,支持高效的实体操作。
|
||||
* 包括实体的创建、销毁、查询和索引管理功能。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const entityManager = new EntityManager();
|
||||
*
|
||||
* // 创建实体
|
||||
* const player = entityManager.createEntity("Player");
|
||||
*
|
||||
* // 查询实体
|
||||
* const playerEntity = entityManager.getEntityByName("Player");
|
||||
*
|
||||
* // 复杂查询
|
||||
* const results = entityManager.query()
|
||||
* .withAll(HealthComponent, PositionComponent)
|
||||
* .active()
|
||||
* .execute();
|
||||
* ```
|
||||
*/
|
||||
export class EntityManager {
|
||||
/** 主要实体存储,使用ID作为键 */
|
||||
private _entities: Map<number, Entity> = new Map();
|
||||
/** 按名称索引的实体映射 */
|
||||
private _entitiesByName: Map<string, Entity[]> = new Map();
|
||||
/** 按标签索引的实体映射 */
|
||||
private _entitiesByTag: Map<number, Entity[]> = new Map();
|
||||
/** 实体ID分配器 */
|
||||
private _identifierPool: IdentifierPool;
|
||||
/** 已销毁实体的ID集合 */
|
||||
private _destroyedEntities: Set<number> = new Set();
|
||||
|
||||
/** 性能优化系统 */
|
||||
private _componentIndexManager: ComponentIndexManager;
|
||||
private _archetypeSystem: ArchetypeSystem;
|
||||
private _dirtyTrackingSystem: DirtyTrackingSystem;
|
||||
/** 事件总线 */
|
||||
private _eventBus: EventBus;
|
||||
|
||||
/**
|
||||
* 创建实体管理器实例
|
||||
*
|
||||
* 初始化内部数据结构和ID分配器。
|
||||
*/
|
||||
constructor() {
|
||||
this._identifierPool = new IdentifierPool();
|
||||
|
||||
// 初始化性能优化系统
|
||||
this._componentIndexManager = new ComponentIndexManager();
|
||||
this._archetypeSystem = new ArchetypeSystem();
|
||||
this._dirtyTrackingSystem = new DirtyTrackingSystem();
|
||||
this._eventBus = new EventBus(false);
|
||||
|
||||
// 设置Entity的静态事件总线引用
|
||||
Entity.eventBus = this._eventBus;
|
||||
|
||||
// 监听组件事件来同步更新索引
|
||||
this._eventBus.on('component:added', (data: any) => {
|
||||
const entity = this._entities.get(data.entityId);
|
||||
if (entity) {
|
||||
this._componentIndexManager.addEntity(entity);
|
||||
this._archetypeSystem.addEntity(entity);
|
||||
}
|
||||
});
|
||||
|
||||
this._eventBus.on('component:removed', (data: any) => {
|
||||
const entity = this._entities.get(data.entityId);
|
||||
if (entity) {
|
||||
this._componentIndexManager.removeEntity(entity);
|
||||
this._archetypeSystem.removeEntity(entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体总数
|
||||
*
|
||||
* @returns 当前管理的实体总数量
|
||||
*/
|
||||
public get entityCount(): number {
|
||||
return this._entities.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取激活状态的实体数量
|
||||
*
|
||||
* 只计算同时满足激活状态且未被销毁的实体。
|
||||
*
|
||||
* @returns 激活状态的实体数量
|
||||
*/
|
||||
public get activeEntityCount(): number {
|
||||
let count = 0;
|
||||
for (const entity of this._entities.values()) {
|
||||
if (entity.active && !entity.isDestroyed) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新实体
|
||||
*
|
||||
* 分配唯一ID并将实体添加到管理系统中,同时更新相关索引。
|
||||
*
|
||||
* @param name 实体名称,如果未指定则使用时间戳生成默认名称
|
||||
* @returns 创建的实体实例
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const player = entityManager.createEntity("Player");
|
||||
* const enemy = entityManager.createEntity(); // 使用默认名称
|
||||
* ```
|
||||
*/
|
||||
public createEntity(name?: string): Entity {
|
||||
const id = this._identifierPool.checkOut();
|
||||
if (!name) {
|
||||
name = `Entity_${id}`;
|
||||
}
|
||||
const entity = new Entity(name, id);
|
||||
|
||||
this._entities.set(id, entity);
|
||||
this.updateNameIndex(entity, true);
|
||||
this.updateTagIndex(entity, true);
|
||||
|
||||
if (entity.components.length > 0) {
|
||||
this._componentIndexManager.addEntity(entity);
|
||||
this._archetypeSystem.addEntity(entity);
|
||||
this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED);
|
||||
}
|
||||
|
||||
// 发射实体创建事件
|
||||
this._eventBus.emitEntityCreated({
|
||||
timestamp: Date.now(),
|
||||
source: 'EntityManager',
|
||||
entityId: entity.id,
|
||||
entityName: entity.name,
|
||||
entityTag: entity.tag?.toString()
|
||||
});
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建实体
|
||||
*
|
||||
* 为了优化大量实体创建的性能,批量处理索引更新和事件发射。
|
||||
* 适用于需要创建大量实体的场景,如子弹、粒子等。
|
||||
*
|
||||
* @param count 要创建的实体数量
|
||||
* @param namePrefix 实体名称前缀,默认为 Entity
|
||||
* @param skipEvents 是否跳过事件发射以提升性能,默认为 false
|
||||
* @returns 创建的实体数组
|
||||
*
|
||||
* @example
|
||||
* const bullets = entityManager.createEntitiesBatch(100, "Bullet", true);
|
||||
* const particles = entityManager.createEntitiesBatch(500, "Particle");
|
||||
*/
|
||||
public createEntitiesBatch(
|
||||
count: number,
|
||||
namePrefix: string = "Entity",
|
||||
skipEvents: boolean = false
|
||||
): Entity[] {
|
||||
if (count <= 0) return [];
|
||||
|
||||
const entities: Entity[] = [];
|
||||
|
||||
// 批量分配ID和创建Entity对象
|
||||
for (let i = 0; i < count; i++) {
|
||||
const id = this._identifierPool.checkOut();
|
||||
const name = `${namePrefix}_${id}`;
|
||||
const entity = new Entity(name, id);
|
||||
|
||||
entities.push(entity);
|
||||
this._entities.set(id, entity);
|
||||
}
|
||||
|
||||
// 批量更新索引
|
||||
for (const entity of entities) {
|
||||
this.updateNameIndex(entity, true);
|
||||
this.updateTagIndex(entity, true);
|
||||
|
||||
if (entity.components.length > 0) {
|
||||
this._componentIndexManager.addEntity(entity);
|
||||
this._archetypeSystem.addEntity(entity);
|
||||
this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量发射事件
|
||||
if (!skipEvents) {
|
||||
const timestamp = Date.now();
|
||||
for (const entity of entities) {
|
||||
this._eventBus.emitEntityCreated({
|
||||
timestamp,
|
||||
source: 'EntityManager',
|
||||
entityId: entity.id,
|
||||
entityName: entity.name,
|
||||
entityTag: entity.tag?.toString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实体
|
||||
*
|
||||
* 支持通过实体对象、名称或ID来销毁实体。
|
||||
* 会清理所有相关索引并回收ID。
|
||||
*
|
||||
* @param entityOrId 要销毁的实体,可以是实体对象、名称字符串或ID数字
|
||||
* @returns 是否成功销毁实体
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 通过实体对象销毁
|
||||
* entityManager.destroyEntity(player);
|
||||
*
|
||||
* // 通过名称销毁
|
||||
* entityManager.destroyEntity("Enemy_1");
|
||||
*
|
||||
* // 通过ID销毁
|
||||
* entityManager.destroyEntity(123);
|
||||
* ```
|
||||
*/
|
||||
public destroyEntity(entityOrId: Entity | string | number): boolean {
|
||||
let entity: Entity | null = null;
|
||||
|
||||
if (typeof entityOrId === 'string') {
|
||||
entity = this.getEntityByName(entityOrId);
|
||||
} else if (typeof entityOrId === 'number') {
|
||||
entity = this._entities.get(entityOrId) || null;
|
||||
} else {
|
||||
entity = this._entities.get(entityOrId.id) || null;
|
||||
}
|
||||
|
||||
if (!entity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._destroyedEntities.add(entity.id);
|
||||
this.updateNameIndex(entity, false);
|
||||
this.updateTagIndex(entity, false);
|
||||
|
||||
this._componentIndexManager.removeEntity(entity);
|
||||
this._archetypeSystem.removeEntity(entity);
|
||||
this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_REMOVED);
|
||||
|
||||
// 发射实体销毁事件
|
||||
this._eventBus.emitEntityDestroyed({
|
||||
timestamp: Date.now(),
|
||||
source: 'EntityManager',
|
||||
entityId: entity.id,
|
||||
entityName: entity.name,
|
||||
entityTag: entity.tag?.toString()
|
||||
});
|
||||
|
||||
entity.destroy();
|
||||
this._entities.delete(entity.id);
|
||||
this._identifierPool.checkIn(entity.id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有实体
|
||||
*
|
||||
* 返回当前管理的所有实体的副本数组。
|
||||
*
|
||||
* @returns 所有实体的数组
|
||||
*/
|
||||
public getAllEntities(): Entity[] {
|
||||
return Array.from(this._entities.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取实体
|
||||
*
|
||||
* 支持字符串和数字类型的ID。
|
||||
*
|
||||
* @param id 实体ID,可以是字符串或数字
|
||||
* @returns 对应的实体,如果不存在则返回null
|
||||
*/
|
||||
public getEntity(id: string | number): Entity | null {
|
||||
const numId = typeof id === 'string' ? parseInt(id) : id;
|
||||
return this._entities.get(numId) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称获取实体
|
||||
*
|
||||
* 如果存在多个同名实体,返回第一个找到的实体。
|
||||
*
|
||||
* @param name 实体名称
|
||||
* @returns 匹配的实体,如果不存在则返回null
|
||||
*/
|
||||
public getEntityByName(name: string): Entity | null {
|
||||
const entities = this._entitiesByName.get(name);
|
||||
return entities && entities.length > 0 ? entities[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签获取实体列表
|
||||
*
|
||||
* 返回所有具有指定标签的实体。
|
||||
*
|
||||
* @param tag 标签值
|
||||
* @returns 具有指定标签的实体数组
|
||||
*/
|
||||
public getEntitiesByTag(tag: number): Entity[] {
|
||||
return Array.from(this._entities.values())
|
||||
.filter(entity => entity.tag === tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取包含指定组件的所有实体
|
||||
*
|
||||
* 遍历所有实体,查找包含指定组件类型的实体。
|
||||
*
|
||||
* @param componentType 组件类型
|
||||
* @returns 包含指定组件的实体数组
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const entitiesWithHealth = entityManager.getEntitiesWithComponent(HealthComponent);
|
||||
* ```
|
||||
*/
|
||||
public getEntitiesWithComponent<T extends Component>(componentType: ComponentType<T>): Entity[] {
|
||||
const indexResult = this._componentIndexManager.query(componentType);
|
||||
return Array.from(indexResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建查询构建器
|
||||
*
|
||||
* 返回一个新的查询构建器实例,用于构建复杂的实体查询。
|
||||
*
|
||||
* @returns 新的查询构建器实例
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const results = entityManager.query()
|
||||
* .withAll(PositionComponent, HealthComponent)
|
||||
* .without(VelocityComponent)
|
||||
* .active()
|
||||
* .execute();
|
||||
* ```
|
||||
*/
|
||||
public query(): EntityQueryBuilder {
|
||||
return new EntityQueryBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用组件索引进行多组件查询
|
||||
*
|
||||
* @param componentTypes 组件类型数组
|
||||
* @param operation 查询操作:'AND' 或 'OR'
|
||||
* @returns 匹配的实体集合
|
||||
*/
|
||||
public queryWithComponentIndex(componentTypes: ComponentType[], operation: 'AND' | 'OR'): Set<Entity> {
|
||||
return this._componentIndexManager.queryMultiple(componentTypes, operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记实体组件已修改
|
||||
*
|
||||
* @param entity 修改的实体
|
||||
* @param componentTypes 修改的组件类型
|
||||
*/
|
||||
public markEntityDirty(entity: Entity, componentTypes: ComponentType[]): void {
|
||||
this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_MODIFIED, componentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取性能优化统计信息
|
||||
*/
|
||||
public getOptimizationStats(): any {
|
||||
return {
|
||||
componentIndex: this._componentIndexManager.getStats(),
|
||||
archetypeSystem: this._archetypeSystem.getAllArchetypes().map(a => ({
|
||||
id: a.id,
|
||||
componentTypes: a.componentTypes.map(t => t.name),
|
||||
entityCount: a.entities.length
|
||||
})),
|
||||
dirtyTracking: this._dirtyTrackingSystem.getStats()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件总线实例
|
||||
*
|
||||
* 允许外部代码监听和发射ECS相关事件。
|
||||
*
|
||||
* @returns 事件总线实例
|
||||
*/
|
||||
public get eventBus(): EventBus {
|
||||
return this._eventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新名称索引
|
||||
*
|
||||
* 维护按名称查找实体的索引结构。支持添加和移除操作。
|
||||
*
|
||||
* @param entity 要更新索引的实体
|
||||
* @param isAdd true表示添加到索引,false表示从索引中移除
|
||||
*/
|
||||
private updateNameIndex(entity: Entity, isAdd: boolean): void {
|
||||
if (!entity.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAdd) {
|
||||
let entities = this._entitiesByName.get(entity.name);
|
||||
if (!entities) {
|
||||
entities = [];
|
||||
this._entitiesByName.set(entity.name, entities);
|
||||
}
|
||||
entities.push(entity);
|
||||
} else {
|
||||
const entities = this._entitiesByName.get(entity.name);
|
||||
if (entities) {
|
||||
const index = entities.indexOf(entity);
|
||||
if (index !== -1) {
|
||||
entities.splice(index, 1);
|
||||
if (entities.length === 0) {
|
||||
this._entitiesByName.delete(entity.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签索引
|
||||
*
|
||||
* 维护按标签查找实体的索引结构。支持添加和移除操作。
|
||||
*
|
||||
* @param entity 要更新索引的实体
|
||||
* @param isAdd true表示添加到索引,false表示从索引中移除
|
||||
*/
|
||||
private updateTagIndex(entity: Entity, isAdd: boolean): void {
|
||||
if (isAdd) {
|
||||
let entities = this._entitiesByTag.get(entity.tag);
|
||||
if (!entities) {
|
||||
entities = [];
|
||||
this._entitiesByTag.set(entity.tag, entities);
|
||||
}
|
||||
entities.push(entity);
|
||||
} else {
|
||||
const entities = this._entitiesByTag.get(entity.tag);
|
||||
if (entities) {
|
||||
const index = entities.indexOf(entity);
|
||||
if (index !== -1) {
|
||||
entities.splice(index, 1);
|
||||
if (entities.length === 0) {
|
||||
this._entitiesByTag.delete(entity.tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
export {
|
||||
ComponentIndexManager,
|
||||
ComponentIndex,
|
||||
IComponentIndex,
|
||||
IndexStats
|
||||
} from '../ComponentIndex';
|
||||
|
||||
export {
|
||||
ArchetypeSystem,
|
||||
Archetype,
|
||||
ArchetypeQueryResult
|
||||
} from '../ArchetypeSystem';
|
||||
|
||||
export {
|
||||
DirtyTrackingSystem,
|
||||
DirtyFlag,
|
||||
DirtyData,
|
||||
DirtyListener
|
||||
} from '../DirtyTrackingSystem';
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { QuerySystem } from '../QuerySystem';
|
||||
export { ECSFluentAPI, createECSAPI } from '../FluentAPI';
|
||||
export { EntityManager, EntityQueryBuilder } from '../EntityManager';
|
||||
|
||||
@@ -220,7 +220,7 @@ export class EntityList {
|
||||
* @param componentType 组件类型
|
||||
* @returns 找到的所有实体数组
|
||||
*/
|
||||
public findEntitiesWithComponent<T extends Component>(componentType: new (...args: unknown[]) => T): Entity[] {
|
||||
public findEntitiesWithComponent<T extends Component>(componentType: new (...args: any[]) => T): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
|
||||
for (const entity of this.buffer) {
|
||||
|
||||
@@ -8,8 +8,6 @@ export { Scene } from './Scene';
|
||||
export { IScene, ISceneFactory, ISceneConfig } from './IScene';
|
||||
export { World, IWorldConfig } from './World';
|
||||
export { WorldManager, IWorldManagerConfig } from './WorldManager';
|
||||
export { EntityManager, EntityQueryBuilder } from './Core/EntityManager';
|
||||
export * from './Core/Events';
|
||||
export * from './Core/Query';
|
||||
export * from './Core/Performance';
|
||||
export * from './Core/Storage';
|
||||
@@ -25,7 +25,10 @@ export interface IComponent {
|
||||
onEnabled(): void;
|
||||
/** 组件禁用时的回调 */
|
||||
onDisabled(): void;
|
||||
/** 更新组件 */
|
||||
/**
|
||||
* 更新组件
|
||||
* @deprecated 不符合ECS架构规范,建议使用EntitySystem来处理更新逻辑
|
||||
*/
|
||||
update(): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { EntityManager } from '../../../src/ECS/Core/EntityManager';
|
||||
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
|
||||
|
||||
describe('批量创建功能测试', () => {
|
||||
let entityManager: EntityManager;
|
||||
|
||||
beforeEach(() => {
|
||||
ComponentTypeManager.instance.reset();
|
||||
entityManager = new EntityManager();
|
||||
});
|
||||
|
||||
test('批量创建实体应该使用ID作为名称', () => {
|
||||
const entities = entityManager.createEntitiesBatch(5, "Test");
|
||||
|
||||
expect(entities).toHaveLength(5);
|
||||
|
||||
// 验证实体名称使用ID而不是索引
|
||||
for (const entity of entities) {
|
||||
expect(entity.name).toBe(`Test_${entity.id}`);
|
||||
}
|
||||
|
||||
// 验证ID是唯一的
|
||||
const ids = entities.map(e => e.id);
|
||||
const uniqueIds = new Set(ids);
|
||||
expect(uniqueIds.size).toBe(5);
|
||||
});
|
||||
|
||||
test('单个创建实体应该使用ID作为默认名称', () => {
|
||||
const entity1 = entityManager.createEntity();
|
||||
const entity2 = entityManager.createEntity();
|
||||
const entity3 = entityManager.createEntity("CustomName");
|
||||
|
||||
expect(entity1.name).toBe(`Entity_${entity1.id}`);
|
||||
expect(entity2.name).toBe(`Entity_${entity2.id}`);
|
||||
expect(entity3.name).toBe("CustomName");
|
||||
|
||||
// 确保ID是连续的或至少是唯一的
|
||||
expect(entity1.id).not.toBe(entity2.id);
|
||||
expect(entity2.id).not.toBe(entity3.id);
|
||||
});
|
||||
|
||||
test('混合创建方式的名称应该一致', () => {
|
||||
// 先单个创建
|
||||
const single = entityManager.createEntity();
|
||||
|
||||
// 再批量创建
|
||||
const batch = entityManager.createEntitiesBatch(3, "Batch");
|
||||
|
||||
// 再单个创建
|
||||
const single2 = entityManager.createEntity();
|
||||
|
||||
// 验证名称格式一致
|
||||
expect(single.name).toBe(`Entity_${single.id}`);
|
||||
expect(single2.name).toBe(`Entity_${single2.id}`);
|
||||
|
||||
for (const entity of batch) {
|
||||
expect(entity.name).toBe(`Batch_${entity.id}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,317 +0,0 @@
|
||||
import { EntityManager } from '../../../src/ECS/Core/EntityManager';
|
||||
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
|
||||
import { Entity } from '../../../src/ECS/Entity';
|
||||
import { Component } from '../../../src/ECS/Component';
|
||||
|
||||
// 测试用组件
|
||||
class TestComponent extends Component {
|
||||
public value: number = 0;
|
||||
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
if (args.length >= 1) this.value = args[0] as number;
|
||||
}
|
||||
}
|
||||
|
||||
class AnotherTestComponent extends Component {
|
||||
public name: string = 'test';
|
||||
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
if (args.length >= 1) this.name = args[0] as string;
|
||||
}
|
||||
}
|
||||
|
||||
describe('ComponentIndexManager功能测试', () => {
|
||||
let entityManager: EntityManager;
|
||||
|
||||
beforeEach(() => {
|
||||
ComponentTypeManager.instance.reset();
|
||||
entityManager = new EntityManager();
|
||||
});
|
||||
|
||||
describe('基本功能测试', () => {
|
||||
test('应该能够正确创建空实体', () => {
|
||||
const entity = entityManager.createEntity('TestEntity');
|
||||
|
||||
expect(entity).toBeDefined();
|
||||
expect(entity.name).toBe('TestEntity');
|
||||
expect(entity.components.length).toBe(0);
|
||||
expect(entity.id).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
test('应该能够正确管理实体的组件索引', () => {
|
||||
const entity = entityManager.createEntity('TestEntity');
|
||||
const component = new TestComponent(42);
|
||||
|
||||
// 添加组件前
|
||||
expect(entity.hasComponent(TestComponent)).toBe(false);
|
||||
|
||||
// 添加组件
|
||||
entity.addComponent(component);
|
||||
|
||||
// 添加组件后
|
||||
expect(entity.hasComponent(TestComponent)).toBe(true);
|
||||
expect(entity.getComponent(TestComponent)).toBe(component);
|
||||
expect(entity.components.length).toBe(1);
|
||||
});
|
||||
|
||||
test('应该能够正确处理组件查询', () => {
|
||||
const entity1 = entityManager.createEntity('Entity1');
|
||||
const entity2 = entityManager.createEntity('Entity2');
|
||||
|
||||
entity1.addComponent(new TestComponent(1));
|
||||
entity2.addComponent(new TestComponent(2));
|
||||
entity2.addComponent(new AnotherTestComponent('test2'));
|
||||
|
||||
// 查询包含TestComponent的实体
|
||||
const entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent);
|
||||
expect(entitiesWithTest).toHaveLength(2);
|
||||
expect(entitiesWithTest).toContain(entity1);
|
||||
expect(entitiesWithTest).toContain(entity2);
|
||||
|
||||
// 查询包含AnotherTestComponent的实体
|
||||
const entitiesWithAnother = entityManager.getEntitiesWithComponent(AnotherTestComponent);
|
||||
expect(entitiesWithAnother).toHaveLength(1);
|
||||
expect(entitiesWithAnother).toContain(entity2);
|
||||
});
|
||||
|
||||
test('应该能够正确处理复杂查询', () => {
|
||||
const entity1 = entityManager.createEntity('Entity1');
|
||||
const entity2 = entityManager.createEntity('Entity2');
|
||||
const entity3 = entityManager.createEntity('Entity3');
|
||||
|
||||
// entity1: TestComponent
|
||||
entity1.addComponent(new TestComponent(1));
|
||||
|
||||
// entity2: TestComponent + AnotherTestComponent
|
||||
entity2.addComponent(new TestComponent(2));
|
||||
entity2.addComponent(new AnotherTestComponent('test2'));
|
||||
|
||||
// entity3: AnotherTestComponent
|
||||
entity3.addComponent(new AnotherTestComponent('test3'));
|
||||
|
||||
// AND查询:同时包含两个组件的实体
|
||||
const bothComponents = entityManager.queryWithComponentIndex(
|
||||
[TestComponent, AnotherTestComponent],
|
||||
'AND'
|
||||
);
|
||||
expect(bothComponents.size).toBe(1);
|
||||
expect(bothComponents.has(entity2)).toBe(true);
|
||||
|
||||
// OR查询:包含任一组件的实体
|
||||
const eitherComponent = entityManager.queryWithComponentIndex(
|
||||
[TestComponent, AnotherTestComponent],
|
||||
'OR'
|
||||
);
|
||||
expect(eitherComponent.size).toBe(3);
|
||||
expect(eitherComponent.has(entity1)).toBe(true);
|
||||
expect(eitherComponent.has(entity2)).toBe(true);
|
||||
expect(eitherComponent.has(entity3)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('组件移除功能测试', () => {
|
||||
test('应该能够手动管理组件索引', () => {
|
||||
const entity1 = entityManager.createEntity('TestEntity1');
|
||||
const entity2 = entityManager.createEntity('TestEntity2');
|
||||
const component1 = new TestComponent(42);
|
||||
const component2 = new TestComponent(84);
|
||||
|
||||
// 添加组件到实体
|
||||
entity1.addComponent(component1);
|
||||
entity2.addComponent(component2);
|
||||
|
||||
// 手动将实体添加到索引
|
||||
entityManager['_componentIndexManager'].addEntity(entity1);
|
||||
entityManager['_componentIndexManager'].addEntity(entity2);
|
||||
|
||||
// 验证能够查询到实体
|
||||
let entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent);
|
||||
expect(entitiesWithTest).toHaveLength(2);
|
||||
expect(entitiesWithTest).toContain(entity1);
|
||||
expect(entitiesWithTest).toContain(entity2);
|
||||
|
||||
// 手动移除一个实体的索引
|
||||
entityManager['_componentIndexManager'].removeEntity(entity1);
|
||||
|
||||
// 验证只能查询到剩余的实体
|
||||
entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent);
|
||||
expect(entitiesWithTest).toHaveLength(1);
|
||||
expect(entitiesWithTest[0]).toBe(entity2);
|
||||
});
|
||||
|
||||
test('应该能够正确处理实体销毁', () => {
|
||||
const entity = entityManager.createEntity('TestEntity');
|
||||
entity.addComponent(new TestComponent(42));
|
||||
|
||||
// 确认实体存在且有组件
|
||||
expect(entityManager.getEntity(entity.id)).toBe(entity);
|
||||
expect(entityManager.getEntitiesWithComponent(TestComponent)).toHaveLength(1);
|
||||
|
||||
// 销毁实体
|
||||
const destroyed = entityManager.destroyEntity(entity);
|
||||
expect(destroyed).toBe(true);
|
||||
|
||||
// 确认实体被正确销毁
|
||||
expect(entityManager.getEntity(entity.id)).toBeNull();
|
||||
expect(entityManager.getEntitiesWithComponent(TestComponent)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('批量操作功能测试', () => {
|
||||
test('应该能够正确处理批量创建实体', () => {
|
||||
const entities = entityManager.createEntitiesBatch(5, 'BatchEntity');
|
||||
|
||||
expect(entities).toHaveLength(5);
|
||||
entities.forEach((entity, index) => {
|
||||
expect(entity.name).toMatch(/^BatchEntity_\d+$/);
|
||||
expect(entity.components.length).toBe(0);
|
||||
expect(entityManager.getEntity(entity.id)).toBe(entity);
|
||||
});
|
||||
|
||||
expect(entityManager.entityCount).toBe(5);
|
||||
});
|
||||
|
||||
test('批量创建的实体应该有正确的索引', () => {
|
||||
const entities = entityManager.createEntitiesBatch(3, 'IndexTest');
|
||||
|
||||
// 给第一个和第三个实体添加组件
|
||||
entities[0].addComponent(new TestComponent(1));
|
||||
entities[2].addComponent(new TestComponent(3));
|
||||
|
||||
const entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent);
|
||||
expect(entitiesWithTest).toHaveLength(2);
|
||||
expect(entitiesWithTest).toContain(entities[0]);
|
||||
expect(entitiesWithTest).toContain(entities[2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('查询构建器功能测试', () => {
|
||||
test('应该能够使用查询构建器进行复杂查询', () => {
|
||||
const entity1 = entityManager.createEntity('Active1');
|
||||
const entity2 = entityManager.createEntity('Active2');
|
||||
const entity3 = entityManager.createEntity('Inactive');
|
||||
|
||||
entity1.addComponent(new TestComponent(1));
|
||||
entity1.active = true;
|
||||
|
||||
entity2.addComponent(new TestComponent(2));
|
||||
entity2.addComponent(new AnotherTestComponent('test2'));
|
||||
entity2.active = true;
|
||||
|
||||
entity3.addComponent(new TestComponent(3));
|
||||
entity3.active = false;
|
||||
|
||||
// 查询激活状态且包含TestComponent的实体
|
||||
const activeWithTest = entityManager.query()
|
||||
.withAll(TestComponent)
|
||||
.active()
|
||||
.execute();
|
||||
|
||||
expect(activeWithTest).toHaveLength(2);
|
||||
expect(activeWithTest).toContain(entity1);
|
||||
expect(activeWithTest).toContain(entity2);
|
||||
expect(activeWithTest).not.toContain(entity3);
|
||||
|
||||
// 查询同时包含两个组件的实体
|
||||
const withBothComponents = entityManager.query()
|
||||
.withAll(TestComponent, AnotherTestComponent)
|
||||
.execute();
|
||||
|
||||
expect(withBothComponents).toHaveLength(1);
|
||||
expect(withBothComponents).toContain(entity2);
|
||||
});
|
||||
|
||||
test('查询构建器应该支持自定义过滤条件', () => {
|
||||
const entity1 = entityManager.createEntity('Player1');
|
||||
const entity2 = entityManager.createEntity('Enemy1');
|
||||
const entity3 = entityManager.createEntity('Player2');
|
||||
|
||||
entity1.addComponent(new TestComponent(100));
|
||||
entity2.addComponent(new TestComponent(50));
|
||||
entity3.addComponent(new TestComponent(200));
|
||||
|
||||
// 查询名称以"Player"开头且TestComponent值大于150的实体
|
||||
const strongPlayers = entityManager.query()
|
||||
.withAll(TestComponent)
|
||||
.where(entity => entity.name.startsWith('Player'))
|
||||
.where(entity => {
|
||||
const testComp = entity.getComponent(TestComponent);
|
||||
return testComp !== null && testComp.value > 150;
|
||||
})
|
||||
.execute();
|
||||
|
||||
expect(strongPlayers).toHaveLength(1);
|
||||
expect(strongPlayers).toContain(entity3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('统计信息功能测试', () => {
|
||||
test('应该能够获取正确的统计信息', () => {
|
||||
// 创建一些实体和组件
|
||||
const entity1 = entityManager.createEntity('StatTest1');
|
||||
const entity2 = entityManager.createEntity('StatTest2');
|
||||
|
||||
entity1.addComponent(new TestComponent(1));
|
||||
entity2.addComponent(new TestComponent(2));
|
||||
entity2.addComponent(new AnotherTestComponent('test'));
|
||||
|
||||
const stats = entityManager.getOptimizationStats();
|
||||
|
||||
expect(stats).toBeDefined();
|
||||
expect(stats.componentIndex).toBeDefined();
|
||||
expect(stats.archetypeSystem).toBeDefined();
|
||||
expect(stats.dirtyTracking).toBeDefined();
|
||||
});
|
||||
|
||||
test('应该能够正确统计实体数量', () => {
|
||||
expect(entityManager.entityCount).toBe(0);
|
||||
expect(entityManager.activeEntityCount).toBe(0);
|
||||
|
||||
const entity1 = entityManager.createEntity('Count1');
|
||||
const entity2 = entityManager.createEntity('Count2');
|
||||
|
||||
expect(entityManager.entityCount).toBe(2);
|
||||
expect(entityManager.activeEntityCount).toBe(2);
|
||||
|
||||
entity1.active = false;
|
||||
expect(entityManager.activeEntityCount).toBe(1);
|
||||
|
||||
entityManager.destroyEntity(entity2);
|
||||
expect(entityManager.entityCount).toBe(1);
|
||||
expect(entityManager.activeEntityCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('边界情况测试', () => {
|
||||
test('应该能够处理空组件列表的查询', () => {
|
||||
const entity = entityManager.createEntity('EmptyTest');
|
||||
|
||||
const emptyQuery = entityManager.queryWithComponentIndex([], 'AND');
|
||||
expect(emptyQuery.size).toBe(0);
|
||||
|
||||
const emptyOrQuery = entityManager.queryWithComponentIndex([], 'OR');
|
||||
expect(emptyOrQuery.size).toBe(0);
|
||||
});
|
||||
|
||||
test('应该能够处理不存在的组件类型查询', () => {
|
||||
class NonExistentComponent extends Component {}
|
||||
|
||||
const entities = entityManager.getEntitiesWithComponent(NonExistentComponent);
|
||||
expect(entities).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('应该能够处理重复添加相同组件类型', () => {
|
||||
const entity = entityManager.createEntity('DuplicateTest');
|
||||
const component1 = new TestComponent(1);
|
||||
const component2 = new TestComponent(2);
|
||||
|
||||
entity.addComponent(component1);
|
||||
expect(() => entity.addComponent(component2)).toThrow();
|
||||
|
||||
// 第一个组件应该仍然存在
|
||||
expect(entity.getComponent(TestComponent)).toBe(component1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,626 +0,0 @@
|
||||
import { EntityManager, EntityQueryBuilder } from '../../../src/ECS/Core/EntityManager';
|
||||
import { Entity } from '../../../src/ECS/Entity';
|
||||
import { Component } from '../../../src/ECS/Component';
|
||||
|
||||
// 测试组件
|
||||
class PositionComponent extends Component {
|
||||
public x: number;
|
||||
public y: number;
|
||||
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [x = 0, y = 0] = args as [number?, number?];
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
class VelocityComponent extends Component {
|
||||
public vx: number;
|
||||
public vy: number;
|
||||
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [vx = 0, vy = 0] = args as [number?, number?];
|
||||
this.vx = vx;
|
||||
this.vy = vy;
|
||||
}
|
||||
}
|
||||
|
||||
class HealthComponent extends Component {
|
||||
public health: number;
|
||||
public maxHealth: number;
|
||||
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [health = 100, maxHealth = 100] = args as [number?, number?];
|
||||
this.health = health;
|
||||
this.maxHealth = maxHealth;
|
||||
}
|
||||
}
|
||||
|
||||
class RenderComponent extends Component {
|
||||
public visible: boolean;
|
||||
public color: string;
|
||||
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [visible = true, color = 'white'] = args as [boolean?, string?];
|
||||
this.visible = visible;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
class AIComponent extends Component {
|
||||
public intelligence: number;
|
||||
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [intelligence = 50] = args as [number?];
|
||||
this.intelligence = intelligence;
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerComponent extends Component {
|
||||
public name: string;
|
||||
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [name = 'Player'] = args as [string?];
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
describe('EntityManager - 实体管理器测试', () => {
|
||||
let entityManager: EntityManager;
|
||||
|
||||
beforeEach(() => {
|
||||
entityManager = new EntityManager();
|
||||
});
|
||||
|
||||
describe('基本功能测试', () => {
|
||||
test('应该能够创建EntityManager实例', () => {
|
||||
expect(entityManager).toBeDefined();
|
||||
expect(entityManager).toBeInstanceOf(EntityManager);
|
||||
expect(entityManager.entityCount).toBe(0);
|
||||
});
|
||||
|
||||
test('应该能够创建实体', () => {
|
||||
const entity = entityManager.createEntity('TestEntity');
|
||||
|
||||
expect(entity).toBeDefined();
|
||||
expect(entity.name).toBe('TestEntity');
|
||||
expect(entity.id).toBeGreaterThan(0);
|
||||
expect(entityManager.entityCount).toBe(1);
|
||||
});
|
||||
|
||||
test('应该能够创建实体使用默认名称', () => {
|
||||
const entity = entityManager.createEntity();
|
||||
|
||||
expect(entity).toBeDefined();
|
||||
expect(entity.name).toContain('Entity_');
|
||||
expect(entity.id).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('应该能够批量创建实体', () => {
|
||||
const entities: Entity[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
entities.push(entityManager.createEntity(`Entity_${i}`));
|
||||
}
|
||||
|
||||
expect(entities.length).toBe(5);
|
||||
expect(entityManager.entityCount).toBe(5);
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
expect(entities[i].name).toBe(`Entity_${i}`);
|
||||
expect(entities[i].id).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够通过ID查找实体', () => {
|
||||
const entity = entityManager.createEntity('TestEntity');
|
||||
const found = entityManager.getEntity(entity.id);
|
||||
|
||||
expect(found).toBe(entity);
|
||||
});
|
||||
|
||||
test('查找不存在的实体应该返回null', () => {
|
||||
const found = entityManager.getEntity(999999);
|
||||
expect(found).toBeNull();
|
||||
});
|
||||
|
||||
test('应该能够销毁实体', () => {
|
||||
const entity = entityManager.createEntity('TestEntity');
|
||||
const entityId = entity.id;
|
||||
|
||||
const result = entityManager.destroyEntity(entity);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(entityManager.getEntity(entityId)).toBeNull();
|
||||
expect(entityManager.entityCount).toBe(0);
|
||||
});
|
||||
|
||||
test('应该能够通过ID销毁实体', () => {
|
||||
const entity = entityManager.createEntity('TestEntity');
|
||||
const entityId = entity.id;
|
||||
|
||||
const result = entityManager.destroyEntity(entityId);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(entityManager.getEntity(entityId)).toBeNull();
|
||||
});
|
||||
|
||||
test('销毁不存在的实体应该返回false', () => {
|
||||
const result = entityManager.destroyEntity(999999);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('应该正确统计激活状态的实体', () => {
|
||||
const entity1 = entityManager.createEntity('Active1');
|
||||
const entity2 = entityManager.createEntity('Active2');
|
||||
const entity3 = entityManager.createEntity('Inactive');
|
||||
|
||||
entity3.active = false;
|
||||
|
||||
expect(entityManager.activeEntityCount).toBe(2);
|
||||
expect(entityManager.entityCount).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('实体标签功能测试', () => {
|
||||
test('实体应该有默认标签', () => {
|
||||
const entity = entityManager.createEntity('TaggedEntity');
|
||||
expect(entity.tag).toBe(0); // 默认标签为0
|
||||
});
|
||||
|
||||
test('应该能够为实体设置标签', () => {
|
||||
const entity = entityManager.createEntity('TaggedEntity');
|
||||
entity.tag = 1;
|
||||
|
||||
expect(entity.tag).toBe(1);
|
||||
});
|
||||
|
||||
test('应该能够按标签查询实体', () => {
|
||||
const entity1 = entityManager.createEntity('Entity1');
|
||||
const entity2 = entityManager.createEntity('Entity2');
|
||||
const entity3 = entityManager.createEntity('Entity3');
|
||||
|
||||
entity1.tag = 1;
|
||||
entity2.tag = 1;
|
||||
entity3.tag = 2;
|
||||
|
||||
const tag1Entities = entityManager.getEntitiesByTag(1);
|
||||
const tag2Entities = entityManager.getEntitiesByTag(2);
|
||||
|
||||
expect(tag1Entities.length).toBe(2);
|
||||
expect(tag2Entities.length).toBe(1);
|
||||
expect(tag1Entities).toContain(entity1);
|
||||
expect(tag1Entities).toContain(entity2);
|
||||
expect(tag2Entities).toContain(entity3);
|
||||
});
|
||||
|
||||
test('查询不存在的标签应该返回空数组', () => {
|
||||
const entities = entityManager.getEntitiesByTag(999);
|
||||
expect(entities).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('查询构建器测试', () => {
|
||||
let player: Entity;
|
||||
let enemy1: Entity;
|
||||
let enemy2: Entity;
|
||||
let npc: Entity;
|
||||
|
||||
beforeEach(() => {
|
||||
// 创建测试实体
|
||||
player = entityManager.createEntity('Player');
|
||||
player.addComponent(new PositionComponent(50, 50));
|
||||
player.addComponent(new HealthComponent(100, 100));
|
||||
player.addComponent(new PlayerComponent('Hero'));
|
||||
player.tag = 1;
|
||||
|
||||
enemy1 = entityManager.createEntity('Enemy1');
|
||||
enemy1.addComponent(new PositionComponent(10, 10));
|
||||
enemy1.addComponent(new VelocityComponent(1, 0));
|
||||
enemy1.addComponent(new HealthComponent(50, 50));
|
||||
enemy1.addComponent(new AIComponent(30));
|
||||
enemy1.tag = 2;
|
||||
|
||||
enemy2 = entityManager.createEntity('Enemy2');
|
||||
enemy2.addComponent(new PositionComponent(90, 90));
|
||||
enemy2.addComponent(new VelocityComponent(-1, 0));
|
||||
enemy2.addComponent(new HealthComponent(75, 75));
|
||||
enemy2.addComponent(new AIComponent(45));
|
||||
enemy2.tag = 2;
|
||||
|
||||
npc = entityManager.createEntity('NPC');
|
||||
npc.addComponent(new PositionComponent(25, 75));
|
||||
npc.addComponent(new RenderComponent(true, 'blue'));
|
||||
npc.tag = 3;
|
||||
});
|
||||
|
||||
test('应该能够查询具有所有指定组件的实体', () => {
|
||||
const results = entityManager.query()
|
||||
.withAll(PositionComponent, HealthComponent)
|
||||
.execute();
|
||||
|
||||
expect(results.length).toBe(3); // player, enemy1, enemy2
|
||||
expect(results).toContain(player);
|
||||
expect(results).toContain(enemy1);
|
||||
expect(results).toContain(enemy2);
|
||||
expect(results).not.toContain(npc);
|
||||
});
|
||||
|
||||
test('应该能够查询具有任意指定组件的实体', () => {
|
||||
const results = entityManager.query()
|
||||
.withAny(PlayerComponent, AIComponent)
|
||||
.execute();
|
||||
|
||||
expect(results.length).toBe(3); // player, enemy1, enemy2
|
||||
expect(results).toContain(player);
|
||||
expect(results).toContain(enemy1);
|
||||
expect(results).toContain(enemy2);
|
||||
expect(results).not.toContain(npc);
|
||||
});
|
||||
|
||||
test('应该能够排除具有指定组件的实体', () => {
|
||||
const results = entityManager.query()
|
||||
.withAll(PositionComponent)
|
||||
.without(AIComponent)
|
||||
.execute();
|
||||
|
||||
expect(results.length).toBe(2); // player, npc
|
||||
expect(results).toContain(player);
|
||||
expect(results).toContain(npc);
|
||||
expect(results).not.toContain(enemy1);
|
||||
expect(results).not.toContain(enemy2);
|
||||
});
|
||||
|
||||
test('应该能够按标签过滤实体', () => {
|
||||
const results = entityManager.query()
|
||||
.withTag(2)
|
||||
.execute();
|
||||
|
||||
expect(results.length).toBe(2); // enemy1, enemy2
|
||||
expect(results).toContain(enemy1);
|
||||
expect(results).toContain(enemy2);
|
||||
expect(results).not.toContain(player);
|
||||
expect(results).not.toContain(npc);
|
||||
});
|
||||
|
||||
test('应该能够排除特定标签的实体', () => {
|
||||
const results = entityManager.query()
|
||||
.withAll(PositionComponent)
|
||||
.withoutTag(2)
|
||||
.execute();
|
||||
|
||||
expect(results.length).toBe(2); // player, npc
|
||||
expect(results).toContain(player);
|
||||
expect(results).toContain(npc);
|
||||
expect(results).not.toContain(enemy1);
|
||||
expect(results).not.toContain(enemy2);
|
||||
});
|
||||
|
||||
test('应该能够只查询激活状态的实体', () => {
|
||||
enemy1.active = false;
|
||||
|
||||
const results = entityManager.query()
|
||||
.withAll(PositionComponent, HealthComponent)
|
||||
.active()
|
||||
.execute();
|
||||
|
||||
expect(results.length).toBe(2); // player, enemy2
|
||||
expect(results).toContain(player);
|
||||
expect(results).toContain(enemy2);
|
||||
expect(results).not.toContain(enemy1);
|
||||
});
|
||||
|
||||
test('应该能够只查询启用状态的实体', () => {
|
||||
npc.enabled = false;
|
||||
|
||||
const results = entityManager.query()
|
||||
.withAll(PositionComponent)
|
||||
.enabled()
|
||||
.execute();
|
||||
|
||||
expect(results.length).toBe(3); // player, enemy1, enemy2
|
||||
expect(results).toContain(player);
|
||||
expect(results).toContain(enemy1);
|
||||
expect(results).toContain(enemy2);
|
||||
expect(results).not.toContain(npc);
|
||||
});
|
||||
|
||||
test('应该能够使用自定义过滤条件', () => {
|
||||
const results = entityManager.query()
|
||||
.withAll(HealthComponent)
|
||||
.where(entity => {
|
||||
const health = entity.getComponent(HealthComponent);
|
||||
return health!.health > 60;
|
||||
})
|
||||
.execute();
|
||||
|
||||
expect(results.length).toBe(2); // player, enemy2
|
||||
expect(results).toContain(player);
|
||||
expect(results).toContain(enemy2);
|
||||
expect(results).not.toContain(enemy1);
|
||||
});
|
||||
|
||||
test('应该能够组合多个查询条件', () => {
|
||||
const results = entityManager.query()
|
||||
.withAll(PositionComponent, HealthComponent)
|
||||
.without(PlayerComponent)
|
||||
.withTag(2)
|
||||
.where(entity => {
|
||||
const position = entity.getComponent(PositionComponent);
|
||||
return position!.x < 50;
|
||||
})
|
||||
.execute();
|
||||
|
||||
expect(results.length).toBe(1); // enemy1
|
||||
expect(results).toContain(enemy1);
|
||||
expect(results).not.toContain(player);
|
||||
expect(results).not.toContain(enemy2);
|
||||
expect(results).not.toContain(npc);
|
||||
});
|
||||
|
||||
test('空查询应该返回所有实体', () => {
|
||||
const results = entityManager.query().execute();
|
||||
|
||||
expect(results.length).toBe(4); // all entities
|
||||
expect(results).toContain(player);
|
||||
expect(results).toContain(enemy1);
|
||||
expect(results).toContain(enemy2);
|
||||
expect(results).toContain(npc);
|
||||
});
|
||||
|
||||
test('不匹配的查询应该返回空数组', () => {
|
||||
const results = entityManager.query()
|
||||
.withAll(PlayerComponent, AIComponent) // 不可能的组合
|
||||
.execute();
|
||||
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('事件系统集成', () => {
|
||||
test('创建实体应该触发事件', () => {
|
||||
let eventData: any = null;
|
||||
|
||||
entityManager.eventBus.onEntityCreated((data) => {
|
||||
eventData = data;
|
||||
});
|
||||
|
||||
const entity = entityManager.createEntity('EventEntity');
|
||||
|
||||
expect(eventData).toBeDefined();
|
||||
expect(eventData.entityName).toBe('EventEntity');
|
||||
expect(eventData.entityId).toBe(entity.id);
|
||||
});
|
||||
|
||||
test('销毁实体应该触发事件', () => {
|
||||
let eventData: any = null;
|
||||
|
||||
entityManager.eventBus.on('entity:destroyed', (data: any) => {
|
||||
eventData = data;
|
||||
});
|
||||
|
||||
const entity = entityManager.createEntity('EventEntity');
|
||||
entityManager.destroyEntity(entity);
|
||||
|
||||
expect(eventData).toBeDefined();
|
||||
expect(eventData.entityName).toBe('EventEntity');
|
||||
expect(eventData.entityId).toBe(entity.id);
|
||||
});
|
||||
|
||||
test('添加组件应该触发事件', () => {
|
||||
let eventData: any = null;
|
||||
|
||||
entityManager.eventBus.onComponentAdded((data) => {
|
||||
eventData = data;
|
||||
});
|
||||
|
||||
const entity = entityManager.createEntity('ComponentEntity');
|
||||
entity.addComponent(new PositionComponent(10, 20));
|
||||
|
||||
expect(eventData).toBeDefined();
|
||||
expect(eventData.componentType).toBe('PositionComponent');
|
||||
expect(eventData.entityId).toBe(entity.id);
|
||||
});
|
||||
|
||||
test('移除组件应该触发事件', () => {
|
||||
let eventData: any = null;
|
||||
|
||||
entityManager.eventBus.on('component:removed', (data: any) => {
|
||||
eventData = data;
|
||||
});
|
||||
|
||||
const entity = entityManager.createEntity('ComponentEntity');
|
||||
const component = entity.addComponent(new PositionComponent(10, 20));
|
||||
entity.removeComponent(component);
|
||||
|
||||
expect(eventData).toBeDefined();
|
||||
expect(eventData.componentType).toBe('PositionComponent');
|
||||
expect(eventData.entityId).toBe(entity.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('性能和内存测试', () => {
|
||||
test('大量实体创建性能应该可接受', () => {
|
||||
const entityCount = 10000;
|
||||
const startTime = performance.now();
|
||||
|
||||
const entities: Entity[] = [];
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
entities.push(entityManager.createEntity(`PerfEntity_${i}`));
|
||||
}
|
||||
|
||||
const endTime = performance.now();
|
||||
const duration = endTime - startTime;
|
||||
|
||||
expect(entities.length).toBe(entityCount);
|
||||
expect(entityManager.entityCount).toBe(entityCount);
|
||||
// 性能记录:实体创建性能数据,不设硬阈值避免CI不稳定
|
||||
|
||||
console.log(`创建${entityCount}个实体耗时: ${duration.toFixed(2)}ms`);
|
||||
});
|
||||
|
||||
test('大量实体查询性能应该可接受', () => {
|
||||
const entityCount = 5000;
|
||||
|
||||
// 创建大量实体并添加组件
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
const entity = entityManager.createEntity(`Entity_${i}`);
|
||||
entity.addComponent(new PositionComponent(i, i));
|
||||
|
||||
if (i % 2 === 0) {
|
||||
entity.addComponent(new VelocityComponent(1, 1));
|
||||
}
|
||||
|
||||
if (i % 3 === 0) {
|
||||
entity.addComponent(new HealthComponent(100));
|
||||
}
|
||||
}
|
||||
|
||||
const startTime = performance.now();
|
||||
|
||||
// 执行多个查询
|
||||
const positionResults = entityManager.query().withAll(PositionComponent).execute();
|
||||
const velocityResults = entityManager.query().withAll(VelocityComponent).execute();
|
||||
const healthResults = entityManager.query().withAll(HealthComponent).execute();
|
||||
const complexResults = entityManager.query()
|
||||
.withAll(PositionComponent, VelocityComponent)
|
||||
.without(HealthComponent)
|
||||
.execute();
|
||||
|
||||
const endTime = performance.now();
|
||||
const duration = endTime - startTime;
|
||||
|
||||
expect(positionResults.length).toBe(entityCount);
|
||||
expect(velocityResults.length).toBe(entityCount / 2);
|
||||
expect(healthResults.length).toBe(Math.floor(entityCount / 3) + 1);
|
||||
// 性能记录:复杂查询性能数据,不设硬阈值避免CI不稳定
|
||||
|
||||
console.log(`${entityCount}个实体的复杂查询耗时: ${duration.toFixed(2)}ms`);
|
||||
});
|
||||
|
||||
test('实体销毁应该正确清理内存', () => {
|
||||
const entityCount = 1000;
|
||||
const entities: Entity[] = [];
|
||||
|
||||
// 创建实体
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
const entity = entityManager.createEntity(`MemoryEntity_${i}`);
|
||||
entity.addComponent(new PositionComponent(0, 0));
|
||||
entity.addComponent(new HealthComponent(100));
|
||||
entities.push(entity);
|
||||
}
|
||||
|
||||
expect(entityManager.entityCount).toBe(entityCount);
|
||||
|
||||
// 销毁所有实体
|
||||
entities.forEach(entity => {
|
||||
entityManager.destroyEntity(entity);
|
||||
});
|
||||
|
||||
// 验证所有实体都已被清理
|
||||
expect(entityManager.entityCount).toBe(0);
|
||||
entities.forEach(entity => {
|
||||
expect(entityManager.getEntity(entity.id)).toBeNull();
|
||||
});
|
||||
|
||||
// 查询应该返回空结果
|
||||
const positionResults = entityManager.query().withAll(PositionComponent).execute();
|
||||
const healthResults = entityManager.query().withAll(HealthComponent).execute();
|
||||
|
||||
expect(positionResults).toEqual([]);
|
||||
expect(healthResults).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('错误处理和边界情况', () => {
|
||||
test('对已销毁实体的查询操作应该安全处理', () => {
|
||||
const entity = entityManager.createEntity('ToBeDestroyed');
|
||||
entity.addComponent(new PositionComponent(0, 0));
|
||||
|
||||
entityManager.destroyEntity(entity);
|
||||
|
||||
// 查询不应该包含已销毁的实体
|
||||
const results = entityManager.query().withAll(PositionComponent).execute();
|
||||
expect(results).not.toContain(entity);
|
||||
});
|
||||
|
||||
test('空查询构建器应该能正常工作', () => {
|
||||
const builder = entityManager.query();
|
||||
|
||||
expect(() => {
|
||||
const results = builder.execute();
|
||||
expect(Array.isArray(results)).toBe(true);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('重复销毁同一实体应该安全处理', () => {
|
||||
const entity = entityManager.createEntity('TestEntity');
|
||||
|
||||
const result1 = entityManager.destroyEntity(entity);
|
||||
const result2 = entityManager.destroyEntity(entity);
|
||||
|
||||
expect(result1).toBe(true);
|
||||
expect(result2).toBe(false);
|
||||
});
|
||||
|
||||
test('销毁实体后其组件应该正确清理', () => {
|
||||
const entity = entityManager.createEntity('TestEntity');
|
||||
entity.addComponent(new PositionComponent(10, 20));
|
||||
entity.addComponent(new HealthComponent(100));
|
||||
|
||||
const initialPositionResults = entityManager.query().withAll(PositionComponent).execute();
|
||||
expect(initialPositionResults).toContain(entity);
|
||||
|
||||
entityManager.destroyEntity(entity);
|
||||
|
||||
const finalPositionResults = entityManager.query().withAll(PositionComponent).execute();
|
||||
expect(finalPositionResults).not.toContain(entity);
|
||||
});
|
||||
});
|
||||
|
||||
describe('统计和调试信息', () => {
|
||||
test('应该能够获取实体管理器统计信息', () => {
|
||||
// 创建一些实体和组件
|
||||
const entities: Entity[] = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const entity = entityManager.createEntity(`StatsEntity_${i}`);
|
||||
entity.addComponent(new PositionComponent(0, 0));
|
||||
entities.push(entity);
|
||||
}
|
||||
|
||||
// EntityManager doesn't have getStats method, use basic counts
|
||||
expect(entityManager.entityCount).toBe(10);
|
||||
expect(entityManager.activeEntityCount).toBe(10);
|
||||
});
|
||||
|
||||
test('销毁实体后统计信息应该更新', () => {
|
||||
const entities: Entity[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
entities.push(entityManager.createEntity(`StatsEntity_${i}`));
|
||||
}
|
||||
|
||||
entityManager.destroyEntity(entities[0]);
|
||||
entityManager.destroyEntity(entities[1]);
|
||||
|
||||
expect(entityManager.entityCount).toBe(3);
|
||||
expect(entityManager.activeEntityCount).toBe(3);
|
||||
});
|
||||
|
||||
test('非激活实体应该在统计中正确反映', () => {
|
||||
const entities: Entity[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
entities.push(entityManager.createEntity(`StatsEntity_${i}`));
|
||||
}
|
||||
|
||||
entities[0].active = false;
|
||||
entities[1].active = false;
|
||||
|
||||
expect(entityManager.entityCount).toBe(5);
|
||||
expect(entityManager.activeEntityCount).toBe(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user