style(core): ESLint自动修复代码格式问题 (#210)
This commit is contained in:
@@ -329,8 +329,8 @@ export class Core {
|
||||
*/
|
||||
public static setScene<T extends IScene>(scene: T): T {
|
||||
if (!this._instance) {
|
||||
Core._logger.warn("Core实例未创建,请先调用Core.create()");
|
||||
throw new Error("Core实例未创建");
|
||||
Core._logger.warn('Core实例未创建,请先调用Core.create()');
|
||||
throw new Error('Core实例未创建');
|
||||
}
|
||||
|
||||
return this._instance._sceneManager.setScene(scene);
|
||||
@@ -387,7 +387,7 @@ export class Core {
|
||||
*/
|
||||
public static loadScene<T extends IScene>(scene: T): void {
|
||||
if (!this._instance) {
|
||||
Core._logger.warn("Core实例未创建,请先调用Core.create()");
|
||||
Core._logger.warn('Core实例未创建,请先调用Core.create()');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -422,7 +422,7 @@ export class Core {
|
||||
*/
|
||||
public static update(deltaTime: number): void {
|
||||
if (!this._instance) {
|
||||
Core._logger.warn("Core实例未创建,请先调用Core.create()");
|
||||
Core._logger.warn('Core实例未创建,请先调用Core.create()');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -472,7 +472,7 @@ export class Core {
|
||||
*/
|
||||
public static enableDebug(config: IECSDebugConfig): void {
|
||||
if (!this._instance) {
|
||||
Core._logger.warn("Core实例未创建,请先调用Core.create()");
|
||||
Core._logger.warn('Core实例未创建,请先调用Core.create()');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ export function Updatable(priority: number = 0): ClassDecorator {
|
||||
if (!prototype || typeof prototype.update !== 'function') {
|
||||
throw new Error(
|
||||
`@Updatable() decorator requires class ${target.name} to implement IUpdatable interface with update() method. ` +
|
||||
`Please add 'implements IUpdatable' and define update(deltaTime?: number): void method.`
|
||||
'Please add \'implements IUpdatable\' and define update(deltaTime?: number): void method.'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ export function createInstance<T>(
|
||||
if (typeof serviceType === 'string' || typeof serviceType === 'symbol') {
|
||||
// 字符串或Symbol类型的服务标识
|
||||
throw new Error(
|
||||
`String and Symbol service identifiers are not yet supported in constructor injection. ` +
|
||||
'String and Symbol service identifiers are not yet supported in constructor injection. ' +
|
||||
`Please use class types for ${constructor.name} parameter ${i}`
|
||||
);
|
||||
} else {
|
||||
@@ -338,7 +338,7 @@ export function registerInjectable<T extends IService>(
|
||||
if (!isInjectable(serviceType)) {
|
||||
throw new Error(
|
||||
`${serviceType.name} is not marked as @Injectable(). ` +
|
||||
`Please add @Injectable() decorator to the class.`
|
||||
'Please add @Injectable() decorator to the class.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ export class ServiceContainer {
|
||||
|
||||
// 检测循环依赖
|
||||
if (this._resolving.has(type as ServiceType<IService>)) {
|
||||
const chain = Array.from(this._resolving).map(t => t.name).join(' -> ');
|
||||
const chain = Array.from(this._resolving).map((t) => t.name).join(' -> ');
|
||||
throw new Error(`Circular dependency detected: ${chain} -> ${type.name}`);
|
||||
}
|
||||
|
||||
@@ -337,7 +337,7 @@ export class ServiceContainer {
|
||||
// 如果有单例实例,调用 dispose
|
||||
if (registration.instance) {
|
||||
// 从可更新列表中移除
|
||||
const index = this._updatableServices.findIndex(item => item.instance === registration.instance);
|
||||
const index = this._updatableServices.findIndex((item) => item.instance === registration.instance);
|
||||
if (index !== -1) {
|
||||
this._updatableServices.splice(index, 1);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { ComponentType, ComponentRegistry } from './ComponentStorage';
|
||||
import { BitMask64Data, BitMask64Utils } from "../Utils";
|
||||
import { BitMaskHashMap } from "../Utils/BitMaskHashMap";
|
||||
import { BitMask64Data, BitMask64Utils } from '../Utils';
|
||||
import { BitMaskHashMap } from '../Utils/BitMaskHashMap';
|
||||
|
||||
/**
|
||||
* 原型标识符
|
||||
@@ -32,7 +32,7 @@ export interface ArchetypeQueryResult {
|
||||
|
||||
/**
|
||||
* Archetype系统
|
||||
*
|
||||
*
|
||||
* 根据实体的组件组合将实体分组到不同的原型中,提供高效的查询性能。
|
||||
*/
|
||||
export class ArchetypeSystem {
|
||||
@@ -50,7 +50,7 @@ export class ArchetypeSystem {
|
||||
|
||||
/** 所有原型 */
|
||||
private _allArchetypes: Archetype[] = [];
|
||||
|
||||
|
||||
/**
|
||||
* 添加实体到原型系统
|
||||
*/
|
||||
@@ -66,7 +66,7 @@ export class ArchetypeSystem {
|
||||
archetype.entities.add(entity);
|
||||
this._entityToArchetype.set(entity, archetype);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从原型系统中移除实体
|
||||
*/
|
||||
@@ -118,7 +118,7 @@ export class ArchetypeSystem {
|
||||
this._entityToArchetype.set(entity, newArchetype);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询包含指定组件组合的原型
|
||||
*
|
||||
@@ -198,14 +198,14 @@ export class ArchetypeSystem {
|
||||
totalEntities
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取实体所属的原型
|
||||
*/
|
||||
public getEntityArchetype(entity: Entity): Archetype | undefined {
|
||||
return this._entityToArchetype.get(entity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取所有原型
|
||||
*/
|
||||
@@ -247,7 +247,7 @@ export class ArchetypeSystem {
|
||||
*/
|
||||
private updateAllArchetypeArrays(): void {
|
||||
this._allArchetypes = [];
|
||||
for (let archetype of this._archetypes.values()) {
|
||||
for (const archetype of this._archetypes.values()) {
|
||||
this._allArchetypes.push(archetype);
|
||||
}
|
||||
}
|
||||
@@ -258,18 +258,18 @@ export class ArchetypeSystem {
|
||||
private getEntityComponentTypes(entity: Entity): ComponentType[] {
|
||||
let componentTypes = this._entityComponentTypesCache.get(entity);
|
||||
if (!componentTypes) {
|
||||
componentTypes = entity.components.map(component => component.constructor as ComponentType);
|
||||
componentTypes = entity.components.map((component) => component.constructor as ComponentType);
|
||||
this._entityComponentTypesCache.set(entity, componentTypes);
|
||||
}
|
||||
return componentTypes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成原型ID
|
||||
* 使用ComponentRegistry确保与Entity.componentMask使用相同的bitIndex
|
||||
*/
|
||||
private generateArchetypeId(componentTypes: ComponentType[]): ArchetypeId {
|
||||
let mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
for (const type of componentTypes) {
|
||||
if (!ComponentRegistry.isRegistered(type)) {
|
||||
ComponentRegistry.register(type);
|
||||
@@ -279,7 +279,7 @@ export class ArchetypeSystem {
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建新原型
|
||||
*/
|
||||
|
||||
@@ -356,4 +356,4 @@ export class ComponentPoolManager {
|
||||
|
||||
return maxSize > 0 ? (used / maxSize * 100) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { ComponentRegistry, ComponentType } from './ComponentStorage/ComponentRe
|
||||
export { ComponentRegistry, ComponentType };
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 高性能组件存储器
|
||||
*/
|
||||
@@ -21,7 +20,7 @@ export class ComponentStorage<T extends Component> {
|
||||
|
||||
constructor(componentType: ComponentType<T>) {
|
||||
this.componentType = componentType;
|
||||
|
||||
|
||||
// 确保组件类型已注册
|
||||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||
ComponentRegistry.register(componentType);
|
||||
@@ -152,7 +151,7 @@ export class ComponentStorage<T extends Component> {
|
||||
usedSlots: number;
|
||||
freeSlots: number;
|
||||
fragmentation: number;
|
||||
} {
|
||||
} {
|
||||
const totalSlots = this.dense.length;
|
||||
const usedSlots = this.dense.length;
|
||||
const freeSlots = 0; // 永远无空洞
|
||||
@@ -342,15 +341,15 @@ export class ComponentStorageManager {
|
||||
* @returns 组件位掩码
|
||||
*/
|
||||
public getComponentMask(entityId: number): BitMask64Data {
|
||||
let mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
|
||||
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
|
||||
for (const [componentType, storage] of this.storages.entries()) {
|
||||
if (storage.hasComponent(entityId)) {
|
||||
const componentMask = ComponentRegistry.getBitMask(componentType as ComponentType);
|
||||
BitMask64Utils.orInPlace(mask, componentMask);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
@@ -360,12 +359,12 @@ export class ComponentStorageManager {
|
||||
*/
|
||||
public getAllStats(): Map<string, { totalSlots: number; usedSlots: number; freeSlots: number; fragmentation: number }> {
|
||||
const stats = new Map<string, { totalSlots: number; usedSlots: number; freeSlots: number; fragmentation: number }>();
|
||||
|
||||
|
||||
for (const [componentType, storage] of this.storages.entries()) {
|
||||
const typeName = getComponentTypeName(componentType as ComponentType);
|
||||
stats.set(typeName, storage.getStats());
|
||||
}
|
||||
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
@@ -378,4 +377,4 @@ export class ComponentStorageManager {
|
||||
}
|
||||
this.storages.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ export class ComponentRegistry {
|
||||
*/
|
||||
public static createSingleComponentMask(componentName: string): BitMask64Data {
|
||||
const cacheKey = `single:${componentName}`;
|
||||
|
||||
|
||||
if (this.maskCache.has(cacheKey)) {
|
||||
return this.maskCache.get(cacheKey)!;
|
||||
}
|
||||
@@ -176,12 +176,12 @@ export class ComponentRegistry {
|
||||
public static createComponentMask(componentNames: string[]): BitMask64Data {
|
||||
const sortedNames = [...componentNames].sort();
|
||||
const cacheKey = `multi:${sortedNames.join(',')}`;
|
||||
|
||||
|
||||
if (this.maskCache.has(cacheKey)) {
|
||||
return this.maskCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
let mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
for (const name of componentNames) {
|
||||
const componentId = this.getComponentId(name);
|
||||
if (componentId !== undefined) {
|
||||
@@ -212,4 +212,4 @@ export class ComponentRegistry {
|
||||
this.maskCache.clear();
|
||||
this.nextBitIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { EventBus, GlobalEventBus } from '../EventBus';
|
||||
export { TypeSafeEventSystem, EventListenerConfig, EventStats } from '../EventSystem';
|
||||
export { TypeSafeEventSystem, EventListenerConfig, EventStats } from '../EventSystem';
|
||||
|
||||
@@ -8,4 +8,4 @@ export {
|
||||
createECSAPI,
|
||||
initializeECS,
|
||||
ECS
|
||||
} from './FluentAPI/index';
|
||||
} from './FluentAPI/index';
|
||||
|
||||
@@ -52,4 +52,4 @@ export class ComponentBuilder<T extends Component> {
|
||||
public build(): T {
|
||||
return this.component;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export class ECSFluentAPI {
|
||||
* @returns 组件构建器
|
||||
*/
|
||||
public createComponent<T extends Component>(
|
||||
componentClass: new (...args: unknown[]) => T,
|
||||
componentClass: new (...args: unknown[]) => T,
|
||||
...args: unknown[]
|
||||
): ComponentBuilder<T> {
|
||||
return new ComponentBuilder(componentClass, ...args);
|
||||
@@ -164,7 +164,7 @@ export class ECSFluentAPI {
|
||||
componentStats: Map<string, unknown>;
|
||||
queryStats: unknown;
|
||||
eventStats: Map<string, unknown>;
|
||||
} {
|
||||
} {
|
||||
return {
|
||||
entityCount: this.scene.entities.count,
|
||||
systemCount: this.scene.systems.length,
|
||||
@@ -183,8 +183,8 @@ export class ECSFluentAPI {
|
||||
* @returns ECS流式API实例
|
||||
*/
|
||||
export function createECSAPI(
|
||||
scene: IScene,
|
||||
querySystem: QuerySystem,
|
||||
scene: IScene,
|
||||
querySystem: QuerySystem,
|
||||
eventSystem: TypeSafeEventSystem
|
||||
): ECSFluentAPI {
|
||||
return new ECSFluentAPI(scene, querySystem, eventSystem);
|
||||
@@ -202,9 +202,9 @@ export let ECS: ECSFluentAPI;
|
||||
* @param eventSystem 事件系统
|
||||
*/
|
||||
export function initializeECS(
|
||||
scene: IScene,
|
||||
querySystem: QuerySystem,
|
||||
scene: IScene,
|
||||
querySystem: QuerySystem,
|
||||
eventSystem: TypeSafeEventSystem
|
||||
): void {
|
||||
ECS = createECSAPI(scene, querySystem, eventSystem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,4 +95,4 @@ export class EntityBatchOperator {
|
||||
public count(): number {
|
||||
return this.entities.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export class EntityBuilder {
|
||||
this.scene = scene;
|
||||
this.storageManager = storageManager;
|
||||
const id = scene.identifierPool.checkOut();
|
||||
this.entity = new Entity("", id);
|
||||
this.entity = new Entity('', id);
|
||||
this.entity.scene = this.scene as any;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ export class EntityBuilder {
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public configure<T extends Component>(
|
||||
componentType: ComponentType<T>,
|
||||
componentType: ComponentType<T>,
|
||||
configurator: (component: T) => void
|
||||
): EntityBuilder {
|
||||
const component = this.entity.getComponent(componentType);
|
||||
@@ -199,4 +199,4 @@ export class EntityBuilder {
|
||||
newBuilder.entity = this.entity; // 实际应该是深度克隆
|
||||
return newBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,4 +87,4 @@ export class SceneBuilder {
|
||||
public build(): Scene {
|
||||
return this.scene;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ export { EntityBuilder } from './EntityBuilder';
|
||||
export { SceneBuilder } from './SceneBuilder';
|
||||
export { ComponentBuilder } from './ComponentBuilder';
|
||||
export { EntityBatchOperator } from './EntityBatchOperator';
|
||||
export { ECSFluentAPI, createECSAPI, initializeECS, ECS } from './ECSFluentAPI';
|
||||
export { ECSFluentAPI, createECSAPI, initializeECS, ECS } from './ECSFluentAPI';
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export { QuerySystem } from '../QuerySystem';
|
||||
export { ECSFluentAPI, createECSAPI } from '../FluentAPI';
|
||||
|
||||
@@ -882,7 +882,7 @@ export class QuerySystem {
|
||||
size: number;
|
||||
hitRate: string;
|
||||
};
|
||||
} {
|
||||
} {
|
||||
return {
|
||||
entityCount: this._entities.length,
|
||||
indexStats: {
|
||||
|
||||
@@ -136,7 +136,7 @@ export class ReactiveQuery {
|
||||
private generateQueryId(): string {
|
||||
const typeStr = this._condition.type;
|
||||
const componentsStr = this._condition.componentTypes
|
||||
.map(t => t.name)
|
||||
.map((t) => t.name)
|
||||
.sort()
|
||||
.join(',');
|
||||
return `${typeStr}:${componentsStr}`;
|
||||
|
||||
@@ -345,12 +345,12 @@ export class SoAStorage<T extends Component> {
|
||||
private _size = 0;
|
||||
private _capacity = 1000;
|
||||
public readonly type: ComponentType<T>;
|
||||
|
||||
|
||||
constructor(componentType: ComponentType<T>) {
|
||||
this.type = componentType;
|
||||
this.initializeFields(componentType);
|
||||
}
|
||||
|
||||
|
||||
private initializeFields(componentType: ComponentType<T>): void {
|
||||
const instance = new componentType();
|
||||
const highPrecisionFields = (componentType as any).__highPrecisionFields || new Set();
|
||||
@@ -368,12 +368,12 @@ export class SoAStorage<T extends Component> {
|
||||
const serializeSetFields = (componentType as any).__serializeSetFields || new Set();
|
||||
const serializeArrayFields = (componentType as any).__serializeArrayFields || new Set();
|
||||
// const deepCopyFields = (componentType as any).__deepCopyFields || new Set(); // 未使用,但保留供future使用
|
||||
|
||||
|
||||
for (const key in instance) {
|
||||
if (instance.hasOwnProperty(key) && key !== 'id') {
|
||||
const value = (instance as any)[key];
|
||||
const type = typeof value;
|
||||
|
||||
|
||||
if (type === 'number') {
|
||||
if (highPrecisionFields.has(key)) {
|
||||
// 标记为高精度,作为复杂对象处理
|
||||
@@ -438,14 +438,14 @@ export class SoAStorage<T extends Component> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public addComponent(entityId: number, component: T): void {
|
||||
if (this.entityToIndex.has(entityId)) {
|
||||
const index = this.entityToIndex.get(entityId)!;
|
||||
this.updateComponentAtIndex(index, component);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let index: number;
|
||||
if (this.freeIndices.length > 0) {
|
||||
index = this.freeIndices.pop()!;
|
||||
@@ -455,13 +455,13 @@ export class SoAStorage<T extends Component> {
|
||||
this.resize(this._capacity * 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.entityToIndex.set(entityId, index);
|
||||
this.indexToEntity[index] = entityId;
|
||||
this.updateComponentAtIndex(index, component);
|
||||
this._size++;
|
||||
}
|
||||
|
||||
|
||||
private updateComponentAtIndex(index: number, component: T): void {
|
||||
const entityId = this.indexToEntity[index]!;
|
||||
const complexFieldMap = new Map<string, any>();
|
||||
@@ -470,13 +470,13 @@ export class SoAStorage<T extends Component> {
|
||||
const serializeSetFields = (this.type as any).__serializeSetFields || new Set();
|
||||
const serializeArrayFields = (this.type as any).__serializeArrayFields || new Set();
|
||||
const deepCopyFields = (this.type as any).__deepCopyFields || new Set();
|
||||
|
||||
|
||||
// 处理所有字段
|
||||
for (const key in component) {
|
||||
if (component.hasOwnProperty(key) && key !== 'id') {
|
||||
const value = (component as any)[key];
|
||||
const type = typeof value;
|
||||
|
||||
|
||||
if (type === 'number') {
|
||||
if (highPrecisionFields.has(key) || !this.fields.has(key)) {
|
||||
// 标记为高精度或未在TypedArray中的数值作为复杂对象存储
|
||||
@@ -487,7 +487,7 @@ export class SoAStorage<T extends Component> {
|
||||
array[index] = value;
|
||||
}
|
||||
} else if (type === 'boolean' && this.fields.has(key)) {
|
||||
// 布尔值存储到TypedArray
|
||||
// 布尔值存储到TypedArray
|
||||
const array = this.fields.get(key)!;
|
||||
array[index] = value ? 1 : 0;
|
||||
} else if (this.stringFields.has(key)) {
|
||||
@@ -509,13 +509,13 @@ export class SoAStorage<T extends Component> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 存储复杂字段
|
||||
if (complexFieldMap.size > 0) {
|
||||
this.complexFields.set(entityId, complexFieldMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 序列化值为JSON字符串
|
||||
*/
|
||||
@@ -539,14 +539,14 @@ export class SoAStorage<T extends Component> {
|
||||
return '{}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 反序列化JSON字符串为值
|
||||
*/
|
||||
private deserializeValue(serialized: string, key: string, mapFields: Set<string>, setFields: Set<string>, arrayFields: Set<string>): any {
|
||||
try {
|
||||
const parsed = JSON.parse(serialized);
|
||||
|
||||
|
||||
if (mapFields.has(key)) {
|
||||
// 恢复Map
|
||||
return new Map(parsed);
|
||||
@@ -564,7 +564,7 @@ export class SoAStorage<T extends Component> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 深拷贝对象
|
||||
*/
|
||||
@@ -572,15 +572,15 @@ export class SoAStorage<T extends Component> {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime());
|
||||
}
|
||||
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(item => this.deepClone(item));
|
||||
return obj.map((item) => this.deepClone(item));
|
||||
}
|
||||
|
||||
|
||||
if (obj instanceof Map) {
|
||||
const cloned = new Map();
|
||||
for (const [key, value] of obj.entries()) {
|
||||
@@ -588,7 +588,7 @@ export class SoAStorage<T extends Component> {
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
|
||||
if (obj instanceof Set) {
|
||||
const cloned = new Set();
|
||||
for (const value of obj.values()) {
|
||||
@@ -596,7 +596,7 @@ export class SoAStorage<T extends Component> {
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
|
||||
// 普通对象
|
||||
const cloned: any = {};
|
||||
for (const key in obj) {
|
||||
@@ -606,36 +606,36 @@ export class SoAStorage<T extends Component> {
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
|
||||
public getComponent(entityId: number): T | null {
|
||||
const index = this.entityToIndex.get(entityId);
|
||||
if (index === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// 创建真正的组件实例以保持兼容性
|
||||
const component = new this.type() as any;
|
||||
const serializeMapFields = (this.type as any).__serializeMapFields || new Set();
|
||||
const serializeSetFields = (this.type as any).__serializeSetFields || new Set();
|
||||
const serializeArrayFields = (this.type as any).__serializeArrayFields || new Set();
|
||||
|
||||
|
||||
// 恢复数值字段
|
||||
for (const [fieldName, array] of this.fields.entries()) {
|
||||
const value = array[index];
|
||||
const fieldType = this.getFieldType(fieldName);
|
||||
|
||||
|
||||
if (fieldType === 'boolean') {
|
||||
component[fieldName] = value === 1;
|
||||
} else {
|
||||
component[fieldName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 恢复字符串字段
|
||||
for (const [fieldName, stringArray] of this.stringFields.entries()) {
|
||||
component[fieldName] = stringArray[index];
|
||||
}
|
||||
|
||||
|
||||
// 恢复序列化字段
|
||||
for (const [fieldName, serializedArray] of this.serializedFields.entries()) {
|
||||
const serialized = serializedArray[index];
|
||||
@@ -643,7 +643,7 @@ export class SoAStorage<T extends Component> {
|
||||
component[fieldName] = this.deserializeValue(serialized, fieldName, serializeMapFields, serializeSetFields, serializeArrayFields);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 恢复复杂字段
|
||||
const complexFieldMap = this.complexFields.get(entityId);
|
||||
if (complexFieldMap) {
|
||||
@@ -651,39 +651,39 @@ export class SoAStorage<T extends Component> {
|
||||
component[fieldName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return component as T;
|
||||
}
|
||||
|
||||
|
||||
private getFieldType(fieldName: string): string {
|
||||
// 通过创建临时实例检查字段类型
|
||||
const tempInstance = new this.type();
|
||||
const value = (tempInstance as any)[fieldName];
|
||||
return typeof value;
|
||||
}
|
||||
|
||||
|
||||
public hasComponent(entityId: number): boolean {
|
||||
return this.entityToIndex.has(entityId);
|
||||
}
|
||||
|
||||
|
||||
public removeComponent(entityId: number): T | null {
|
||||
const index = this.entityToIndex.get(entityId);
|
||||
if (index === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// 获取组件副本以便返回
|
||||
const component = this.getComponent(entityId);
|
||||
|
||||
|
||||
// 清理复杂字段
|
||||
this.complexFields.delete(entityId);
|
||||
|
||||
|
||||
this.entityToIndex.delete(entityId);
|
||||
this.freeIndices.push(index);
|
||||
this._size--;
|
||||
return component;
|
||||
}
|
||||
|
||||
|
||||
private resize(newCapacity: number): void {
|
||||
// 调整数值字段的TypedArray
|
||||
for (const [fieldName, oldArray] of this.fields.entries()) {
|
||||
@@ -716,7 +716,7 @@ export class SoAStorage<T extends Component> {
|
||||
newArray.set(oldArray);
|
||||
this.fields.set(fieldName, newArray);
|
||||
}
|
||||
|
||||
|
||||
// 调整字符串字段的数组
|
||||
for (const [fieldName, oldArray] of this.stringFields.entries()) {
|
||||
const newArray = new Array(newCapacity);
|
||||
@@ -725,7 +725,7 @@ export class SoAStorage<T extends Component> {
|
||||
}
|
||||
this.stringFields.set(fieldName, newArray);
|
||||
}
|
||||
|
||||
|
||||
// 调整序列化字段的数组
|
||||
for (const [fieldName, oldArray] of this.serializedFields.entries()) {
|
||||
const newArray = new Array(newCapacity);
|
||||
@@ -734,14 +734,14 @@ export class SoAStorage<T extends Component> {
|
||||
}
|
||||
this.serializedFields.set(fieldName, newArray);
|
||||
}
|
||||
|
||||
|
||||
this._capacity = newCapacity;
|
||||
}
|
||||
|
||||
|
||||
public getActiveIndices(): number[] {
|
||||
return Array.from(this.entityToIndex.values());
|
||||
}
|
||||
|
||||
|
||||
public getFieldArray(fieldName: string): SupportedTypedArray | null {
|
||||
return this.fields.get(fieldName) || null;
|
||||
}
|
||||
@@ -749,38 +749,38 @@ export class SoAStorage<T extends Component> {
|
||||
public getTypedFieldArray<K extends keyof T>(fieldName: K): SupportedTypedArray | null {
|
||||
return this.fields.get(String(fieldName)) || null;
|
||||
}
|
||||
|
||||
|
||||
public getEntityIndex(entityId: number): number | undefined {
|
||||
return this.entityToIndex.get(entityId);
|
||||
}
|
||||
|
||||
|
||||
public getEntityIdByIndex(index: number): number | undefined {
|
||||
return this.indexToEntity[index];
|
||||
}
|
||||
|
||||
|
||||
public size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
|
||||
public clear(): void {
|
||||
this.entityToIndex.clear();
|
||||
this.indexToEntity = [];
|
||||
this.freeIndices = [];
|
||||
this.complexFields.clear();
|
||||
this._size = 0;
|
||||
|
||||
|
||||
// 重置数值字段数组
|
||||
for (const array of this.fields.values()) {
|
||||
array.fill(0);
|
||||
}
|
||||
|
||||
|
||||
// 重置字符串字段数组
|
||||
for (const stringArray of this.stringFields.values()) {
|
||||
for (let i = 0; i < stringArray.length; i++) {
|
||||
stringArray[i] = undefined as any;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 重置序列化字段数组
|
||||
for (const serializedArray of this.serializedFields.values()) {
|
||||
for (let i = 0; i < serializedArray.length; i++) {
|
||||
@@ -884,7 +884,7 @@ export class SoAStorage<T extends Component> {
|
||||
bytesPerElement = 4;
|
||||
typeName = 'unknown';
|
||||
}
|
||||
|
||||
|
||||
const memory = array.length * bytesPerElement;
|
||||
totalMemory += memory;
|
||||
|
||||
@@ -914,4 +914,4 @@ export class SoAStorage<T extends Component> {
|
||||
const activeIndices = this.getActiveIndices();
|
||||
operation(this.fields, activeIndices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { ComponentPool, ComponentPoolManager } from '../ComponentPool';
|
||||
export { ComponentStorage, ComponentRegistry } from '../ComponentStorage';
|
||||
export { EnableSoA, HighPrecision, Float64, Float32, Int32, SerializeMap, SoAStorage } from '../SoAStorage';
|
||||
export { EnableSoA, HighPrecision, Float64, Float32, Int32, SerializeMap, SoAStorage } from '../SoAStorage';
|
||||
|
||||
@@ -46,4 +46,4 @@ export {
|
||||
|
||||
// 类型定义
|
||||
SupportedTypedArray
|
||||
} from './SoAStorage';
|
||||
} from './SoAStorage';
|
||||
|
||||
@@ -13,14 +13,14 @@ export enum ECSEventType {
|
||||
ENTITY_TAG_ADDED = 'entity:tag:added',
|
||||
ENTITY_TAG_REMOVED = 'entity:tag:removed',
|
||||
ENTITY_NAME_CHANGED = 'entity:name:changed',
|
||||
|
||||
|
||||
// 组件相关事件
|
||||
COMPONENT_ADDED = 'component:added',
|
||||
COMPONENT_REMOVED = 'component:removed',
|
||||
COMPONENT_MODIFIED = 'component:modified',
|
||||
COMPONENT_ENABLED = 'component:enabled',
|
||||
COMPONENT_DISABLED = 'component:disabled',
|
||||
|
||||
|
||||
// 系统相关事件
|
||||
SYSTEM_ADDED = 'system:added',
|
||||
SYSTEM_REMOVED = 'system:removed',
|
||||
@@ -29,7 +29,7 @@ export enum ECSEventType {
|
||||
SYSTEM_PROCESSING_START = 'system:processing:start',
|
||||
SYSTEM_PROCESSING_END = 'system:processing:end',
|
||||
SYSTEM_ERROR = 'system:error',
|
||||
|
||||
|
||||
// 场景相关事件
|
||||
SCENE_CREATED = 'scene:created',
|
||||
SCENE_DESTROYED = 'scene:destroyed',
|
||||
@@ -37,41 +37,41 @@ export enum ECSEventType {
|
||||
SCENE_DEACTIVATED = 'scene:deactivated',
|
||||
SCENE_PAUSED = 'scene:paused',
|
||||
SCENE_RESUMED = 'scene:resumed',
|
||||
|
||||
|
||||
// 查询相关事件
|
||||
QUERY_EXECUTED = 'query:executed',
|
||||
QUERY_CACHE_HIT = 'query:cache:hit',
|
||||
QUERY_CACHE_MISS = 'query:cache:miss',
|
||||
QUERY_OPTIMIZED = 'query:optimized',
|
||||
|
||||
|
||||
// 性能相关事件
|
||||
PERFORMANCE_WARNING = 'performance:warning',
|
||||
PERFORMANCE_CRITICAL = 'performance:critical',
|
||||
MEMORY_USAGE_HIGH = 'memory:usage:high',
|
||||
FRAME_RATE_DROP = 'frame:rate:drop',
|
||||
|
||||
|
||||
// 索引相关事件
|
||||
INDEX_CREATED = 'index:created',
|
||||
INDEX_UPDATED = 'index:updated',
|
||||
INDEX_OPTIMIZED = 'index:optimized',
|
||||
|
||||
|
||||
// Archetype相关事件
|
||||
ARCHETYPE_CREATED = 'archetype:created',
|
||||
ARCHETYPE_ENTITY_ADDED = 'archetype:entity:added',
|
||||
ARCHETYPE_ENTITY_REMOVED = 'archetype:entity:removed',
|
||||
|
||||
|
||||
// 脏标记相关事件
|
||||
DIRTY_MARK_ADDED = 'dirty:mark:added',
|
||||
DIRTY_BATCH_PROCESSED = 'dirty:batch:processed',
|
||||
|
||||
|
||||
// 错误和警告事件
|
||||
ERROR_OCCURRED = 'error:occurred',
|
||||
WARNING_ISSUED = 'warning:issued',
|
||||
|
||||
|
||||
// 生命周期事件
|
||||
FRAMEWORK_INITIALIZED = 'framework:initialized',
|
||||
FRAMEWORK_SHUTDOWN = 'framework:shutdown',
|
||||
|
||||
|
||||
// 调试相关事件
|
||||
DEBUG_INFO = 'debug:info',
|
||||
DEBUG_STATS_UPDATED = 'debug:stats:updated'
|
||||
@@ -105,7 +105,7 @@ export const EVENT_TYPES = {
|
||||
TAG_REMOVED: ECSEventType.ENTITY_TAG_REMOVED,
|
||||
NAME_CHANGED: ECSEventType.ENTITY_NAME_CHANGED
|
||||
},
|
||||
|
||||
|
||||
// 组件事件
|
||||
COMPONENT: {
|
||||
ADDED: ECSEventType.COMPONENT_ADDED,
|
||||
@@ -114,7 +114,7 @@ export const EVENT_TYPES = {
|
||||
ENABLED: ECSEventType.COMPONENT_ENABLED,
|
||||
DISABLED: ECSEventType.COMPONENT_DISABLED
|
||||
},
|
||||
|
||||
|
||||
// 系统事件
|
||||
SYSTEM: {
|
||||
ADDED: ECSEventType.SYSTEM_ADDED,
|
||||
@@ -125,7 +125,7 @@ export const EVENT_TYPES = {
|
||||
PROCESSING_END: ECSEventType.SYSTEM_PROCESSING_END,
|
||||
ERROR: ECSEventType.SYSTEM_ERROR
|
||||
},
|
||||
|
||||
|
||||
// 性能事件
|
||||
PERFORMANCE: {
|
||||
WARNING: ECSEventType.PERFORMANCE_WARNING,
|
||||
@@ -147,7 +147,7 @@ export class EventTypeValidator {
|
||||
...Object.values(EVENT_TYPES.SYSTEM),
|
||||
...Object.values(EVENT_TYPES.PERFORMANCE)
|
||||
]);
|
||||
|
||||
|
||||
/**
|
||||
* 验证事件类型是否有效
|
||||
* @param eventType 事件类型
|
||||
@@ -156,7 +156,7 @@ export class EventTypeValidator {
|
||||
public static isValid(eventType: string): boolean {
|
||||
return this.validTypes.has(eventType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取所有有效的事件类型
|
||||
* @returns 事件类型数组
|
||||
@@ -164,7 +164,7 @@ export class EventTypeValidator {
|
||||
public static getAllValidTypes(): string[] {
|
||||
return Array.from(this.validTypes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加自定义事件类型
|
||||
* @param eventType 事件类型
|
||||
@@ -172,7 +172,7 @@ export class EventTypeValidator {
|
||||
public static addCustomType(eventType: string): void {
|
||||
this.validTypes.add(eventType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 移除自定义事件类型
|
||||
* @param eventType 事件类型
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type {Component} from '../Component';
|
||||
import type {EntitySystem} from '../Systems';
|
||||
import {ComponentType} from "../../Types";
|
||||
import type { Component } from '../Component';
|
||||
import type { EntitySystem } from '../Systems';
|
||||
import { ComponentType } from '../../Types';
|
||||
|
||||
/**
|
||||
* 存储组件类型名称的Symbol键
|
||||
@@ -15,7 +15,7 @@ export const SYSTEM_TYPE_NAME = Symbol('SystemTypeName');
|
||||
/**
|
||||
* 组件类型装饰器
|
||||
* 用于为组件类指定固定的类型名称,避免在代码混淆后失效
|
||||
*
|
||||
*
|
||||
* @param typeName 组件类型名称
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -31,10 +31,10 @@ export function ECSComponent(typeName: string) {
|
||||
if (!typeName || typeof typeName !== 'string') {
|
||||
throw new Error('ECSComponent装饰器必须提供有效的类型名称');
|
||||
}
|
||||
|
||||
|
||||
// 在构造函数上存储类型名称
|
||||
(target as any)[COMPONENT_TYPE_NAME] = typeName;
|
||||
|
||||
|
||||
return target;
|
||||
};
|
||||
}
|
||||
@@ -107,7 +107,7 @@ export function getSystemMetadata(systemType: new (...args: any[]) => EntitySyst
|
||||
|
||||
/**
|
||||
* 获取组件类型的名称,优先使用装饰器指定的名称
|
||||
*
|
||||
*
|
||||
* @param componentType 组件构造函数
|
||||
* @returns 组件类型名称
|
||||
*/
|
||||
@@ -119,14 +119,14 @@ export function getComponentTypeName(
|
||||
if (decoratorName) {
|
||||
return decoratorName;
|
||||
}
|
||||
|
||||
|
||||
// 回退到constructor.name
|
||||
return componentType.name || 'UnknownComponent';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统类型的名称,优先使用装饰器指定的名称
|
||||
*
|
||||
*
|
||||
* @param systemType 系统构造函数
|
||||
* @returns 系统类型名称
|
||||
*/
|
||||
@@ -138,14 +138,14 @@ export function getSystemTypeName<T extends EntitySystem>(
|
||||
if (decoratorName) {
|
||||
return decoratorName;
|
||||
}
|
||||
|
||||
|
||||
// 回退到constructor.name
|
||||
return systemType.name || 'UnknownSystem';
|
||||
}
|
||||
|
||||
/**
|
||||
* 从组件实例获取类型名称
|
||||
*
|
||||
*
|
||||
* @param component 组件实例
|
||||
* @returns 组件类型名称
|
||||
*/
|
||||
@@ -155,7 +155,7 @@ export function getComponentInstanceTypeName(component: Component): string {
|
||||
|
||||
/**
|
||||
* 从系统实例获取类型名称
|
||||
*
|
||||
*
|
||||
* @param system 系统实例
|
||||
* @returns 系统类型名称
|
||||
*/
|
||||
|
||||
@@ -19,4 +19,4 @@ export {
|
||||
ENTITY_REF_METADATA
|
||||
} from './EntityRefDecorator';
|
||||
|
||||
export type { EntityRefMetadata } from './EntityRefDecorator';
|
||||
export type { EntityRefMetadata } from './EntityRefDecorator';
|
||||
|
||||
@@ -50,12 +50,12 @@ export interface IScene {
|
||||
* 标识符池
|
||||
*/
|
||||
readonly identifierPool: IdentifierPool;
|
||||
|
||||
|
||||
/**
|
||||
* 组件存储管理器
|
||||
*/
|
||||
readonly componentStorageManager: ComponentStorageManager;
|
||||
|
||||
|
||||
/**
|
||||
* 查询系统
|
||||
*/
|
||||
@@ -316,4 +316,4 @@ export interface ISceneConfig {
|
||||
* 场景名称
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ export class ComponentSerializer {
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => this.serializeValue(item));
|
||||
return value.map((item) => this.serializeValue(item));
|
||||
}
|
||||
|
||||
// Map (如果没有使用@SerializeMap装饰器)
|
||||
@@ -276,7 +276,7 @@ export class ComponentSerializer {
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => this.deserializeValue(item));
|
||||
return value.map((item) => this.deserializeValue(item));
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
@@ -340,10 +340,10 @@ export class ComponentSerializer {
|
||||
return {
|
||||
type: metadata.options.typeId || getComponentTypeName(componentType),
|
||||
version: metadata.options.version,
|
||||
fields: Array.from(metadata.fields.keys()).map(k =>
|
||||
fields: Array.from(metadata.fields.keys()).map((k) =>
|
||||
typeof k === 'symbol' ? k.toString() : k
|
||||
),
|
||||
ignoredFields: Array.from(metadata.ignoredFields).map(k =>
|
||||
ignoredFields: Array.from(metadata.ignoredFields).map((k) =>
|
||||
typeof k === 'symbol' ? k.toString() : k
|
||||
),
|
||||
isSerializable: true
|
||||
|
||||
@@ -696,22 +696,22 @@ export class IncrementalSerializer {
|
||||
componentChanges: incremental.componentChanges.length,
|
||||
sceneDataChanges: incremental.sceneDataChanges.length,
|
||||
addedEntities: incremental.entityChanges.filter(
|
||||
c => c.operation === ChangeOperation.EntityAdded
|
||||
(c) => c.operation === ChangeOperation.EntityAdded
|
||||
).length,
|
||||
removedEntities: incremental.entityChanges.filter(
|
||||
c => c.operation === ChangeOperation.EntityRemoved
|
||||
(c) => c.operation === ChangeOperation.EntityRemoved
|
||||
).length,
|
||||
updatedEntities: incremental.entityChanges.filter(
|
||||
c => c.operation === ChangeOperation.EntityUpdated
|
||||
(c) => c.operation === ChangeOperation.EntityUpdated
|
||||
).length,
|
||||
addedComponents: incremental.componentChanges.filter(
|
||||
c => c.operation === ChangeOperation.ComponentAdded
|
||||
(c) => c.operation === ChangeOperation.ComponentAdded
|
||||
).length,
|
||||
removedComponents: incremental.componentChanges.filter(
|
||||
c => c.operation === ChangeOperation.ComponentRemoved
|
||||
(c) => c.operation === ChangeOperation.ComponentRemoved
|
||||
).length,
|
||||
updatedComponents: incremental.componentChanges.filter(
|
||||
c => c.operation === ChangeOperation.ComponentUpdated
|
||||
(c) => c.operation === ChangeOperation.ComponentUpdated
|
||||
).length
|
||||
};
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ export class SceneSerializer {
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => this.serializeValue(item));
|
||||
return value.map((item) => this.serializeValue(item));
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
@@ -409,7 +409,7 @@ export class SceneSerializer {
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => this.deserializeValue(item));
|
||||
return value.map((item) => this.deserializeValue(item));
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
@@ -437,8 +437,8 @@ export class SceneSerializer {
|
||||
const componentTypeSet = new Set(options.components);
|
||||
|
||||
// 只返回拥有指定组件的实体
|
||||
return entities.filter(entity => {
|
||||
return Array.from(entity.components).some(component =>
|
||||
return entities.filter((entity) => {
|
||||
return Array.from(entity.components).some((component) =>
|
||||
componentTypeSet.has(component.constructor as ComponentType)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -127,7 +127,7 @@ export class VersionMigrationManager {
|
||||
return component;
|
||||
}
|
||||
|
||||
let migratedData = { ...component };
|
||||
const migratedData = { ...component };
|
||||
let version = currentVersion;
|
||||
|
||||
// 执行迁移链
|
||||
@@ -193,12 +193,12 @@ export class VersionMigrationManager {
|
||||
private static migrateSceneComponents(scene: SerializedScene): SerializedScene {
|
||||
const migratedScene = { ...scene };
|
||||
|
||||
migratedScene.entities = scene.entities.map(entity => ({
|
||||
migratedScene.entities = scene.entities.map((entity) => ({
|
||||
...entity,
|
||||
components: entity.components.map(component => {
|
||||
components: entity.components.map((component) => {
|
||||
// 查找组件的目标版本
|
||||
const typeInfo = scene.componentTypeRegistry.find(
|
||||
t => t.typeName === component.type
|
||||
(t) => t.typeName === component.type
|
||||
);
|
||||
|
||||
if (typeInfo && typeInfo.version !== component.version) {
|
||||
@@ -220,10 +220,10 @@ export class VersionMigrationManager {
|
||||
entities: any[],
|
||||
typeRegistry: Array<{ typeName: string; version: number }>
|
||||
): any[] {
|
||||
return entities.map(entity => ({
|
||||
return entities.map((entity) => ({
|
||||
...entity,
|
||||
components: entity.components.map((component: SerializedComponent) => {
|
||||
const typeInfo = typeRegistry.find(t => t.typeName === component.type);
|
||||
const typeInfo = typeRegistry.find((t) => t.typeName === component.type);
|
||||
|
||||
if (typeInfo && typeInfo.version !== component.version) {
|
||||
return this.migrateComponent(component, typeInfo.version);
|
||||
|
||||
@@ -154,7 +154,7 @@ export class EntityCache {
|
||||
trackedCount: number;
|
||||
frameEntityCount: number;
|
||||
persistentEntityCount: number;
|
||||
} {
|
||||
} {
|
||||
return {
|
||||
hasFrame: this._frameCache !== null,
|
||||
hasPersistent: this._persistentCache !== null,
|
||||
|
||||
@@ -55,4 +55,4 @@ export abstract class IntervalSystem extends EntitySystem {
|
||||
protected getIntervalDelta(): number {
|
||||
return this.interval + this.intervalRemainder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Matcher } from '../Utils/Matcher';
|
||||
* 被动的实体系统不会对实体进行任何修改,只会被动地接收实体的变化事件
|
||||
*/
|
||||
export abstract class PassiveSystem extends EntitySystem {
|
||||
|
||||
|
||||
constructor(matcher?: Matcher) {
|
||||
super(matcher);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Matcher } from '../Utils/Matcher';
|
||||
* 子类需要实现processSystem方法,用于实现具体的处理逻辑
|
||||
*/
|
||||
export abstract class ProcessingSystem extends EntitySystem {
|
||||
|
||||
|
||||
constructor(matcher?: Matcher) {
|
||||
super(matcher);
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
protected sharedBuffer: SharedArrayBuffer | null = null;
|
||||
protected sharedFloatArray: Float32Array | null = null;
|
||||
private platformAdapter: IPlatformAdapter;
|
||||
private hasLoggedSyncMode = false;
|
||||
private hasLoggedSyncMode = false;
|
||||
|
||||
constructor(matcher?: Matcher, config: WorkerSystemConfig = {}) {
|
||||
super(matcher);
|
||||
@@ -414,7 +414,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
${sharedProcessFunctionBody}
|
||||
};
|
||||
userProcessFunction(sharedFloatArray, startIndex, endIndex, deltaTime, systemConfig);
|
||||
` : ``}
|
||||
` : ''}
|
||||
}
|
||||
`;
|
||||
}
|
||||
@@ -494,7 +494,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
const deltaTime = Time.deltaTime;
|
||||
|
||||
// 3. Worker执行阶段
|
||||
const promises = batches.map(batch =>
|
||||
const promises = batches.map((batch) =>
|
||||
this.workerPool!.execute({
|
||||
entities: batch,
|
||||
deltaTime,
|
||||
@@ -525,7 +525,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
*/
|
||||
private processSynchronously(entities: readonly Entity[]): void {
|
||||
// 1. 数据提取阶段
|
||||
const entityData = entities.map(entity => this.extractEntityData(entity));
|
||||
const entityData = entities.map((entity) => this.extractEntityData(entity));
|
||||
|
||||
// 2. 主线程处理阶段
|
||||
const deltaTime = Time.deltaTime;
|
||||
@@ -534,7 +534,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
// 3. 结果应用阶段
|
||||
// 处理Promise返回值
|
||||
if (results && typeof (results as any).then === 'function') {
|
||||
(results as Promise<TEntityData[]>).then(finalResults => {
|
||||
(results as Promise<TEntityData[]>).then((finalResults) => {
|
||||
entities.forEach((entity, index) => {
|
||||
this.applyResult(entity, finalResults[index]!);
|
||||
});
|
||||
@@ -813,7 +813,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
sharedArrayBufferSupported: boolean;
|
||||
sharedArrayBufferEnabled: boolean;
|
||||
currentMode: 'shared-buffer' | 'worker' | 'sync';
|
||||
} {
|
||||
} {
|
||||
let currentMode: 'shared-buffer' | 'worker' | 'sync' = 'sync';
|
||||
|
||||
if (this.config.enableWorker && this.workerPool) {
|
||||
|
||||
@@ -10,4 +10,4 @@ export type {
|
||||
WorkerProcessFunction,
|
||||
WorkerSystemConfig,
|
||||
SharedArrayBufferProcessFunction
|
||||
} from './WorkerEntitySystem';
|
||||
} from './WorkerEntitySystem';
|
||||
|
||||
@@ -69,7 +69,7 @@ export class BitMask64Utils {
|
||||
return maskSegments.some((seg, index) => {
|
||||
const bitsSeg = bitsSegments[index];
|
||||
return bitsSeg && ((seg[SegmentPart.LOW] & bitsSeg[SegmentPart.LOW]) !== 0 || (seg[SegmentPart.HIGH] & bitsSeg[SegmentPart.HIGH]) !== 0);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,7 +145,7 @@ export class BitMask64Utils {
|
||||
return baseIsZero;
|
||||
}
|
||||
// 额外检查扩展区域是否都为0
|
||||
return mask.segments.every(seg => seg[SegmentPart.LOW] === 0 && seg[SegmentPart.HIGH] === 0);
|
||||
return mask.segments.every((seg) => seg[SegmentPart.LOW] === 0 && seg[SegmentPart.HIGH] === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +155,7 @@ export class BitMask64Utils {
|
||||
* @returns 如果两个掩码完全相等则返回true
|
||||
*/
|
||||
public static equals(a: BitMask64Data, b: BitMask64Data): boolean {
|
||||
let baseEquals = a.base[SegmentPart.LOW] === b.base[SegmentPart.LOW] && a.base[SegmentPart.HIGH] === b.base[SegmentPart.HIGH];
|
||||
const baseEquals = a.base[SegmentPart.LOW] === b.base[SegmentPart.LOW] && a.base[SegmentPart.HIGH] === b.base[SegmentPart.HIGH];
|
||||
// base不相等,或ab都没有扩展区域位,直接返回base比较结果
|
||||
if(!baseEquals || (!a.segments && !b.segments)) return baseEquals;
|
||||
// 不能假设a,b的segments都存在或长度相同.
|
||||
@@ -355,7 +355,7 @@ export class BitMask64Utils {
|
||||
if(!source.segments || source.segments.length == 0) return;
|
||||
// 没有拓展段,则直接复制数组
|
||||
if(!target.segments){
|
||||
target.segments = source.segments.map(seg => [...seg]);
|
||||
target.segments = source.segments.map((seg) => [...seg]);
|
||||
return;
|
||||
}
|
||||
// source有扩展段,target扩展段不足,则补充长度
|
||||
@@ -382,7 +382,7 @@ export class BitMask64Utils {
|
||||
public static clone(mask: BitMask64Data): BitMask64Data {
|
||||
return {
|
||||
base: mask.base.slice() as BitMask64Segment,
|
||||
...(mask.segments && { segments: mask.segments.map(seg => [...seg] as BitMask64Segment) })
|
||||
...(mask.segments && { segments: mask.segments.map((seg) => [...seg] as BitMask64Segment) })
|
||||
};
|
||||
}
|
||||
|
||||
@@ -414,8 +414,8 @@ export class BitMask64Utils {
|
||||
for (let i = -1; i < totalLength; i++) {
|
||||
let segResult = '';
|
||||
const bitMaskData = i == -1 ? mask.base : mask.segments![i]!;
|
||||
let hi = bitMaskData[SegmentPart.HIGH];
|
||||
let lo = bitMaskData[SegmentPart.LOW];
|
||||
const hi = bitMaskData[SegmentPart.HIGH];
|
||||
const lo = bitMaskData[SegmentPart.LOW];
|
||||
if(radix == 2){
|
||||
const hiBits = hi.toString(2).padStart(32, '0');
|
||||
const loBits = lo.toString(2).padStart(32, '0');
|
||||
@@ -493,4 +493,4 @@ export class BitMask64Utils {
|
||||
return segments[targetSegIndex] ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BitMask64Data } from "./BigIntCompatibility";
|
||||
import { BitMask64Data } from './BigIntCompatibility';
|
||||
|
||||
// FlatHashMapFast.ts
|
||||
|
||||
|
||||
@@ -156,10 +156,10 @@ export class Bits {
|
||||
if (maxBits > 64) {
|
||||
maxBits = 64;
|
||||
}
|
||||
|
||||
|
||||
const result = new Bits();
|
||||
BitMask64Utils.copy(this._value, result._value);
|
||||
|
||||
|
||||
if (maxBits <= 32) {
|
||||
const mask = (1 << maxBits) - 1;
|
||||
result._value.base[SegmentPart.LOW] = (~result._value.base[SegmentPart.LOW]) & mask;
|
||||
@@ -174,7 +174,7 @@ export class Bits {
|
||||
result._value.base[SegmentPart.HIGH] = ~result._value.base[SegmentPart.HIGH];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ export class Bits {
|
||||
if (BitMask64Utils.isZero(this._value)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (this._value.base[SegmentPart.HIGH] !== 0) {
|
||||
for (let i = 31; i >= 0; i--) {
|
||||
if ((this._value.base[SegmentPart.HIGH] & (1 << i)) !== 0) {
|
||||
@@ -325,13 +325,13 @@ export class Bits {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (let i = 31; i >= 0; i--) {
|
||||
if ((this._value.base[SegmentPart.LOW] & (1 << i)) !== 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -343,19 +343,19 @@ export class Bits {
|
||||
if (BitMask64Utils.isZero(this._value)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < 32; i++) {
|
||||
if ((this._value.base[SegmentPart.LOW] & (1 << i)) !== 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < 32; i++) {
|
||||
if ((this._value.base[SegmentPart.HIGH] & (1 << i)) !== 0) {
|
||||
return i + 32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ import { IPoolable } from '../../Utils/Pool/IPoolable';
|
||||
|
||||
/**
|
||||
* 可池化的实体集合
|
||||
*
|
||||
*
|
||||
* 实现IPoolable接口,支持对象池复用以减少内存分配开销。
|
||||
*/
|
||||
class PoolableEntitySet extends Set<Entity> implements IPoolable {
|
||||
constructor(..._args: unknown[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
reset(): void {
|
||||
this.clear();
|
||||
}
|
||||
@@ -22,9 +22,9 @@ class PoolableEntitySet extends Set<Entity> implements IPoolable {
|
||||
|
||||
/**
|
||||
* 组件稀疏集合实现
|
||||
*
|
||||
*
|
||||
* 结合通用稀疏集合和组件位掩码
|
||||
*
|
||||
*
|
||||
* 存储结构:
|
||||
* - 稀疏集合存储实体
|
||||
* - 位掩码数组存储组件信息
|
||||
@@ -33,42 +33,42 @@ class PoolableEntitySet extends Set<Entity> implements IPoolable {
|
||||
export class ComponentSparseSet {
|
||||
/**
|
||||
* 实体稀疏集合
|
||||
*
|
||||
*
|
||||
* 存储所有拥有组件的实体,提供O(1)的实体操作。
|
||||
*/
|
||||
private _entities: SparseSet<Entity>;
|
||||
|
||||
|
||||
/**
|
||||
* 组件位掩码数组
|
||||
*
|
||||
*
|
||||
* 与实体稀疏集合的密集数组对应,存储每个实体的组件位掩码。
|
||||
* 数组索引与稀疏集合的密集数组索引一一对应。
|
||||
*/
|
||||
private _componentMasks: BitMask64Data[] = [];
|
||||
|
||||
|
||||
/**
|
||||
* 组件类型到实体集合的映射
|
||||
*
|
||||
*
|
||||
* 维护每个组件类型对应的实体集合,用于快速的单组件查询。
|
||||
*/
|
||||
private _componentToEntities = new Map<ComponentType, PoolableEntitySet>();
|
||||
|
||||
|
||||
/**
|
||||
* 实体集合对象池
|
||||
*
|
||||
*
|
||||
* 使用core库的Pool系统来管理PoolableEntitySet对象的复用。
|
||||
*/
|
||||
private static _entitySetPool = Pool.getPool(PoolableEntitySet, 50, 512);
|
||||
|
||||
|
||||
constructor() {
|
||||
this._entities = new SparseSet<Entity>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加实体到组件索引
|
||||
*
|
||||
*
|
||||
* 分析实体的组件组成,生成位掩码,并更新所有相关索引。
|
||||
*
|
||||
*
|
||||
* @param entity 要添加的实体
|
||||
*/
|
||||
public addEntity(entity: Entity): void {
|
||||
@@ -76,44 +76,44 @@ export class ComponentSparseSet {
|
||||
if (this._entities.has(entity)) {
|
||||
this.removeEntity(entity);
|
||||
}
|
||||
|
||||
let componentMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
|
||||
const componentMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
const entityComponents = new Set<ComponentType>();
|
||||
|
||||
|
||||
// 分析实体组件并构建位掩码
|
||||
for (const component of entity.components) {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
entityComponents.add(componentType);
|
||||
|
||||
|
||||
// 确保组件类型已注册
|
||||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||
ComponentRegistry.register(componentType);
|
||||
}
|
||||
|
||||
|
||||
// 获取组件位掩码并合并
|
||||
const bitMask = ComponentRegistry.getBitMask(componentType);
|
||||
BitMask64Utils.orInPlace(componentMask, bitMask);
|
||||
}
|
||||
|
||||
|
||||
// 添加实体到稀疏集合
|
||||
this._entities.add(entity);
|
||||
const entityIndex = this._entities.getIndex(entity)!;
|
||||
|
||||
|
||||
// 确保位掩码数组有足够空间
|
||||
while (this._componentMasks.length <= entityIndex) {
|
||||
this._componentMasks.push(BitMask64Utils.clone(BitMask64Utils.ZERO));
|
||||
}
|
||||
this._componentMasks[entityIndex] = componentMask;
|
||||
|
||||
|
||||
// 更新组件类型到实体的映射
|
||||
this.updateComponentMappings(entity, entityComponents, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从组件索引中移除实体
|
||||
*
|
||||
*
|
||||
* 清理实体相关的所有索引数据,保持数据结构的紧凑性。
|
||||
*
|
||||
*
|
||||
* @param entity 要移除的实体
|
||||
*/
|
||||
public removeEntity(entity: Entity): void {
|
||||
@@ -121,16 +121,16 @@ export class ComponentSparseSet {
|
||||
if (entityIndex === undefined) {
|
||||
return; // 实体不存在
|
||||
}
|
||||
|
||||
|
||||
// 获取实体的组件类型集合
|
||||
const entityComponents = this.getEntityComponentTypes(entity);
|
||||
|
||||
|
||||
// 更新组件类型到实体的映射
|
||||
this.updateComponentMappings(entity, entityComponents, false);
|
||||
|
||||
|
||||
// 从稀疏集合中移除实体
|
||||
this._entities.remove(entity);
|
||||
|
||||
|
||||
// 维护位掩码数组的紧凑性
|
||||
const lastIndex = this._componentMasks.length - 1;
|
||||
if (entityIndex !== lastIndex) {
|
||||
@@ -139,10 +139,10 @@ export class ComponentSparseSet {
|
||||
}
|
||||
this._componentMasks.pop();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询包含指定组件的所有实体
|
||||
*
|
||||
*
|
||||
* @param componentType 组件类型
|
||||
* @returns 包含该组件的实体集合
|
||||
*/
|
||||
@@ -150,12 +150,12 @@ export class ComponentSparseSet {
|
||||
const entities = this._componentToEntities.get(componentType);
|
||||
return entities ? new Set(entities) : new Set<Entity>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 多组件查询(AND操作)
|
||||
*
|
||||
*
|
||||
* 查找同时包含所有指定组件的实体。
|
||||
*
|
||||
*
|
||||
* @param componentTypes 组件类型数组
|
||||
* @returns 满足条件的实体集合
|
||||
*/
|
||||
@@ -163,13 +163,13 @@ export class ComponentSparseSet {
|
||||
if (componentTypes.length === 0) {
|
||||
return new Set<Entity>();
|
||||
}
|
||||
|
||||
|
||||
if (componentTypes.length === 1) {
|
||||
return this.queryByComponent(componentTypes[0]!);
|
||||
}
|
||||
|
||||
|
||||
// 构建目标位掩码
|
||||
let targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
for (const componentType of componentTypes) {
|
||||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||
return new Set<Entity>(); // 未注册的组件类型,结果为空
|
||||
@@ -177,9 +177,9 @@ export class ComponentSparseSet {
|
||||
const bitMask = ComponentRegistry.getBitMask(componentType);
|
||||
BitMask64Utils.orInPlace(targetMask, bitMask);
|
||||
}
|
||||
|
||||
|
||||
const result = ComponentSparseSet._entitySetPool.obtain();
|
||||
|
||||
|
||||
// 遍历所有实体,检查位掩码匹配
|
||||
this._entities.forEach((entity, index) => {
|
||||
const entityMask = this._componentMasks[index]!;
|
||||
@@ -187,15 +187,15 @@ export class ComponentSparseSet {
|
||||
result.add(entity);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 多组件查询(OR操作)
|
||||
*
|
||||
*
|
||||
* 查找包含任意一个指定组件的实体。
|
||||
*
|
||||
*
|
||||
* @param componentTypes 组件类型数组
|
||||
* @returns 满足条件的实体集合
|
||||
*/
|
||||
@@ -203,26 +203,26 @@ export class ComponentSparseSet {
|
||||
if (componentTypes.length === 0) {
|
||||
return new Set<Entity>();
|
||||
}
|
||||
|
||||
|
||||
if (componentTypes.length === 1) {
|
||||
return this.queryByComponent(componentTypes[0]!);
|
||||
}
|
||||
|
||||
|
||||
// 构建目标位掩码
|
||||
let targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
for (const componentType of componentTypes) {
|
||||
if (ComponentRegistry.isRegistered(componentType)) {
|
||||
const bitMask = ComponentRegistry.getBitMask(componentType);
|
||||
BitMask64Utils.orInPlace(targetMask, bitMask);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (BitMask64Utils.equals(targetMask, BitMask64Utils.ZERO)) {
|
||||
return new Set<Entity>(); // 没有有效的组件类型
|
||||
}
|
||||
|
||||
|
||||
const result = ComponentSparseSet._entitySetPool.obtain();
|
||||
|
||||
|
||||
// 遍历所有实体,检查位掩码匹配
|
||||
this._entities.forEach((entity, index) => {
|
||||
const entityMask = this._componentMasks[index]!;
|
||||
@@ -230,13 +230,13 @@ export class ComponentSparseSet {
|
||||
result.add(entity);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查实体是否包含指定组件
|
||||
*
|
||||
*
|
||||
* @param entity 实体
|
||||
* @param componentType 组件类型
|
||||
* @returns 是否包含该组件
|
||||
@@ -246,20 +246,20 @@ export class ComponentSparseSet {
|
||||
if (entityIndex === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const entityMask = this._componentMasks[entityIndex]!;
|
||||
const componentMask = ComponentRegistry.getBitMask(componentType);
|
||||
|
||||
return BitMask64Utils.hasAny(entityMask, componentMask);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取实体的组件位掩码
|
||||
*
|
||||
*
|
||||
* @param entity 实体
|
||||
* @returns 组件位掩码,如果实体不存在则返回undefined
|
||||
*/
|
||||
@@ -270,33 +270,33 @@ export class ComponentSparseSet {
|
||||
}
|
||||
return this._componentMasks[entityIndex];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取所有实体
|
||||
*
|
||||
*
|
||||
* @returns 所有实体的数组
|
||||
*/
|
||||
public getAllEntities(): Entity[] {
|
||||
return this._entities.toArray();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取实体数量
|
||||
*/
|
||||
public get size(): number {
|
||||
return this._entities.size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查是否为空
|
||||
*/
|
||||
public get isEmpty(): boolean {
|
||||
return this._entities.isEmpty;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 遍历所有实体
|
||||
*
|
||||
*
|
||||
* @param callback 遍历回调函数
|
||||
*/
|
||||
public forEach(callback: (entity: Entity, mask: BitMask64Data, index: number) => void): void {
|
||||
@@ -304,21 +304,21 @@ export class ComponentSparseSet {
|
||||
callback(entity, this._componentMasks[index]!, index);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清空所有数据
|
||||
*/
|
||||
public clear(): void {
|
||||
this._entities.clear();
|
||||
this._componentMasks.length = 0;
|
||||
|
||||
|
||||
// 清理时将所有持有的实体集合返回到池中
|
||||
for (const entitySet of this._componentToEntities.values()) {
|
||||
ComponentSparseSet._entitySetPool.release(entitySet);
|
||||
}
|
||||
this._componentToEntities.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取内存使用统计
|
||||
*/
|
||||
@@ -327,15 +327,15 @@ export class ComponentSparseSet {
|
||||
masksMemory: number;
|
||||
mappingsMemory: number;
|
||||
totalMemory: number;
|
||||
} {
|
||||
} {
|
||||
const entitiesStats = this._entities.getMemoryStats();
|
||||
const masksMemory = this._componentMasks.length * 16; // 估计每个BigInt 16字节
|
||||
|
||||
|
||||
let mappingsMemory = this._componentToEntities.size * 16; // Map条目开销
|
||||
for (const entitySet of this._componentToEntities.values()) {
|
||||
mappingsMemory += entitySet.size * 8; // 每个实体引用8字节
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
entitiesMemory: entitiesStats.totalMemory,
|
||||
masksMemory,
|
||||
@@ -343,7 +343,7 @@ export class ComponentSparseSet {
|
||||
totalMemory: entitiesStats.totalMemory + masksMemory + mappingsMemory
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证数据结构完整性
|
||||
*/
|
||||
@@ -352,12 +352,12 @@ export class ComponentSparseSet {
|
||||
if (!this._entities.validate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查位掩码数组长度一致性
|
||||
if (this._componentMasks.length !== this._entities.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查组件映射的一致性
|
||||
const allMappedEntities = new Set<Entity>();
|
||||
for (const entitySet of this._componentToEntities.values()) {
|
||||
@@ -365,17 +365,17 @@ export class ComponentSparseSet {
|
||||
allMappedEntities.add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 验证映射中的实体都在稀疏集合中
|
||||
for (const entity of allMappedEntities) {
|
||||
if (!this._entities.has(entity)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取实体的组件类型集合
|
||||
*/
|
||||
@@ -386,18 +386,18 @@ export class ComponentSparseSet {
|
||||
}
|
||||
return componentTypes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新组件类型到实体的映射
|
||||
*/
|
||||
private updateComponentMappings(
|
||||
entity: Entity,
|
||||
componentTypes: Set<ComponentType>,
|
||||
entity: Entity,
|
||||
componentTypes: Set<ComponentType>,
|
||||
add: boolean
|
||||
): void {
|
||||
for (const componentType of componentTypes) {
|
||||
let entities = this._componentToEntities.get(componentType);
|
||||
|
||||
|
||||
if (add) {
|
||||
if (!entities) {
|
||||
entities = ComponentSparseSet._entitySetPool.obtain();
|
||||
@@ -415,5 +415,5 @@ export class ComponentSparseSet {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export class EntityList {
|
||||
|
||||
this.buffer.push(entity);
|
||||
this._idToEntity.set(entity.id, entity);
|
||||
|
||||
|
||||
// 更新名称索引
|
||||
this.updateNameIndex(entity, true);
|
||||
}
|
||||
@@ -67,10 +67,10 @@ export class EntityList {
|
||||
if (index !== -1) {
|
||||
this.buffer.splice(index, 1);
|
||||
this._idToEntity.delete(entity.id);
|
||||
|
||||
|
||||
// 更新名称索引
|
||||
this.updateNameIndex(entity, false);
|
||||
|
||||
|
||||
// 回收实体ID到ID池
|
||||
if (this._scene && this._scene.identifierPool) {
|
||||
this._scene.identifierPool.checkIn(entity.id);
|
||||
@@ -84,19 +84,19 @@ export class EntityList {
|
||||
public removeAllEntities(): void {
|
||||
// 收集所有实体ID用于回收
|
||||
const idsToRecycle: number[] = [];
|
||||
|
||||
|
||||
for (let i = this.buffer.length - 1; i >= 0; i--) {
|
||||
idsToRecycle.push(this.buffer[i]!.id);
|
||||
this.buffer[i]!.destroy();
|
||||
}
|
||||
|
||||
|
||||
// 批量回收ID
|
||||
if (this._scene && this._scene.identifierPool) {
|
||||
for (const id of idsToRecycle) {
|
||||
this._scene.identifierPool.checkIn(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.buffer.length = 0;
|
||||
this._idToEntity.clear();
|
||||
this._nameToEntities.clear();
|
||||
@@ -170,13 +170,13 @@ export class EntityList {
|
||||
*/
|
||||
public findEntitiesByTag(tag: number): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
|
||||
|
||||
for (const entity of this.buffer) {
|
||||
if (entity.tag === tag) {
|
||||
result.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -187,13 +187,13 @@ export class EntityList {
|
||||
*/
|
||||
public findEntitiesWithComponent<T extends Component>(componentType: new (...args: any[]) => T): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
|
||||
|
||||
for (const entity of this.buffer) {
|
||||
if (entity.hasComponent(componentType)) {
|
||||
result.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ export class EntityList {
|
||||
const index = entities.indexOf(entity);
|
||||
if (index !== -1) {
|
||||
entities.splice(index, 1);
|
||||
|
||||
|
||||
// 如果数组为空,删除映射
|
||||
if (entities.length === 0) {
|
||||
this._nameToEntities.delete(entity.name);
|
||||
@@ -263,7 +263,7 @@ export class EntityList {
|
||||
pendingAdd: number;
|
||||
pendingRemove: number;
|
||||
nameIndexSize: number;
|
||||
} {
|
||||
} {
|
||||
let activeCount = 0;
|
||||
for (const entity of this.buffer) {
|
||||
if (entity.enabled && !entity.isDestroyed) {
|
||||
|
||||
@@ -53,7 +53,7 @@ export class EntityProcessorList {
|
||||
|
||||
/**
|
||||
* 开始处理
|
||||
*
|
||||
*
|
||||
* 对所有处理器进行排序以确保正确的执行顺序。
|
||||
*/
|
||||
public begin(): void {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/**
|
||||
* 世代式ID池管理器
|
||||
*
|
||||
*
|
||||
* 用于管理实体ID的分配和回收,支持世代版本控制以防止悬空引用问题。
|
||||
* 世代式ID由索引和版本组成,当ID被回收时版本会递增,确保旧引用失效。
|
||||
*
|
||||
*
|
||||
* 支持动态扩展,理论上可以支持到65535个索引(16位),每个索引65535个版本(16位)。
|
||||
* 总计可以处理超过42亿个独特的ID组合,完全满足ECS大规模实体需求。
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const pool = new IdentifierPool();
|
||||
*
|
||||
*
|
||||
* // 分配ID
|
||||
* const id = pool.checkOut(); // 例如: 65536 (版本1,索引0)
|
||||
*
|
||||
*
|
||||
* // 回收ID
|
||||
* pool.checkIn(id);
|
||||
*
|
||||
*
|
||||
* // 验证ID是否有效
|
||||
* const isValid = pool.isValid(id); // false,因为版本已递增
|
||||
* ```
|
||||
@@ -26,18 +26,18 @@ export class IdentifierPool {
|
||||
* 下一个可用的索引
|
||||
*/
|
||||
private _nextAvailableIndex = 0;
|
||||
|
||||
|
||||
/**
|
||||
* 空闲的索引列表
|
||||
*/
|
||||
private _freeIndices: number[] = [];
|
||||
|
||||
|
||||
/**
|
||||
* 每个索引对应的世代版本
|
||||
* 动态扩展的Map,按需分配内存
|
||||
*/
|
||||
private _generations = new Map<number, number>();
|
||||
|
||||
|
||||
/**
|
||||
* 延迟回收队列
|
||||
* 防止在同一帧内立即重用ID,避免时序问题
|
||||
@@ -47,30 +47,30 @@ export class IdentifierPool {
|
||||
generation: number;
|
||||
timestamp: number;
|
||||
}> = [];
|
||||
|
||||
|
||||
/**
|
||||
* 延迟回收时间(毫秒)
|
||||
*/
|
||||
private _recycleDelay: number = 100;
|
||||
|
||||
|
||||
/**
|
||||
* 最大索引限制(16位)
|
||||
* 这是框架设计选择:16位索引 + 16位版本 = 32位ID,确保高效位操作
|
||||
* 不是硬件限制,而是性能和内存效率的权衡
|
||||
*/
|
||||
private static readonly MAX_INDEX = 0xFFFF; // 65535
|
||||
|
||||
|
||||
/**
|
||||
* 最大世代限制(16位)
|
||||
*/
|
||||
private static readonly MAX_GENERATION = 0xFFFF; // 65535
|
||||
|
||||
|
||||
/**
|
||||
* 内存扩展块大小
|
||||
* 当需要更多内存时,一次性预分配的索引数量
|
||||
*/
|
||||
private _expansionBlockSize: number;
|
||||
|
||||
|
||||
/**
|
||||
* 统计信息
|
||||
*/
|
||||
@@ -83,32 +83,32 @@ export class IdentifierPool {
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
*
|
||||
* @param recycleDelay 延迟回收时间(毫秒),默认为100ms
|
||||
* @param expansionBlockSize 内存扩展块大小,默认为1024
|
||||
*/
|
||||
constructor(recycleDelay: number = 100, expansionBlockSize: number = 1024) {
|
||||
this._recycleDelay = recycleDelay;
|
||||
this._expansionBlockSize = expansionBlockSize;
|
||||
|
||||
|
||||
// 预分配第一个块的世代信息
|
||||
this._preAllocateGenerations(0, this._expansionBlockSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个可用的ID
|
||||
*
|
||||
*
|
||||
* 返回一个32位ID,高16位为世代版本,低16位为索引。
|
||||
*
|
||||
*
|
||||
* @returns 新分配的实体ID
|
||||
* @throws {Error} 当达到索引限制时抛出错误
|
||||
*/
|
||||
public checkOut(): number {
|
||||
// 处理延迟回收队列
|
||||
this._processDelayedRecycle();
|
||||
|
||||
|
||||
let index: number;
|
||||
|
||||
|
||||
if (this._freeIndices.length > 0) {
|
||||
// 重用回收的索引
|
||||
index = this._freeIndices.pop()!;
|
||||
@@ -117,69 +117,69 @@ export class IdentifierPool {
|
||||
if (this._nextAvailableIndex > IdentifierPool.MAX_INDEX) {
|
||||
throw new Error(
|
||||
`实体索引已达到框架设计限制 (${IdentifierPool.MAX_INDEX})。` +
|
||||
`这意味着您已经分配了超过65535个不同的实体索引。` +
|
||||
`这是16位索引设计的限制,考虑优化实体回收策略或升级到64位ID设计。`
|
||||
'这意味着您已经分配了超过65535个不同的实体索引。' +
|
||||
'这是16位索引设计的限制,考虑优化实体回收策略或升级到64位ID设计。'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
index = this._nextAvailableIndex++;
|
||||
|
||||
|
||||
// 按需扩展世代存储
|
||||
this._ensureGenerationCapacity(index);
|
||||
}
|
||||
|
||||
|
||||
const generation = this._generations.get(index) || 1;
|
||||
this._stats.totalAllocated++;
|
||||
this._stats.currentActive++;
|
||||
|
||||
|
||||
return this._packId(index, generation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收一个ID
|
||||
*
|
||||
*
|
||||
* 验证ID的有效性后,将其加入延迟回收队列。
|
||||
* ID不会立即可重用,而是在延迟时间后才真正回收。
|
||||
*
|
||||
*
|
||||
* @param id 要回收的实体ID
|
||||
* @returns 是否成功回收(ID是否有效且未被重复回收)
|
||||
*/
|
||||
public checkIn(id: number): boolean {
|
||||
const index = this._unpackIndex(id);
|
||||
const generation = this._unpackGeneration(id);
|
||||
|
||||
|
||||
// 验证ID有效性
|
||||
if (!this._isValidId(index, generation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查是否已经在待回收队列中
|
||||
const alreadyPending = this._pendingRecycle.some(
|
||||
item => item.index === index && item.generation === generation
|
||||
(item) => item.index === index && item.generation === generation
|
||||
);
|
||||
|
||||
|
||||
if (alreadyPending) {
|
||||
return false; // 已经在回收队列中,拒绝重复回收
|
||||
}
|
||||
|
||||
|
||||
// 加入延迟回收队列
|
||||
this._pendingRecycle.push({
|
||||
index,
|
||||
generation,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
|
||||
this._stats.currentActive--;
|
||||
this._stats.totalRecycled++;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证ID是否有效
|
||||
*
|
||||
*
|
||||
* 检查ID的索引和世代版本是否匹配当前状态。
|
||||
*
|
||||
*
|
||||
* @param id 要验证的实体ID
|
||||
* @returns ID是否有效
|
||||
*/
|
||||
@@ -191,7 +191,7 @@ export class IdentifierPool {
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
*
|
||||
*
|
||||
* @returns 池的当前状态统计
|
||||
*/
|
||||
public getStats(): {
|
||||
@@ -217,20 +217,20 @@ export class IdentifierPool {
|
||||
averageGeneration: number;
|
||||
/** 世代存储大小 */
|
||||
generationStorageSize: number;
|
||||
} {
|
||||
} {
|
||||
// 计算平均世代版本
|
||||
let totalGeneration = 0;
|
||||
let generationCount = 0;
|
||||
|
||||
|
||||
for (const [index, generation] of this._generations) {
|
||||
if (index < this._nextAvailableIndex) {
|
||||
totalGeneration += generation;
|
||||
generationCount++;
|
||||
}
|
||||
}
|
||||
|
||||
const averageGeneration = generationCount > 0
|
||||
? totalGeneration / generationCount
|
||||
|
||||
const averageGeneration = generationCount > 0
|
||||
? totalGeneration / generationCount
|
||||
: 1;
|
||||
|
||||
return {
|
||||
@@ -250,7 +250,7 @@ export class IdentifierPool {
|
||||
|
||||
/**
|
||||
* 强制执行延迟回收处理
|
||||
*
|
||||
*
|
||||
* 在某些情况下可能需要立即处理延迟回收队列,
|
||||
* 比如内存压力大或者需要精确的统计信息时。
|
||||
*/
|
||||
@@ -260,19 +260,19 @@ export class IdentifierPool {
|
||||
|
||||
/**
|
||||
* 清理过期的延迟回收项
|
||||
*
|
||||
*
|
||||
* 将超过延迟时间的回收项真正回收到空闲列表中。
|
||||
*
|
||||
*
|
||||
* @param forceAll 是否强制处理所有延迟回收项
|
||||
* @private
|
||||
*/
|
||||
private _processDelayedRecycle(forceAll: boolean = false): void {
|
||||
if (this._pendingRecycle.length === 0) return;
|
||||
|
||||
|
||||
const now = Date.now();
|
||||
const readyToRecycle: typeof this._pendingRecycle = [];
|
||||
const stillPending: typeof this._pendingRecycle = [];
|
||||
|
||||
|
||||
// 分离已到期和未到期的项
|
||||
for (const item of this._pendingRecycle) {
|
||||
if (forceAll || now - item.timestamp >= this._recycleDelay) {
|
||||
@@ -281,33 +281,33 @@ export class IdentifierPool {
|
||||
stillPending.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 处理到期的回收项
|
||||
for (const item of readyToRecycle) {
|
||||
// 再次验证ID有效性(防止重复回收)
|
||||
if (this._isValidId(item.index, item.generation)) {
|
||||
// 递增世代版本
|
||||
let newGeneration = item.generation + 1;
|
||||
|
||||
|
||||
// 防止世代版本溢出
|
||||
if (newGeneration > IdentifierPool.MAX_GENERATION) {
|
||||
newGeneration = 1; // 重置为1而不是0
|
||||
}
|
||||
|
||||
|
||||
this._generations.set(item.index, newGeneration);
|
||||
|
||||
|
||||
// 添加到空闲列表
|
||||
this._freeIndices.push(item.index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新待回收队列
|
||||
this._pendingRecycle = stillPending;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预分配世代信息
|
||||
*
|
||||
*
|
||||
* @param startIndex 起始索引
|
||||
* @param count 分配数量
|
||||
* @private
|
||||
@@ -324,7 +324,7 @@ export class IdentifierPool {
|
||||
|
||||
/**
|
||||
* 确保指定索引的世代信息存在
|
||||
*
|
||||
*
|
||||
* @param index 索引
|
||||
* @private
|
||||
*/
|
||||
@@ -332,7 +332,7 @@ export class IdentifierPool {
|
||||
if (!this._generations.has(index)) {
|
||||
// 计算需要扩展的起始位置
|
||||
const expansionStart = Math.floor(index / this._expansionBlockSize) * this._expansionBlockSize;
|
||||
|
||||
|
||||
// 预分配一个块
|
||||
this._preAllocateGenerations(expansionStart, this._expansionBlockSize);
|
||||
}
|
||||
@@ -340,7 +340,7 @@ export class IdentifierPool {
|
||||
|
||||
/**
|
||||
* 计算内存使用量
|
||||
*
|
||||
*
|
||||
* @returns 内存使用字节数
|
||||
* @private
|
||||
*/
|
||||
@@ -348,13 +348,13 @@ export class IdentifierPool {
|
||||
const generationMapSize = this._generations.size * 16; // Map overhead + number pair
|
||||
const freeIndicesSize = this._freeIndices.length * 8;
|
||||
const pendingRecycleSize = this._pendingRecycle.length * 32;
|
||||
|
||||
|
||||
return generationMapSize + freeIndicesSize + pendingRecycleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包索引和世代为32位ID
|
||||
*
|
||||
*
|
||||
* @param index 索引(16位)
|
||||
* @param generation 世代版本(16位)
|
||||
* @returns 打包后的32位ID
|
||||
@@ -366,7 +366,7 @@ export class IdentifierPool {
|
||||
|
||||
/**
|
||||
* 从ID中解包索引
|
||||
*
|
||||
*
|
||||
* @param id 32位ID
|
||||
* @returns 索引部分(16位)
|
||||
* @private
|
||||
@@ -377,7 +377,7 @@ export class IdentifierPool {
|
||||
|
||||
/**
|
||||
* 从ID中解包世代版本
|
||||
*
|
||||
*
|
||||
* @param id 32位ID
|
||||
* @returns 世代版本部分(16位)
|
||||
* @private
|
||||
@@ -388,7 +388,7 @@ export class IdentifierPool {
|
||||
|
||||
/**
|
||||
* 内部ID有效性检查
|
||||
*
|
||||
*
|
||||
* @param index 索引
|
||||
* @param generation 世代版本
|
||||
* @returns 是否有效
|
||||
@@ -398,8 +398,8 @@ export class IdentifierPool {
|
||||
if (index < 0 || index >= this._nextAvailableIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const currentGeneration = this._generations.get(index);
|
||||
return currentGeneration !== undefined && currentGeneration === generation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,15 @@ export interface QueryCondition {
|
||||
|
||||
/**
|
||||
* 实体匹配条件描述符
|
||||
*
|
||||
*
|
||||
* 用于描述实体查询条件,不执行实际查询
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const matcher = Matcher.all(Position, Velocity)
|
||||
* .any(Health, Shield)
|
||||
* .none(Dead);
|
||||
*
|
||||
*
|
||||
* // 获取查询条件
|
||||
* const condition = matcher.getCondition();
|
||||
* ```
|
||||
@@ -219,8 +219,8 @@ export class Matcher {
|
||||
* 检查是否为空条件
|
||||
*/
|
||||
public isEmpty(): boolean {
|
||||
return this.condition.all.length === 0 &&
|
||||
this.condition.any.length === 0 &&
|
||||
return this.condition.all.length === 0 &&
|
||||
this.condition.any.length === 0 &&
|
||||
this.condition.none.length === 0 &&
|
||||
this.condition.tag === undefined &&
|
||||
this.condition.name === undefined &&
|
||||
@@ -265,32 +265,32 @@ export class Matcher {
|
||||
*/
|
||||
public toString(): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
|
||||
if (this.condition.all.length > 0) {
|
||||
parts.push(`all(${this.condition.all.map(t => getComponentTypeName(t)).join(', ')})`);
|
||||
parts.push(`all(${this.condition.all.map((t) => getComponentTypeName(t)).join(', ')})`);
|
||||
}
|
||||
|
||||
|
||||
if (this.condition.any.length > 0) {
|
||||
parts.push(`any(${this.condition.any.map(t => getComponentTypeName(t)).join(', ')})`);
|
||||
parts.push(`any(${this.condition.any.map((t) => getComponentTypeName(t)).join(', ')})`);
|
||||
}
|
||||
|
||||
|
||||
if (this.condition.none.length > 0) {
|
||||
parts.push(`none(${this.condition.none.map(t => getComponentTypeName(t)).join(', ')})`);
|
||||
parts.push(`none(${this.condition.none.map((t) => getComponentTypeName(t)).join(', ')})`);
|
||||
}
|
||||
|
||||
|
||||
if (this.condition.tag !== undefined) {
|
||||
parts.push(`tag(${this.condition.tag})`);
|
||||
}
|
||||
|
||||
|
||||
if (this.condition.name !== undefined) {
|
||||
parts.push(`name(${this.condition.name})`);
|
||||
}
|
||||
|
||||
|
||||
if (this.condition.component !== undefined) {
|
||||
parts.push(`component(${getComponentTypeName(this.condition.component)})`);
|
||||
}
|
||||
|
||||
|
||||
return `Matcher[${parts.join(' & ')}]`;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/**
|
||||
* 稀疏集合实现
|
||||
*
|
||||
*
|
||||
* 提供O(1)的插入、删除、查找操作,同时保持数据的紧凑存储。
|
||||
* 使用密集数组存储实际数据,稀疏映射提供快速访问
|
||||
*
|
||||
*
|
||||
* @template T 存储的数据类型
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const sparseSet = new SparseSet<Entity>();
|
||||
*
|
||||
*
|
||||
* sparseSet.add(entity1);
|
||||
* sparseSet.add(entity2);
|
||||
*
|
||||
*
|
||||
* if (sparseSet.has(entity1)) {
|
||||
* sparseSet.remove(entity1);
|
||||
* }
|
||||
*
|
||||
*
|
||||
* sparseSet.forEach((entity, index) => {
|
||||
* console.log(`Entity at index ${index}: ${entity.name}`);
|
||||
* });
|
||||
@@ -25,21 +25,21 @@
|
||||
export class SparseSet<T> {
|
||||
/**
|
||||
* 密集存储数组
|
||||
*
|
||||
*
|
||||
* 连续存储所有有效数据,确保遍历时的缓存友好性。
|
||||
*/
|
||||
private _dense: T[] = [];
|
||||
|
||||
|
||||
/**
|
||||
* 稀疏映射表
|
||||
*
|
||||
*
|
||||
* 将数据项映射到密集数组中的索引,提供O(1)的查找性能。
|
||||
*/
|
||||
private _sparse = new Map<T, number>();
|
||||
|
||||
|
||||
/**
|
||||
* 添加元素到集合
|
||||
*
|
||||
*
|
||||
* @param item 要添加的元素
|
||||
* @returns 是否成功添加(false表示元素已存在)
|
||||
*/
|
||||
@@ -47,21 +47,21 @@ export class SparseSet<T> {
|
||||
if (this._sparse.has(item)) {
|
||||
return false; // 元素已存在
|
||||
}
|
||||
|
||||
|
||||
const index = this._dense.length;
|
||||
this._dense.push(item);
|
||||
this._sparse.set(item, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从集合中移除元素
|
||||
*
|
||||
*
|
||||
* 使用swap-and-pop技术保持数组紧凑性:
|
||||
* 1. 将要删除的元素与最后一个元素交换
|
||||
* 2. 删除最后一个元素
|
||||
* 3. 更新映射表
|
||||
*
|
||||
*
|
||||
* @param item 要移除的元素
|
||||
* @returns 是否成功移除(false表示元素不存在)
|
||||
*/
|
||||
@@ -70,72 +70,72 @@ export class SparseSet<T> {
|
||||
if (index === undefined) {
|
||||
return false; // 元素不存在
|
||||
}
|
||||
|
||||
|
||||
const lastIndex = this._dense.length - 1;
|
||||
|
||||
|
||||
// 如果不是最后一个元素,则与最后一个元素交换
|
||||
if (index !== lastIndex) {
|
||||
const lastItem = this._dense[lastIndex]!;
|
||||
this._dense[index] = lastItem;
|
||||
this._sparse.set(lastItem, index);
|
||||
}
|
||||
|
||||
|
||||
// 移除最后一个元素
|
||||
this._dense.pop();
|
||||
this._sparse.delete(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查元素是否存在于集合中
|
||||
*
|
||||
*
|
||||
* @param item 要检查的元素
|
||||
* @returns 元素是否存在
|
||||
*/
|
||||
public has(item: T): boolean {
|
||||
return this._sparse.has(item);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取元素在密集数组中的索引
|
||||
*
|
||||
*
|
||||
* @param item 要查询的元素
|
||||
* @returns 索引,如果元素不存在则返回undefined
|
||||
*/
|
||||
public getIndex(item: T): number | undefined {
|
||||
return this._sparse.get(item);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据索引获取元素
|
||||
*
|
||||
*
|
||||
* @param index 索引
|
||||
* @returns 元素,如果索引无效则返回undefined
|
||||
*/
|
||||
public getByIndex(index: number): T | undefined {
|
||||
return this._dense[index];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取集合大小
|
||||
*/
|
||||
public get size(): number {
|
||||
return this._dense.length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查集合是否为空
|
||||
*/
|
||||
public get isEmpty(): boolean {
|
||||
return this._dense.length === 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 遍历集合中的所有元素
|
||||
*
|
||||
*
|
||||
* 保证遍历顺序与添加顺序一致(除非中间有删除操作)。
|
||||
* 遍历性能优秀,因为数据在内存中连续存储。
|
||||
*
|
||||
*
|
||||
* @param callback 遍历回调函数
|
||||
*/
|
||||
public forEach(callback: (item: T, index: number) => void): void {
|
||||
@@ -143,10 +143,10 @@ export class SparseSet<T> {
|
||||
callback(this._dense[i]!, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 映射集合中的所有元素
|
||||
*
|
||||
*
|
||||
* @param callback 映射回调函数
|
||||
* @returns 映射后的新数组
|
||||
*/
|
||||
@@ -157,10 +157,10 @@ export class SparseSet<T> {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 过滤集合中的元素
|
||||
*
|
||||
*
|
||||
* @param predicate 过滤条件
|
||||
* @returns 满足条件的元素数组
|
||||
*/
|
||||
@@ -173,10 +173,10 @@ export class SparseSet<T> {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查找第一个满足条件的元素
|
||||
*
|
||||
*
|
||||
* @param predicate 查找条件
|
||||
* @returns 找到的元素,如果没有则返回undefined
|
||||
*/
|
||||
@@ -188,10 +188,10 @@ export class SparseSet<T> {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查是否存在满足条件的元素
|
||||
*
|
||||
*
|
||||
* @param predicate 检查条件
|
||||
* @returns 是否存在满足条件的元素
|
||||
*/
|
||||
@@ -203,10 +203,10 @@ export class SparseSet<T> {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查是否所有元素都满足条件
|
||||
*
|
||||
*
|
||||
* @param predicate 检查条件
|
||||
* @returns 是否所有元素都满足条件
|
||||
*/
|
||||
@@ -218,26 +218,26 @@ export class SparseSet<T> {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取密集数组的只读副本
|
||||
*
|
||||
*
|
||||
* 返回数组的浅拷贝,确保外部无法直接修改内部数据。
|
||||
*/
|
||||
public getDenseArray(): readonly T[] {
|
||||
return [...this._dense];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取密集数组的直接引用(内部使用)
|
||||
*
|
||||
*
|
||||
* 警告:直接修改返回的数组会破坏数据结构的完整性。
|
||||
* 仅在性能关键场景下使用,并确保不会修改数组内容。
|
||||
*/
|
||||
public getDenseArrayUnsafe(): readonly T[] {
|
||||
return this._dense;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清空集合
|
||||
*/
|
||||
@@ -245,21 +245,21 @@ export class SparseSet<T> {
|
||||
this._dense.length = 0;
|
||||
this._sparse.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 转换为数组
|
||||
*/
|
||||
public toArray(): T[] {
|
||||
return [...this._dense];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 转换为Set
|
||||
*/
|
||||
public toSet(): Set<T> {
|
||||
return new Set(this._dense);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取内存使用统计信息
|
||||
*/
|
||||
@@ -267,20 +267,20 @@ export class SparseSet<T> {
|
||||
denseArraySize: number;
|
||||
sparseMapSize: number;
|
||||
totalMemory: number;
|
||||
} {
|
||||
} {
|
||||
const denseArraySize = this._dense.length * 8; // 估计每个引用8字节
|
||||
const sparseMapSize = this._sparse.size * 16; // 估计每个Map条目16字节
|
||||
|
||||
|
||||
return {
|
||||
denseArraySize,
|
||||
sparseMapSize,
|
||||
totalMemory: denseArraySize + sparseMapSize
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证数据结构的完整性
|
||||
*
|
||||
*
|
||||
* 调试用方法,检查内部数据结构是否一致。
|
||||
*/
|
||||
public validate(): boolean {
|
||||
@@ -288,7 +288,7 @@ export class SparseSet<T> {
|
||||
if (this._dense.length !== this._sparse.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查映射关系的正确性
|
||||
for (let i = 0; i < this._dense.length; i++) {
|
||||
const item = this._dense[i]!;
|
||||
@@ -297,14 +297,14 @@ export class SparseSet<T> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检查稀疏映射中的所有项都在密集数组中
|
||||
for (const [item, index] of this._sparse) {
|
||||
if (index >= this._dense.length || this._dense[index] !== item) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ export { Matcher } from './Matcher';
|
||||
export { Bits } from './Bits';
|
||||
export { BitMask64Utils, BitMask64Data } from './BigIntCompatibility';
|
||||
export { SparseSet } from './SparseSet';
|
||||
export { ComponentSparseSet } from './ComponentSparseSet';
|
||||
export { ComponentSparseSet } from './ComponentSparseSet';
|
||||
|
||||
@@ -13,22 +13,22 @@ export interface IGlobalSystem {
|
||||
* 系统名称
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
|
||||
/**
|
||||
* 初始化系统
|
||||
*/
|
||||
initialize?(): void;
|
||||
|
||||
|
||||
/**
|
||||
* 更新系统
|
||||
*/
|
||||
update(deltaTime?: number): void;
|
||||
|
||||
|
||||
/**
|
||||
* 重置系统
|
||||
*/
|
||||
reset?(): void;
|
||||
|
||||
|
||||
/**
|
||||
* 销毁系统
|
||||
*/
|
||||
@@ -43,17 +43,17 @@ export interface IWorldConfig {
|
||||
* World名称
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
|
||||
/**
|
||||
* 是否启用调试模式
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* 最大Scene数量限制
|
||||
*/
|
||||
maxScenes?: number;
|
||||
|
||||
|
||||
/**
|
||||
* 是否自动清理空Scene
|
||||
*/
|
||||
@@ -62,22 +62,22 @@ export interface IWorldConfig {
|
||||
|
||||
/**
|
||||
* World类 - ECS世界管理器
|
||||
*
|
||||
*
|
||||
* World是Scene的容器,每个World可以管理多个Scene。
|
||||
* 这种设计允许创建独立的游戏世界,如:
|
||||
* - 游戏房间(每个房间一个World)
|
||||
* - 不同的游戏模式
|
||||
* - 独立的模拟环境
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 创建游戏房间的World
|
||||
* const roomWorld = new World({ name: 'Room_001' });
|
||||
*
|
||||
*
|
||||
* // 在World中创建Scene
|
||||
* const gameScene = roomWorld.createScene('game', new Scene());
|
||||
* const uiScene = roomWorld.createScene('ui', new Scene());
|
||||
*
|
||||
*
|
||||
* // 更新整个World
|
||||
* roomWorld.update(deltaTime);
|
||||
* ```
|
||||
@@ -99,7 +99,7 @@ export class World {
|
||||
autoCleanup: true,
|
||||
...config
|
||||
};
|
||||
|
||||
|
||||
this.name = this._config.name!;
|
||||
this._createdAt = Date.now();
|
||||
}
|
||||
@@ -120,7 +120,7 @@ export class World {
|
||||
|
||||
// 如果没有提供Scene实例,创建默认Scene
|
||||
const scene = sceneInstance || (new Scene() as unknown as T);
|
||||
|
||||
|
||||
// 设置Scene的标识
|
||||
if ('id' in scene) {
|
||||
(scene as any).id = sceneId;
|
||||
@@ -154,7 +154,7 @@ export class World {
|
||||
// 清理Scene资源
|
||||
scene.end();
|
||||
this._scenes.delete(sceneId);
|
||||
|
||||
|
||||
logger.info(`从World '${this.name}' 中移除Scene: ${sceneId}`);
|
||||
return true;
|
||||
}
|
||||
@@ -244,7 +244,7 @@ export class World {
|
||||
if (system.initialize) {
|
||||
system.initialize();
|
||||
}
|
||||
|
||||
|
||||
logger.debug(`在World '${this.name}' 中添加全局System: ${system.name}`);
|
||||
return system;
|
||||
}
|
||||
@@ -262,7 +262,7 @@ export class World {
|
||||
if (system.reset) {
|
||||
system.reset();
|
||||
}
|
||||
|
||||
|
||||
logger.debug(`从World '${this.name}' 中移除全局System: ${system.name}`);
|
||||
return true;
|
||||
}
|
||||
@@ -290,14 +290,14 @@ export class World {
|
||||
}
|
||||
|
||||
this._isActive = true;
|
||||
|
||||
|
||||
// 启动所有全局System
|
||||
for (const system of this._globalSystems) {
|
||||
if (system.initialize) {
|
||||
system.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
logger.info(`启动World: ${this.name}`);
|
||||
}
|
||||
|
||||
@@ -408,7 +408,7 @@ export class World {
|
||||
globalSystemCount: this._globalSystems.length,
|
||||
createdAt: this._createdAt,
|
||||
config: { ...this._config },
|
||||
scenes: Array.from(this._scenes.keys()).map(sceneId => ({
|
||||
scenes: Array.from(this._scenes.keys()).map((sceneId) => ({
|
||||
id: sceneId,
|
||||
isActive: this._activeScenes.has(sceneId),
|
||||
name: this._scenes.get(sceneId)?.name || sceneId
|
||||
@@ -454,8 +454,8 @@ export class World {
|
||||
const cleanupThreshold = 5 * 60 * 1000; // 5分钟
|
||||
|
||||
for (const [sceneId, scene] of this._scenes) {
|
||||
if (!this._activeScenes.has(sceneId) &&
|
||||
scene.entities &&
|
||||
if (!this._activeScenes.has(sceneId) &&
|
||||
scene.entities &&
|
||||
scene.entities.count === 0 &&
|
||||
(currentTime - this._createdAt) > cleanupThreshold) {
|
||||
return true;
|
||||
@@ -472,15 +472,15 @@ export class World {
|
||||
const sceneIds = Array.from(this._scenes.keys());
|
||||
const currentTime = Date.now();
|
||||
const cleanupThreshold = 5 * 60 * 1000; // 5分钟
|
||||
|
||||
|
||||
for (const sceneId of sceneIds) {
|
||||
const scene = this._scenes.get(sceneId);
|
||||
if (scene &&
|
||||
!this._activeScenes.has(sceneId) &&
|
||||
scene.entities &&
|
||||
if (scene &&
|
||||
!this._activeScenes.has(sceneId) &&
|
||||
scene.entities &&
|
||||
scene.entities.count === 0 &&
|
||||
(currentTime - this._createdAt) > cleanupThreshold) {
|
||||
|
||||
|
||||
this.removeScene(sceneId);
|
||||
logger.debug(`自动清理空Scene: ${sceneId} from World ${this.name}`);
|
||||
}
|
||||
@@ -509,4 +509,4 @@ export class World {
|
||||
public get createdAt(): number {
|
||||
return this._createdAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,17 +12,17 @@ export interface IWorldManagerConfig {
|
||||
* 最大World数量
|
||||
*/
|
||||
maxWorlds?: number;
|
||||
|
||||
|
||||
/**
|
||||
* 是否自动清理空World
|
||||
*/
|
||||
autoCleanup?: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* 清理间隔(毫秒)
|
||||
*/
|
||||
cleanupInterval?: number;
|
||||
|
||||
|
||||
/**
|
||||
* 是否启用调试模式
|
||||
*/
|
||||
@@ -242,11 +242,11 @@ export class WorldManager implements IService {
|
||||
*/
|
||||
public startAll(): void {
|
||||
this._isRunning = true;
|
||||
|
||||
|
||||
for (const worldId of this._worlds.keys()) {
|
||||
this.setWorldActive(worldId, true);
|
||||
}
|
||||
|
||||
|
||||
logger.info('启动所有World');
|
||||
}
|
||||
|
||||
@@ -255,11 +255,11 @@ export class WorldManager implements IService {
|
||||
*/
|
||||
public stopAll(): void {
|
||||
this._isRunning = false;
|
||||
|
||||
|
||||
for (const worldId of this._activeWorlds) {
|
||||
this.setWorldActive(worldId, false);
|
||||
}
|
||||
|
||||
|
||||
logger.info('停止所有World');
|
||||
}
|
||||
|
||||
@@ -432,7 +432,7 @@ export class WorldManager implements IService {
|
||||
// 1. World未激活
|
||||
// 2. 没有Scene或所有Scene都是空的
|
||||
// 3. 创建时间超过10分钟
|
||||
|
||||
|
||||
if (world.isActive) {
|
||||
return false;
|
||||
}
|
||||
@@ -444,7 +444,7 @@ export class WorldManager implements IService {
|
||||
|
||||
// 检查是否所有Scene都是空的
|
||||
const allScenes = world.getAllScenes();
|
||||
const hasEntities = allScenes.some(scene =>
|
||||
const hasEntities = allScenes.some((scene) =>
|
||||
scene.entities && scene.entities.count > 0
|
||||
);
|
||||
|
||||
@@ -485,4 +485,4 @@ export class WorldManager implements IService {
|
||||
public get config(): IWorldManagerConfig {
|
||||
return { ...this._config };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@ export * from './Serialization';
|
||||
export { ReferenceTracker, getSceneByEntityId } from './Core/ReferenceTracker';
|
||||
export type { EntityRefRecord } from './Core/ReferenceTracker';
|
||||
export { ReactiveQuery, ReactiveQueryChangeType } from './Core/ReactiveQuery';
|
||||
export type { ReactiveQueryChange, ReactiveQueryListener, ReactiveQueryConfig } from './Core/ReactiveQuery';
|
||||
export type { ReactiveQueryChange, ReactiveQueryListener, ReactiveQueryConfig } from './Core/ReactiveQuery';
|
||||
|
||||
@@ -235,4 +235,4 @@ export class PlatformDetector {
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ export class PlatformManager {
|
||||
platformSupportsSharedArrayBuffer: boolean;
|
||||
platformMaxWorkerCount: number;
|
||||
platformLimitations: any;
|
||||
} {
|
||||
} {
|
||||
if (!this.adapter) {
|
||||
return {
|
||||
platformSupportsWorker: false,
|
||||
@@ -134,4 +134,4 @@ export class PlatformManager {
|
||||
// 否则返回同步配置
|
||||
return this.adapter.getPlatformConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,4 +44,4 @@ export function supportsFeature(feature: 'worker' | 'shared-array-buffer' | 'tra
|
||||
|
||||
export function hasAdapter() {
|
||||
return PlatformManager.getInstance().hasAdapter();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,8 +208,8 @@ export class DebugPlugin implements IPlugin, IService {
|
||||
return {
|
||||
name: scene.name,
|
||||
entityCount: entities.length,
|
||||
systems: systems.map(sys => this.getSystemInfo(sys)),
|
||||
entities: entities.map(entity => this.getEntityInfo(entity))
|
||||
systems: systems.map((sys) => this.getSystemInfo(sys)),
|
||||
entities: entities.map((entity) => this.getEntityInfo(entity))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ export class DebugPlugin implements IPlugin, IService {
|
||||
enabled: entity.enabled,
|
||||
tag: entity.tag,
|
||||
componentCount: components.length,
|
||||
components: components.map(comp => this.getComponentInfo(comp))
|
||||
components: components.map((comp) => this.getComponentInfo(comp))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ export class DebugPlugin implements IPlugin, IService {
|
||||
|
||||
if (filter.hasComponent) {
|
||||
const hasComp = entity.components.some(
|
||||
c => c.constructor.name === filter.hasComponent
|
||||
(c) => c.constructor.name === filter.hasComponent
|
||||
);
|
||||
if (!hasComp) {
|
||||
continue;
|
||||
|
||||
@@ -28,7 +28,7 @@ export interface IComponent {
|
||||
|
||||
/**
|
||||
* 系统基础接口
|
||||
*
|
||||
*
|
||||
* 为现有的EntitySystem类提供类型定义
|
||||
*/
|
||||
export interface ISystemBase {
|
||||
@@ -38,7 +38,7 @@ export interface ISystemBase {
|
||||
updateOrder: number;
|
||||
/** 系统启用状态 */
|
||||
enabled: boolean;
|
||||
|
||||
|
||||
/** 系统初始化 */
|
||||
initialize(): void;
|
||||
/** 更新系统(主要处理阶段) */
|
||||
@@ -49,7 +49,7 @@ export interface ISystemBase {
|
||||
|
||||
/**
|
||||
* 组件类型定义
|
||||
*
|
||||
*
|
||||
* 用于类型安全的组件操作
|
||||
* 支持任意构造函数签名,提供更好的类型安全性
|
||||
*/
|
||||
@@ -66,14 +66,14 @@ export interface IEventBus {
|
||||
* @param data 事件数据
|
||||
*/
|
||||
emit<T>(eventType: string, data: T): void;
|
||||
|
||||
|
||||
/**
|
||||
* 异步发射事件
|
||||
* @param eventType 事件类型
|
||||
* @param data 事件数据
|
||||
*/
|
||||
emitAsync<T>(eventType: string, data: T): Promise<void>;
|
||||
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
* @param eventType 事件类型
|
||||
@@ -82,7 +82,7 @@ export interface IEventBus {
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
on<T>(eventType: string, handler: (data: T) => void, config?: IEventListenerConfig): string;
|
||||
|
||||
|
||||
/**
|
||||
* 监听事件(一次性)
|
||||
* @param eventType 事件类型
|
||||
@@ -91,7 +91,7 @@ export interface IEventBus {
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
once<T>(eventType: string, handler: (data: T) => void, config?: IEventListenerConfig): string;
|
||||
|
||||
|
||||
/**
|
||||
* 异步监听事件
|
||||
* @param eventType 事件类型
|
||||
@@ -100,32 +100,32 @@ export interface IEventBus {
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
onAsync<T>(eventType: string, handler: (data: T) => Promise<void>, config?: IEventListenerConfig): string;
|
||||
|
||||
|
||||
/**
|
||||
* 移除事件监听器
|
||||
* @param eventType 事件类型
|
||||
* @param listenerId 监听器ID
|
||||
*/
|
||||
off(eventType: string, listenerId: string): boolean;
|
||||
|
||||
|
||||
/**
|
||||
* 移除指定事件类型的所有监听器
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
offAll(eventType: string): void;
|
||||
|
||||
|
||||
/**
|
||||
* 检查是否有指定事件的监听器
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
hasListeners(eventType: string): boolean;
|
||||
|
||||
|
||||
/**
|
||||
* 获取事件统计信息
|
||||
* @param eventType 事件类型(可选)
|
||||
*/
|
||||
getStats(eventType?: string): IEventStats | Map<string, IEventStats>;
|
||||
|
||||
|
||||
/**
|
||||
* 清空所有监听器
|
||||
*/
|
||||
@@ -499,4 +499,4 @@ export interface ISceneDebugData {
|
||||
sceneMemory: number;
|
||||
/** 场景启动时间 */
|
||||
sceneUptime: number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,18 +47,18 @@ export class ComponentDataCollector {
|
||||
});
|
||||
|
||||
// 获取池利用率信息
|
||||
let poolUtilizations = new Map<string, number>();
|
||||
let poolSizes = new Map<string, number>();
|
||||
|
||||
const poolUtilizations = new Map<string, number>();
|
||||
const poolSizes = new Map<string, number>();
|
||||
|
||||
try {
|
||||
const poolManager = ComponentPoolManager.getInstance();
|
||||
const poolStats = poolManager.getPoolStats();
|
||||
const utilizations = poolManager.getPoolUtilization();
|
||||
|
||||
|
||||
for (const [typeName, stats] of poolStats.entries()) {
|
||||
poolSizes.set(typeName, stats.maxSize);
|
||||
}
|
||||
|
||||
|
||||
for (const [typeName, util] of utilizations.entries()) {
|
||||
poolUtilizations.set(typeName, util.utilization);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ export class ComponentDataCollector {
|
||||
const poolUtilization = poolUtilizations.get(typeName) || 0;
|
||||
// 使用预估的基础内存大小,避免每帧计算
|
||||
const memoryPerInstance = this.getEstimatedComponentSize(typeName, scene);
|
||||
|
||||
|
||||
return {
|
||||
typeName,
|
||||
instanceCount: stats.count,
|
||||
@@ -97,12 +97,12 @@ export class ComponentDataCollector {
|
||||
}
|
||||
|
||||
if (!scene) return 64;
|
||||
|
||||
|
||||
const entityList = (scene as any).entities;
|
||||
if (!entityList?.buffer) return 64;
|
||||
|
||||
|
||||
let calculatedSize = 64;
|
||||
|
||||
|
||||
try {
|
||||
for (const entity of entityList.buffer) {
|
||||
if (entity.components) {
|
||||
@@ -116,25 +116,25 @@ export class ComponentDataCollector {
|
||||
} catch (error) {
|
||||
calculatedSize = 64;
|
||||
}
|
||||
|
||||
|
||||
ComponentDataCollector.componentSizeCache.set(typeName, calculatedSize);
|
||||
return calculatedSize;
|
||||
}
|
||||
|
||||
private calculateQuickObjectSize(obj: any): number {
|
||||
if (!obj || typeof obj !== 'object') return 8;
|
||||
|
||||
|
||||
let size = 32;
|
||||
const visited = new WeakSet();
|
||||
|
||||
|
||||
const calculate = (item: any, depth: number = 0): number => {
|
||||
if (!item || typeof item !== 'object' || visited.has(item) || depth > 3) {
|
||||
return 0;
|
||||
}
|
||||
visited.add(item);
|
||||
|
||||
|
||||
let itemSize = 0;
|
||||
|
||||
|
||||
try {
|
||||
const keys = Object.keys(item);
|
||||
for (let i = 0; i < Math.min(keys.length, 20); i++) {
|
||||
@@ -143,7 +143,7 @@ export class ComponentDataCollector {
|
||||
|
||||
const value = item[key];
|
||||
itemSize += key.length * 2;
|
||||
|
||||
|
||||
if (typeof value === 'string') {
|
||||
itemSize += Math.min(value.length * 2, 200);
|
||||
} else if (typeof value === 'number') {
|
||||
@@ -157,10 +157,10 @@ export class ComponentDataCollector {
|
||||
} catch (error) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
|
||||
return itemSize;
|
||||
};
|
||||
|
||||
|
||||
size += calculate(obj);
|
||||
return Math.max(size, 32);
|
||||
}
|
||||
@@ -176,7 +176,7 @@ export class ComponentDataCollector {
|
||||
|
||||
const entityList = (scene as any).entities;
|
||||
if (!entityList?.buffer) return this.getEstimatedComponentSize(typeName, scene);
|
||||
|
||||
|
||||
try {
|
||||
// 找到第一个包含此组件的实体,分析组件大小
|
||||
for (const entity of entityList.buffer) {
|
||||
@@ -190,7 +190,7 @@ export class ComponentDataCollector {
|
||||
} catch (error) {
|
||||
// 忽略错误,使用估算值
|
||||
}
|
||||
|
||||
|
||||
return this.getEstimatedComponentSize(typeName, scene);
|
||||
}
|
||||
|
||||
@@ -201,10 +201,10 @@ export class ComponentDataCollector {
|
||||
private estimateObjectSize(obj: any, visited = new WeakSet(), depth = 0): number {
|
||||
if (obj === null || obj === undefined || depth > 10) return 0;
|
||||
if (visited.has(obj)) return 0;
|
||||
|
||||
|
||||
let size = 0;
|
||||
const type = typeof obj;
|
||||
|
||||
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
size = 4;
|
||||
@@ -217,7 +217,7 @@ export class ComponentDataCollector {
|
||||
break;
|
||||
case 'object':
|
||||
visited.add(obj);
|
||||
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
size = 40 + (obj.length * 8);
|
||||
const maxElements = Math.min(obj.length, 50);
|
||||
@@ -226,11 +226,11 @@ export class ComponentDataCollector {
|
||||
}
|
||||
} else {
|
||||
size = 32;
|
||||
|
||||
|
||||
try {
|
||||
const ownKeys = Object.getOwnPropertyNames(obj);
|
||||
const maxProps = Math.min(ownKeys.length, 30);
|
||||
|
||||
|
||||
for (let i = 0; i < maxProps; i++) {
|
||||
const key = ownKeys[i];
|
||||
if (!key) continue;
|
||||
@@ -263,11 +263,11 @@ export class ComponentDataCollector {
|
||||
default:
|
||||
size = 8;
|
||||
}
|
||||
|
||||
|
||||
return Math.ceil(size / 8) * 8;
|
||||
}
|
||||
|
||||
public static clearCache(): void {
|
||||
ComponentDataCollector.componentSizeCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ export class DebugManager implements IService, IUpdatable {
|
||||
* 格式化日志消息
|
||||
*/
|
||||
private formatLogMessage(args: unknown[]): string {
|
||||
return args.map(arg => {
|
||||
return args.map((arg) => {
|
||||
if (typeof arg === 'string') return arg;
|
||||
if (arg instanceof Error) return `${arg.name}: ${arg.message}`;
|
||||
if (arg === null) return 'null';
|
||||
@@ -173,7 +173,7 @@ export class DebugManager implements IService, IUpdatable {
|
||||
seen.add(value);
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const result = value.map(item => stringify(item, depth + 1));
|
||||
const result = value.map((item) => stringify(item, depth + 1));
|
||||
seen.delete(value);
|
||||
return result;
|
||||
}
|
||||
@@ -436,9 +436,6 @@ export class DebugManager implements IService, IUpdatable {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 处理内存快照请求
|
||||
*/
|
||||
@@ -519,7 +516,7 @@ export class DebugManager implements IService, IUpdatable {
|
||||
jsHeapSizeLimit: number;
|
||||
} | null;
|
||||
detailedMemory?: unknown;
|
||||
} {
|
||||
} {
|
||||
const memoryInfo = {
|
||||
totalMemory: 0,
|
||||
usedMemory: 0,
|
||||
@@ -575,7 +572,6 @@ export class DebugManager implements IService, IUpdatable {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 收集组件内存统计(仅用于内存快照)
|
||||
*/
|
||||
@@ -672,7 +668,7 @@ export class DebugManager implements IService, IUpdatable {
|
||||
enabled: boolean;
|
||||
updateOrder: number;
|
||||
}>;
|
||||
} {
|
||||
} {
|
||||
const scene = this.sceneManager.currentScene;
|
||||
let totalSystemMemory = 0;
|
||||
const systemBreakdown: Array<{
|
||||
@@ -766,7 +762,7 @@ export class DebugManager implements IService, IUpdatable {
|
||||
utilization: number;
|
||||
hitRate?: number;
|
||||
}>;
|
||||
} {
|
||||
} {
|
||||
let totalPoolMemory = 0;
|
||||
const poolBreakdown: Array<{
|
||||
typeName: string;
|
||||
@@ -845,7 +841,7 @@ export class DebugManager implements IService, IUpdatable {
|
||||
samples: number;
|
||||
}>;
|
||||
error?: string;
|
||||
} {
|
||||
} {
|
||||
try {
|
||||
if (!this.performanceMonitor) {
|
||||
return { enabled: false };
|
||||
@@ -959,4 +955,4 @@ export class DebugManager implements IService, IUpdatable {
|
||||
console.warn = this.originalConsole.warn;
|
||||
console.error = this.originalConsole.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,10 +641,10 @@ export class EntityDataCollector {
|
||||
componentTypes: baseDebugInfo.componentTypes || componentDetails.map((comp) => comp.typeName),
|
||||
cachePerformance: componentCacheStats
|
||||
? {
|
||||
hitRate: componentCacheStats.cacheStats.hitRate,
|
||||
size: componentCacheStats.cacheStats.size,
|
||||
maxSize: componentCacheStats.cacheStats.maxSize
|
||||
}
|
||||
hitRate: componentCacheStats.cacheStats.hitRate,
|
||||
size: componentCacheStats.cacheStats.size,
|
||||
maxSize: componentCacheStats.cacheStats.maxSize
|
||||
}
|
||||
: null
|
||||
};
|
||||
});
|
||||
|
||||
@@ -17,11 +17,11 @@ export class PerformanceDataCollector {
|
||||
const frameTimeSeconds = Time.deltaTime;
|
||||
const engineFrameTimeMs = frameTimeSeconds * 1000;
|
||||
const currentFps = frameTimeSeconds > 0 ? Math.round(1 / frameTimeSeconds) : 0;
|
||||
|
||||
|
||||
const ecsPerformanceData = this.getECSPerformanceData(performanceMonitor);
|
||||
const ecsExecutionTimeMs = ecsPerformanceData.totalExecutionTime;
|
||||
const ecsPercentage = engineFrameTimeMs > 0 ? (ecsExecutionTimeMs / engineFrameTimeMs * 100) : 0;
|
||||
|
||||
|
||||
let memoryUsage = 0;
|
||||
if ((performance as any).memory) {
|
||||
memoryUsage = (performance as any).memory.usedJSHeapSize / 1024 / 1024;
|
||||
@@ -32,9 +32,9 @@ export class PerformanceDataCollector {
|
||||
if (this.frameTimeHistory.length > this.maxHistoryLength) {
|
||||
this.frameTimeHistory.shift();
|
||||
}
|
||||
|
||||
|
||||
// 计算ECS执行时间统计
|
||||
const history = this.frameTimeHistory.filter(t => t >= 0);
|
||||
const history = this.frameTimeHistory.filter((t) => t >= 0);
|
||||
const averageECSTime = history.length > 0 ? history.reduce((a, b) => a + b, 0) / history.length : ecsExecutionTimeMs;
|
||||
const minECSTime = history.length > 0 ? Math.min(...history) : ecsExecutionTimeMs;
|
||||
const maxECSTime = history.length > 0 ? Math.max(...history) : ecsExecutionTimeMs;
|
||||
@@ -77,20 +77,20 @@ export class PerformanceDataCollector {
|
||||
try {
|
||||
let totalTime = 0;
|
||||
const systemBreakdown = [];
|
||||
|
||||
|
||||
const stats = performanceMonitor.getAllSystemStats();
|
||||
|
||||
|
||||
if (stats.size === 0) {
|
||||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||
}
|
||||
|
||||
|
||||
// 计算各系统的执行时间
|
||||
for (const [systemName, stat] of stats.entries()) {
|
||||
// 使用最近的执行时间而不是平均时间,这样更能反映当前状态
|
||||
const systemTime = stat.recentTimes && stat.recentTimes.length > 0 ?
|
||||
stat.recentTimes[stat.recentTimes.length - 1] :
|
||||
const systemTime = stat.recentTimes && stat.recentTimes.length > 0 ?
|
||||
stat.recentTimes[stat.recentTimes.length - 1] :
|
||||
(stat.averageTime || 0);
|
||||
|
||||
|
||||
totalTime += systemTime;
|
||||
systemBreakdown.push({
|
||||
systemName: systemName,
|
||||
@@ -98,15 +98,15 @@ export class PerformanceDataCollector {
|
||||
percentage: 0 // 后面计算
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 计算各系统占ECS总时间的百分比
|
||||
systemBreakdown.forEach(system => {
|
||||
systemBreakdown.forEach((system) => {
|
||||
system.percentage = totalTime > 0 ? (system.executionTime / totalTime * 100) : 0;
|
||||
});
|
||||
|
||||
|
||||
// 按执行时间排序
|
||||
systemBreakdown.sort((a, b) => b.executionTime - a.executionTime);
|
||||
|
||||
|
||||
return {
|
||||
totalExecutionTime: totalTime,
|
||||
systemBreakdown: systemBreakdown
|
||||
@@ -127,7 +127,7 @@ export class PerformanceDataCollector {
|
||||
try {
|
||||
const stats = performanceMonitor.getAllSystemStats();
|
||||
const systemData = performanceMonitor.getAllSystemData();
|
||||
|
||||
|
||||
return Array.from(stats.entries() as Iterable<[string, any]>).map(([systemName, stat]) => {
|
||||
const data = systemData.get(systemName);
|
||||
return {
|
||||
@@ -167,7 +167,7 @@ export class PerformanceDataCollector {
|
||||
memoryInfo.totalMemory = perfMemory.jsHeapSizeLimit || 512 * 1024 * 1024;
|
||||
memoryInfo.usedMemory = perfMemory.usedJSHeapSize || 0;
|
||||
memoryInfo.freeMemory = memoryInfo.totalMemory - memoryInfo.usedMemory;
|
||||
|
||||
|
||||
// 检测GC:如果使用的内存突然大幅减少,可能发生了GC
|
||||
if (this.lastMemoryCheck > 0) {
|
||||
const memoryDrop = this.lastMemoryCheck - memoryInfo.usedMemory;
|
||||
@@ -207,16 +207,16 @@ export class PerformanceDataCollector {
|
||||
// 实际的GC检测需要更复杂的逻辑
|
||||
return this.gcCollections;
|
||||
}
|
||||
|
||||
|
||||
// 如果有其他GC检测API,可以在这里添加
|
||||
if ((performance as any).measureUserAgentSpecificMemory) {
|
||||
// 实验性API,可能不可用
|
||||
return this.gcCollections;
|
||||
}
|
||||
|
||||
|
||||
return this.gcCollections;
|
||||
} catch (error) {
|
||||
return this.gcCollections;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,4 +47,4 @@ export class SceneDataCollector {
|
||||
public setSceneStartTime(time: number): void {
|
||||
this.sceneStartTime = time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ export class SystemDataCollector {
|
||||
}
|
||||
|
||||
const systems = entityProcessors.processors || [];
|
||||
|
||||
|
||||
// 获取性能监控数据
|
||||
let systemStats: Map<string, any> = new Map();
|
||||
let systemData: Map<string, any> = new Map();
|
||||
|
||||
|
||||
if (performanceMonitor) {
|
||||
try {
|
||||
systemStats = performanceMonitor.getAllSystemStats();
|
||||
@@ -41,14 +41,14 @@ export class SystemDataCollector {
|
||||
// 忽略错误,使用空的Map
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
totalSystems: systems.length,
|
||||
systemsInfo: systems.map((system: any) => {
|
||||
const systemName = system.systemName || getSystemInstanceTypeName(system);
|
||||
const stats = systemStats.get(systemName);
|
||||
const data = systemData.get(systemName);
|
||||
|
||||
|
||||
return {
|
||||
name: systemName,
|
||||
type: getSystemInstanceTypeName(system),
|
||||
@@ -64,4 +64,4 @@ export class SystemDataCollector {
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export class WebSocketManager {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this.ws = new WebSocket(this.url);
|
||||
|
||||
|
||||
this.ws.onopen = (event) => {
|
||||
this.handleOpen(event);
|
||||
resolve();
|
||||
@@ -112,7 +112,7 @@ export class WebSocketManager {
|
||||
this.reconnectAttempts++;
|
||||
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.connect().catch(_error => {
|
||||
this.connect().catch((_error) => {
|
||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
@@ -137,7 +137,7 @@ export class WebSocketManager {
|
||||
private handleOpen(event: Event): void {
|
||||
this.isConnected = true;
|
||||
this.reconnectAttempts = 0;
|
||||
|
||||
|
||||
if (this.onOpen) {
|
||||
this.onOpen(event);
|
||||
}
|
||||
@@ -145,11 +145,11 @@ export class WebSocketManager {
|
||||
|
||||
private handleClose(event: CloseEvent): void {
|
||||
this.isConnected = false;
|
||||
|
||||
|
||||
if (this.onClose) {
|
||||
this.onClose(event);
|
||||
}
|
||||
|
||||
|
||||
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
@@ -166,4 +166,4 @@ export class WebSocketManager {
|
||||
this.onError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ export { ComponentDataCollector } from './ComponentDataCollector';
|
||||
export { SceneDataCollector } from './SceneDataCollector';
|
||||
export { WebSocketManager } from './WebSocketManager';
|
||||
export { DebugManager } from './DebugManager';
|
||||
export { DebugConfigService } from './DebugConfigService';
|
||||
export { DebugConfigService } from './DebugConfigService';
|
||||
|
||||
@@ -47,9 +47,9 @@ export class Emitter<T, TContext = unknown> {
|
||||
* @param handler 事件函数
|
||||
*/
|
||||
public removeObserver(eventType: T, handler: Function) {
|
||||
let messageData = this._messageTable.get(eventType);
|
||||
const messageData = this._messageTable.get(eventType);
|
||||
if (messageData) {
|
||||
let index = messageData.findIndex(data => data.func == handler);
|
||||
const index = messageData.findIndex((data) => data.func == handler);
|
||||
if (index != -1)
|
||||
messageData.splice(index, 1);
|
||||
}
|
||||
@@ -61,9 +61,9 @@ export class Emitter<T, TContext = unknown> {
|
||||
* @param data 事件数据
|
||||
*/
|
||||
public emit<TData = unknown>(eventType: T, ...data: TData[]) {
|
||||
let list = this._messageTable.get(eventType);
|
||||
const list = this._messageTable.get(eventType);
|
||||
if (list) {
|
||||
for (let observer of list) {
|
||||
for (const observer of list) {
|
||||
observer.func.call(observer.context, ...data);
|
||||
}
|
||||
}
|
||||
@@ -75,8 +75,8 @@ export class Emitter<T, TContext = unknown> {
|
||||
* @param handler 事件函数
|
||||
*/
|
||||
public hasObserver(eventType: T, handler: Function): boolean {
|
||||
let list = this._messageTable.get(eventType);
|
||||
return list ? list.some(observer => observer.func === handler) : false;
|
||||
const list = this._messageTable.get(eventType);
|
||||
return list ? list.some((observer) => observer.func === handler) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,4 +12,4 @@ export class NumberExtension {
|
||||
if (value == undefined) return 0;
|
||||
return Number(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ export class TypeUtils {
|
||||
public static getType(obj: any) {
|
||||
return obj.constructor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// 扩展工具类导出
|
||||
export { TypeUtils } from './TypeUtils';
|
||||
export { NumberExtension } from './NumberExtension';
|
||||
export { NumberExtension } from './NumberExtension';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Colors, LogLevel } from "./Constants";
|
||||
import { ILogger, LoggerColorConfig, LoggerConfig } from "./Types";
|
||||
import { Colors, LogLevel } from './Constants';
|
||||
import { ILogger, LoggerColorConfig, LoggerConfig } from './Types';
|
||||
|
||||
|
||||
/**
|
||||
@@ -138,7 +138,7 @@ export class ConsoleLogger implements ILogger {
|
||||
*/
|
||||
private outputToConsole(level: LogLevel, message: string, ...args: unknown[]): void {
|
||||
const colors = this._config.enableColors ? this.getColors() : null;
|
||||
|
||||
|
||||
switch (level) {
|
||||
case LogLevel.Debug:
|
||||
if (colors) {
|
||||
|
||||
@@ -23,7 +23,7 @@ export const Colors = {
|
||||
MAGENTA: '\x1b[35m',
|
||||
CYAN: '\x1b[36m',
|
||||
WHITE: '\x1b[37m',
|
||||
|
||||
|
||||
// 亮色版本
|
||||
BRIGHT_BLACK: '\x1b[90m',
|
||||
BRIGHT_RED: '\x1b[91m',
|
||||
@@ -33,7 +33,7 @@ export const Colors = {
|
||||
BRIGHT_MAGENTA: '\x1b[95m',
|
||||
BRIGHT_CYAN: '\x1b[96m',
|
||||
BRIGHT_WHITE: '\x1b[97m',
|
||||
|
||||
|
||||
// 特殊
|
||||
RESET: '\x1b[0m',
|
||||
BOLD: '\x1b[1m',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ConsoleLogger } from "./ConsoleLogger";
|
||||
import { LogLevel } from "./Constants";
|
||||
import { ILogger, LoggerColorConfig } from "./Types";
|
||||
import { ConsoleLogger } from './ConsoleLogger';
|
||||
import { LogLevel } from './Constants';
|
||||
import { ILogger, LoggerColorConfig } from './Types';
|
||||
|
||||
/**
|
||||
* 日志管理器
|
||||
@@ -186,7 +186,7 @@ export function setGlobalLogLevel(level: LogLevel): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置日志器工厂方法
|
||||
* 设置日志器工厂方法
|
||||
* @param factory 日志器工厂方法
|
||||
*/
|
||||
export function setLoggerFactory(factory: (name?: string) => ILogger): void {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { LogLevel } from "./Constants";
|
||||
import type { LogLevel } from './Constants';
|
||||
|
||||
/**
|
||||
* 日志接口
|
||||
|
||||
@@ -183,7 +183,7 @@ export class PerformanceMonitor implements IService {
|
||||
*/
|
||||
private updateStats(systemName: string, executionTime: number): void {
|
||||
let stats = this._systemStats.get(systemName);
|
||||
|
||||
|
||||
if (!stats) {
|
||||
stats = {
|
||||
totalTime: 0,
|
||||
@@ -231,7 +231,7 @@ export class PerformanceMonitor implements IService {
|
||||
// 计算百分位数
|
||||
const sortedTimes = [...stats.recentTimes].sort((a, b) => a - b);
|
||||
const len = sortedTimes.length;
|
||||
|
||||
|
||||
stats.percentile95 = sortedTimes[Math.floor(len * 0.95)] || 0;
|
||||
stats.percentile99 = sortedTimes[Math.floor(len * 0.99)] || 0;
|
||||
}
|
||||
@@ -276,12 +276,12 @@ export class PerformanceMonitor implements IService {
|
||||
*/
|
||||
public getPerformanceReport(): string {
|
||||
if (!this._isEnabled) {
|
||||
return "Performance monitoring is disabled.";
|
||||
return 'Performance monitoring is disabled.';
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push("=== ECS Performance Report ===");
|
||||
lines.push("");
|
||||
lines.push('=== ECS Performance Report ===');
|
||||
lines.push('');
|
||||
|
||||
// 按平均执行时间排序
|
||||
const sortedSystems = Array.from(this._systemStats.entries())
|
||||
@@ -289,24 +289,24 @@ export class PerformanceMonitor implements IService {
|
||||
|
||||
for (const [systemName, stats] of sortedSystems) {
|
||||
const data = this._systemData.get(systemName);
|
||||
|
||||
|
||||
lines.push(`System: ${systemName}`);
|
||||
lines.push(` Current: ${data?.executionTime.toFixed(2)}ms (${data?.entityCount} entities)`);
|
||||
lines.push(` Average: ${stats.averageTime.toFixed(2)}ms`);
|
||||
lines.push(` Min/Max: ${stats.minTime.toFixed(2)}ms / ${stats.maxTime.toFixed(2)}ms`);
|
||||
lines.push(` Total: ${stats.totalTime.toFixed(2)}ms (${stats.executionCount} calls)`);
|
||||
|
||||
|
||||
if (data?.averageTimePerEntity && data.averageTimePerEntity > 0) {
|
||||
lines.push(` Per Entity: ${data.averageTimePerEntity.toFixed(4)}ms`);
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
// 总体统计
|
||||
const totalCurrentTime = Array.from(this._systemData.values())
|
||||
.reduce((sum, data) => sum + data.executionTime, 0);
|
||||
|
||||
|
||||
lines.push(`Total Frame Time: ${totalCurrentTime.toFixed(2)}ms`);
|
||||
lines.push(`Systems Count: ${this._systemData.size}`);
|
||||
|
||||
@@ -337,13 +337,13 @@ export class PerformanceMonitor implements IService {
|
||||
*/
|
||||
public getPerformanceWarnings(thresholdMs: number = 16.67): string[] {
|
||||
const warnings: string[] = [];
|
||||
|
||||
|
||||
for (const [systemName, data] of this._systemData.entries()) {
|
||||
if (data.executionTime > thresholdMs) {
|
||||
warnings.push(`${systemName}: ${data.executionTime.toFixed(2)}ms (>${thresholdMs}ms)`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
@@ -370,4 +370,4 @@ export class PerformanceMonitor implements IService {
|
||||
this._systemStats.clear();
|
||||
this._isEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,4 +26,4 @@ export interface PoolStats {
|
||||
hitRate: number;
|
||||
/** 内存使用估算(字节) */
|
||||
estimatedMemoryUsage: number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { IPoolable, PoolStats } from './IPoolable';
|
||||
*/
|
||||
export class Pool<T extends IPoolable> {
|
||||
private static _pools = new Map<Function, Pool<any>>();
|
||||
|
||||
|
||||
private _objects: T[] = [];
|
||||
private _createFn: () => T;
|
||||
private _maxSize: number;
|
||||
@@ -42,17 +42,17 @@ export class Pool<T extends IPoolable> {
|
||||
* @returns 对象池实例
|
||||
*/
|
||||
public static getPool<T extends IPoolable>(
|
||||
type: new (...args: unknown[]) => T,
|
||||
type: new (...args: unknown[]) => T,
|
||||
maxSize: number = 100,
|
||||
estimatedObjectSize: number = 1024
|
||||
): Pool<T> {
|
||||
let pool = this._pools.get(type);
|
||||
|
||||
|
||||
if (!pool) {
|
||||
pool = new Pool<T>(() => new type(), maxSize, estimatedObjectSize);
|
||||
this._pools.set(type, pool);
|
||||
}
|
||||
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export class Pool<T extends IPoolable> {
|
||||
*/
|
||||
public obtain(): T {
|
||||
this._stats.totalObtained++;
|
||||
|
||||
|
||||
if (this._objects.length > 0) {
|
||||
const obj = this._objects.pop()!;
|
||||
this._stats.size--;
|
||||
@@ -70,7 +70,7 @@ export class Pool<T extends IPoolable> {
|
||||
this._updateMemoryUsage();
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
// 池中没有可用对象,创建新对象
|
||||
this._stats.totalCreated++;
|
||||
this._updateHitRate();
|
||||
@@ -83,9 +83,9 @@ export class Pool<T extends IPoolable> {
|
||||
*/
|
||||
public release(obj: T): void {
|
||||
if (!obj) return;
|
||||
|
||||
|
||||
this._stats.totalReleased++;
|
||||
|
||||
|
||||
// 如果池未满,将对象放回池中
|
||||
if (this._stats.size < this._maxSize) {
|
||||
// 重置对象状态
|
||||
@@ -113,7 +113,7 @@ export class Pool<T extends IPoolable> {
|
||||
for (const obj of this._objects) {
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
|
||||
this._objects.length = 0;
|
||||
this._stats.size = 0;
|
||||
this._updateMemoryUsage();
|
||||
@@ -125,7 +125,7 @@ export class Pool<T extends IPoolable> {
|
||||
*/
|
||||
public compact(targetSize?: number): void {
|
||||
const target = targetSize ?? Math.floor(this._objects.length / 2);
|
||||
|
||||
|
||||
while (this._objects.length > target) {
|
||||
const obj = this._objects.pop();
|
||||
if (obj) {
|
||||
@@ -133,7 +133,7 @@ export class Pool<T extends IPoolable> {
|
||||
this._stats.size--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this._updateMemoryUsage();
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ export class Pool<T extends IPoolable> {
|
||||
*/
|
||||
public prewarm(count: number): void {
|
||||
const actualCount = Math.min(count, this._maxSize - this._objects.length);
|
||||
|
||||
|
||||
for (let i = 0; i < actualCount; i++) {
|
||||
const obj = this._createFn();
|
||||
obj.reset();
|
||||
@@ -151,7 +151,7 @@ export class Pool<T extends IPoolable> {
|
||||
this._stats.totalCreated++;
|
||||
this._stats.size++;
|
||||
}
|
||||
|
||||
|
||||
this._updateMemoryUsage();
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ export class Pool<T extends IPoolable> {
|
||||
public setMaxSize(maxSize: number): void {
|
||||
this._maxSize = maxSize;
|
||||
this._stats.maxSize = maxSize;
|
||||
|
||||
|
||||
// 如果当前池大小超过新的最大值,进行压缩
|
||||
if (this._objects.length > maxSize) {
|
||||
this.compact(maxSize);
|
||||
@@ -207,12 +207,12 @@ export class Pool<T extends IPoolable> {
|
||||
*/
|
||||
public static getAllPoolStats(): Record<string, PoolStats> {
|
||||
const stats: Record<string, PoolStats> = {};
|
||||
|
||||
|
||||
for (const [type, pool] of this._pools) {
|
||||
const typeName = type.name || type.toString();
|
||||
stats[typeName] = pool.getStats();
|
||||
}
|
||||
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
@@ -242,12 +242,12 @@ export class Pool<T extends IPoolable> {
|
||||
public static getGlobalStatsString(): string {
|
||||
const stats = this.getAllPoolStats();
|
||||
const lines: string[] = ['=== Object Pool Global Statistics ===', ''];
|
||||
|
||||
|
||||
if (Object.keys(stats).length === 0) {
|
||||
lines.push('No pools registered');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
|
||||
for (const [typeName, stat] of Object.entries(stats)) {
|
||||
lines.push(`${typeName}:`);
|
||||
lines.push(` Size: ${stat.size}/${stat.maxSize}`);
|
||||
@@ -257,7 +257,7 @@ export class Pool<T extends IPoolable> {
|
||||
lines.push(` Memory: ${(stat.estimatedMemoryUsage / 1024).toFixed(1)} KB`);
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
@@ -279,4 +279,4 @@ export class Pool<T extends IPoolable> {
|
||||
private _updateMemoryUsage(): void {
|
||||
this._stats.estimatedMemoryUsage = this._stats.size * this._objectSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export class PoolManager implements IService {
|
||||
*/
|
||||
public update(): void {
|
||||
const now = Date.now();
|
||||
|
||||
|
||||
if (now - this.lastCompactTime > this.autoCompactInterval) {
|
||||
this.compactAllPools();
|
||||
this.lastCompactTime = now;
|
||||
@@ -60,12 +60,12 @@ export class PoolManager implements IService {
|
||||
estimatedObjectSize: number = 1024
|
||||
): Pool<T> {
|
||||
let pool = this.pools.get(name) as Pool<T>;
|
||||
|
||||
|
||||
if (!pool) {
|
||||
pool = new Pool(createFn, maxSize, estimatedObjectSize);
|
||||
this.pools.set(name, pool);
|
||||
}
|
||||
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
@@ -125,11 +125,11 @@ export class PoolManager implements IService {
|
||||
*/
|
||||
public getAllStats(): Map<string, PoolStats> {
|
||||
const stats = new Map<string, PoolStats>();
|
||||
|
||||
|
||||
for (const [name, pool] of this.pools) {
|
||||
stats.set(name, pool.getStats());
|
||||
}
|
||||
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ export class PoolManager implements IService {
|
||||
let totalObtained = 0;
|
||||
let totalReleased = 0;
|
||||
let totalMemoryUsage = 0;
|
||||
|
||||
|
||||
for (const pool of this.pools.values()) {
|
||||
const stats = pool.getStats();
|
||||
totalSize += stats.size;
|
||||
@@ -154,9 +154,9 @@ export class PoolManager implements IService {
|
||||
totalReleased += stats.totalReleased;
|
||||
totalMemoryUsage += stats.estimatedMemoryUsage;
|
||||
}
|
||||
|
||||
|
||||
const hitRate = totalObtained === 0 ? 0 : (totalObtained - totalCreated) / totalObtained;
|
||||
|
||||
|
||||
return {
|
||||
size: totalSize,
|
||||
maxSize: totalMaxSize,
|
||||
@@ -174,18 +174,18 @@ export class PoolManager implements IService {
|
||||
*/
|
||||
public getStatsString(): string {
|
||||
const lines: string[] = ['=== Pool Manager Statistics ===', ''];
|
||||
|
||||
|
||||
if (this.pools.size === 0) {
|
||||
lines.push('No pools registered');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
|
||||
const globalStats = this.getGlobalStats();
|
||||
lines.push(`Total Pools: ${this.pools.size}`);
|
||||
lines.push(`Global Hit Rate: ${(globalStats.hitRate * 100).toFixed(1)}%`);
|
||||
lines.push(`Global Memory Usage: ${(globalStats.estimatedMemoryUsage / 1024).toFixed(1)} KB`);
|
||||
lines.push('');
|
||||
|
||||
|
||||
for (const [name, pool] of this.pools) {
|
||||
const stats = pool.getStats();
|
||||
lines.push(`${name}:`);
|
||||
@@ -194,7 +194,7 @@ export class PoolManager implements IService {
|
||||
lines.push(` Memory: ${(stats.estimatedMemoryUsage / 1024).toFixed(1)} KB`);
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
@@ -233,4 +233,4 @@ export class PoolManager implements IService {
|
||||
public dispose(): void {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './IPoolable';
|
||||
export * from './Pool';
|
||||
export * from './PoolManager';
|
||||
export * from './PoolManager';
|
||||
|
||||
@@ -7,27 +7,27 @@ export class Time {
|
||||
* 上一帧到当前帧的时间间隔(秒)
|
||||
*/
|
||||
public static deltaTime: number = 0;
|
||||
|
||||
|
||||
/**
|
||||
* 未缩放的帧时间间隔(秒)
|
||||
*/
|
||||
public static unscaledDeltaTime: number = 0;
|
||||
|
||||
|
||||
/**
|
||||
* 游戏开始以来的总时间(秒)
|
||||
*/
|
||||
public static totalTime: number = 0;
|
||||
|
||||
|
||||
/**
|
||||
* 未缩放的总时间(秒)
|
||||
*/
|
||||
public static unscaledTotalTime: number = 0;
|
||||
|
||||
|
||||
/**
|
||||
* 时间缩放比例
|
||||
*/
|
||||
public static timeScale: number = 1;
|
||||
|
||||
|
||||
/**
|
||||
* 当前帧数
|
||||
*/
|
||||
@@ -70,4 +70,4 @@ export class Time {
|
||||
public static checkEvery(interval: number, lastTime: number): boolean {
|
||||
return this.totalTime - lastTime >= interval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,4 +15,4 @@ export interface ITimer<TContext = unknown> {
|
||||
* 返回投向T的上下文,作为方便
|
||||
*/
|
||||
getContext<T>(): T;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,4 +67,4 @@ export class Timer<TContext = unknown> implements ITimer<TContext>{
|
||||
this.context = null as unknown as TContext;
|
||||
this._onTime = null!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export class TimerManager implements IService, IUpdatable {
|
||||
* @param onTime
|
||||
*/
|
||||
public schedule<TContext = unknown>(timeInSeconds: number, repeats: boolean, context: TContext, onTime: (timer: ITimer<TContext>)=>void): Timer<TContext> {
|
||||
let timer = new Timer<TContext>();
|
||||
const timer = new Timer<TContext>();
|
||||
timer.initialize(timeInSeconds, repeats, context, onTime);
|
||||
this._timers.push(timer as Timer<unknown>);
|
||||
|
||||
@@ -46,4 +46,4 @@ export class TimerManager implements IService, IUpdatable {
|
||||
}
|
||||
this._timers = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ export * from './PerformanceMonitor';
|
||||
export { Time } from './Time';
|
||||
export * from './Debug';
|
||||
export * from './Logger';
|
||||
export * from './BinarySerializer';
|
||||
export * from './BinarySerializer';
|
||||
|
||||
@@ -36,13 +36,13 @@ export { ITimer } from './Utils/Timers/ITimer';
|
||||
export { Timer } from './Utils/Timers/Timer';
|
||||
|
||||
// 日志系统
|
||||
export {
|
||||
LoggerManager,
|
||||
ConsoleLogger,
|
||||
Logger,
|
||||
createLogger,
|
||||
export {
|
||||
LoggerManager,
|
||||
ConsoleLogger,
|
||||
Logger,
|
||||
createLogger,
|
||||
setGlobalLogLevel,
|
||||
LogLevel
|
||||
LogLevel
|
||||
} from './Utils/Logger';
|
||||
export type { ILogger, LoggerConfig } from './Utils/Logger';
|
||||
|
||||
@@ -64,4 +64,4 @@ export * from './Types';
|
||||
export { ComponentPool, ComponentPoolManager } from './ECS/Core/Storage';
|
||||
|
||||
// 平台适配
|
||||
export * from './Platform';
|
||||
export * from './Platform';
|
||||
|
||||
Reference in New Issue
Block a user