style(core): ESLint自动修复代码格式问题 (#210)
This commit is contained in:
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user