使用Lerna 和 monorepo管理项目结构
This commit is contained in:
746
packages/core/src/ECS/Utils/BigIntCompatibility.ts
Normal file
746
packages/core/src/ECS/Utils/BigIntCompatibility.ts
Normal file
@@ -0,0 +1,746 @@
|
||||
/**
|
||||
* BigInt兼容性抽象层
|
||||
*
|
||||
* 为不支持BigInt的环境提供兼容实现,确保ECS框架在所有平台上都能正常运行。
|
||||
* 自动检测运行时环境的BigInt支持情况,并提供统一的接口。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 创建兼容的BigInt值
|
||||
* const value = BigIntFactory.create(123);
|
||||
*
|
||||
* // 位运算
|
||||
* const result = value.or(BigIntFactory.create(456));
|
||||
*
|
||||
* // 检查兼容性
|
||||
* console.log(BigIntFactory.isNativeSupported()); // true/false
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* BigInt兼容接口
|
||||
*
|
||||
* 定义了BigInt的基本操作接口,支持原生BigInt和兼容实现的统一调用。
|
||||
*/
|
||||
export interface IBigIntLike {
|
||||
/**
|
||||
* 获取数值表示
|
||||
* @returns 数值
|
||||
*/
|
||||
valueOf(): number;
|
||||
|
||||
/**
|
||||
* 转换为字符串
|
||||
* @param radix 进制,支持2、10、16
|
||||
* @returns 字符串表示
|
||||
*/
|
||||
toString(radix?: number): string;
|
||||
|
||||
/**
|
||||
* 位运算:与
|
||||
* @param other 另一个BigInt值
|
||||
* @returns 运算结果
|
||||
*/
|
||||
and(other: IBigIntLike): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 位运算:或
|
||||
* @param other 另一个BigInt值
|
||||
* @returns 运算结果
|
||||
*/
|
||||
or(other: IBigIntLike): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 位运算:异或
|
||||
* @param other 另一个BigInt值
|
||||
* @returns 运算结果
|
||||
*/
|
||||
xor(other: IBigIntLike): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 位运算:非
|
||||
* @param maxBits 最大位数限制
|
||||
* @returns 运算结果
|
||||
*/
|
||||
not(maxBits?: number): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 左移位运算
|
||||
* @param bits 移位数
|
||||
* @returns 运算结果
|
||||
*/
|
||||
shiftLeft(bits: number): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 右移位运算
|
||||
* @param bits 移位数
|
||||
* @returns 运算结果
|
||||
*/
|
||||
shiftRight(bits: number): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 相等比较
|
||||
* @param other 另一个BigInt值
|
||||
* @returns 是否相等
|
||||
*/
|
||||
equals(other: IBigIntLike): boolean;
|
||||
|
||||
/**
|
||||
* 检查是否为零
|
||||
* @returns 是否为零
|
||||
*/
|
||||
isZero(): boolean;
|
||||
|
||||
/**
|
||||
* 创建副本
|
||||
* @returns 新的实例
|
||||
*/
|
||||
clone(): IBigIntLike;
|
||||
}
|
||||
|
||||
/**
|
||||
* 原生BigInt包装器
|
||||
*
|
||||
* 为支持BigInt的环境提供统一接口包装。
|
||||
*/
|
||||
class NativeBigInt implements IBigIntLike {
|
||||
constructor(private value: bigint) {}
|
||||
|
||||
valueOf(): number {
|
||||
return Number(this.value);
|
||||
}
|
||||
|
||||
toString(radix?: number): string {
|
||||
if (radix !== undefined && radix !== 10 && radix !== 16 && radix !== 2) {
|
||||
throw new Error('Only radix 2, 10, and 16 are supported');
|
||||
}
|
||||
const result = this.value.toString(radix);
|
||||
if (radix === 16) {
|
||||
return result.toUpperCase();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
and(other: IBigIntLike): IBigIntLike {
|
||||
const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf());
|
||||
return new NativeBigInt(this.value & otherBigInt);
|
||||
}
|
||||
|
||||
or(other: IBigIntLike): IBigIntLike {
|
||||
const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf());
|
||||
return new NativeBigInt(this.value | otherBigInt);
|
||||
}
|
||||
|
||||
xor(other: IBigIntLike): IBigIntLike {
|
||||
const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf());
|
||||
return new NativeBigInt(this.value ^ otherBigInt);
|
||||
}
|
||||
|
||||
not(maxBits: number = 64): IBigIntLike {
|
||||
const mask = (BigInt(1) << BigInt(maxBits)) - BigInt(1);
|
||||
return new NativeBigInt((~this.value) & mask);
|
||||
}
|
||||
|
||||
shiftLeft(bits: number): IBigIntLike {
|
||||
return new NativeBigInt(this.value << BigInt(bits));
|
||||
}
|
||||
|
||||
shiftRight(bits: number): IBigIntLike {
|
||||
return new NativeBigInt(this.value >> BigInt(bits));
|
||||
}
|
||||
|
||||
equals(other: IBigIntLike): boolean {
|
||||
const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf());
|
||||
return this.value === otherBigInt;
|
||||
}
|
||||
|
||||
isZero(): boolean {
|
||||
return this.value === BigInt(0);
|
||||
}
|
||||
|
||||
clone(): IBigIntLike {
|
||||
return new NativeBigInt(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组模拟BigInt实现
|
||||
*
|
||||
* 为不支持BigInt的环境提供兼容实现,使用32位数组模拟大整数运算。
|
||||
* 性能略低于原生BigInt,但保证功能一致性。
|
||||
*/
|
||||
class ArrayBigInt implements IBigIntLike {
|
||||
private chunks: number[] = []; // 32位块数组
|
||||
private static readonly CHUNK_SIZE = 32;
|
||||
private static readonly CHUNK_MASK = 0xFFFFFFFF;
|
||||
private static readonly CHUNK_MAX = 0x100000000; // 2^32
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param value 初始值,可以是数值、字符串或数组
|
||||
*/
|
||||
constructor(value: number | string | number[] = 0) {
|
||||
if (typeof value === 'number') {
|
||||
this.fromNumber(value);
|
||||
} else if (typeof value === 'string') {
|
||||
this.fromString(value);
|
||||
} else {
|
||||
this.chunks = value.slice();
|
||||
}
|
||||
this.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数值初始化
|
||||
* @param value 数值
|
||||
*/
|
||||
private fromNumber(value: number): void {
|
||||
this.chunks = [];
|
||||
|
||||
// 处理负数(在位运算中通常不会遇到)
|
||||
if (value < 0) {
|
||||
value = Math.abs(value);
|
||||
}
|
||||
|
||||
if (value === 0) {
|
||||
this.chunks = [0];
|
||||
return;
|
||||
}
|
||||
|
||||
while (value > 0) {
|
||||
this.chunks.push(value & ArrayBigInt.CHUNK_MASK);
|
||||
value = Math.floor(value / ArrayBigInt.CHUNK_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串初始化
|
||||
* @param value 字符串(支持十进制、十六进制、二进制)
|
||||
*/
|
||||
private fromString(value: string): void {
|
||||
value = value.trim();
|
||||
|
||||
if (value.startsWith('0x') || value.startsWith('0X')) {
|
||||
// 十六进制
|
||||
this.fromHexString(value.substring(2));
|
||||
} else if (value.startsWith('0b') || value.startsWith('0B')) {
|
||||
// 二进制
|
||||
this.fromBinaryString(value.substring(2));
|
||||
} else {
|
||||
// 十进制
|
||||
this.fromDecimalString(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从十六进制字符串初始化
|
||||
* @param hex 十六进制字符串
|
||||
*/
|
||||
private fromHexString(hex: string): void {
|
||||
this.chunks = [0];
|
||||
|
||||
for (let i = hex.length - 1; i >= 0; i -= 8) {
|
||||
const start = Math.max(0, i - 7);
|
||||
const chunk = parseInt(hex.substring(start, i + 1), 16);
|
||||
this.chunks.push(chunk);
|
||||
}
|
||||
|
||||
this.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从二进制字符串初始化
|
||||
* @param binary 二进制字符串
|
||||
*/
|
||||
private fromBinaryString(binary: string): void {
|
||||
this.chunks = [0];
|
||||
|
||||
for (let i = binary.length - 1; i >= 0; i -= 32) {
|
||||
const start = Math.max(0, i - 31);
|
||||
const chunk = parseInt(binary.substring(start, i + 1), 2);
|
||||
this.chunks.push(chunk);
|
||||
}
|
||||
|
||||
this.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从十进制字符串初始化
|
||||
* @param decimal 十进制字符串
|
||||
*/
|
||||
private fromDecimalString(decimal: string): void {
|
||||
// 简化实现,直接转换为数值(在ECS位运算场景中通常是小数值)
|
||||
const num = parseInt(decimal, 10);
|
||||
this.fromNumber(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化数组,移除前导零
|
||||
*/
|
||||
private normalize(): void {
|
||||
while (this.chunks.length > 1 && this.chunks[this.chunks.length - 1] === 0) {
|
||||
this.chunks.pop();
|
||||
}
|
||||
|
||||
if (this.chunks.length === 0) {
|
||||
this.chunks = [0];
|
||||
}
|
||||
}
|
||||
|
||||
valueOf(): number {
|
||||
let result = 0;
|
||||
let multiplier = 1;
|
||||
|
||||
for (const chunk of this.chunks) {
|
||||
result += chunk * multiplier;
|
||||
multiplier *= ArrayBigInt.CHUNK_MAX;
|
||||
|
||||
// 防止溢出
|
||||
if (multiplier > Number.MAX_SAFE_INTEGER) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
toString(radix: number = 10): string {
|
||||
if (radix !== 10 && radix !== 16 && radix !== 2) {
|
||||
throw new Error('Only radix 2, 10, and 16 are supported');
|
||||
}
|
||||
|
||||
if (this.isZero()) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if (radix === 10) {
|
||||
// 简化实现,转换为数值
|
||||
return this.valueOf().toString(10);
|
||||
} else if (radix === 16) {
|
||||
let result = '';
|
||||
for (let i = this.chunks.length - 1; i >= 0; i--) {
|
||||
const hex = this.chunks[i].toString(16);
|
||||
result += i === this.chunks.length - 1 ? hex : hex.padStart(8, '0');
|
||||
}
|
||||
return result.toUpperCase();
|
||||
} else if (radix === 2) {
|
||||
let result = '';
|
||||
for (let i = this.chunks.length - 1; i >= 0; i--) {
|
||||
const binary = this.chunks[i].toString(2);
|
||||
result += i === this.chunks.length - 1 ? binary : binary.padStart(32, '0');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return this.valueOf().toString(radix);
|
||||
}
|
||||
|
||||
and(other: IBigIntLike): IBigIntLike {
|
||||
const otherArray = other as ArrayBigInt;
|
||||
const maxLength = Math.max(this.chunks.length, otherArray.chunks.length);
|
||||
const result: number[] = [];
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const a = i < this.chunks.length ? this.chunks[i] : 0;
|
||||
const b = i < otherArray.chunks.length ? otherArray.chunks[i] : 0;
|
||||
result.push(a & b);
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
or(other: IBigIntLike): IBigIntLike {
|
||||
const otherArray = other as ArrayBigInt;
|
||||
const maxLength = Math.max(this.chunks.length, otherArray.chunks.length);
|
||||
const result: number[] = [];
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const a = i < this.chunks.length ? this.chunks[i] : 0;
|
||||
const b = i < otherArray.chunks.length ? otherArray.chunks[i] : 0;
|
||||
result.push(a | b);
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
xor(other: IBigIntLike): IBigIntLike {
|
||||
const otherArray = other as ArrayBigInt;
|
||||
const maxLength = Math.max(this.chunks.length, otherArray.chunks.length);
|
||||
const result: number[] = [];
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const a = i < this.chunks.length ? this.chunks[i] : 0;
|
||||
const b = i < otherArray.chunks.length ? otherArray.chunks[i] : 0;
|
||||
result.push(a ^ b);
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
not(maxBits: number = 64): IBigIntLike {
|
||||
const maxChunks = Math.ceil(maxBits / ArrayBigInt.CHUNK_SIZE);
|
||||
const result: number[] = [];
|
||||
|
||||
for (let i = 0; i < maxChunks; i++) {
|
||||
const chunk = i < this.chunks.length ? this.chunks[i] : 0;
|
||||
|
||||
if (i === maxChunks - 1) {
|
||||
// 最后一个块需要处理剩余位数
|
||||
const remainingBits = maxBits % ArrayBigInt.CHUNK_SIZE;
|
||||
if (remainingBits > 0) {
|
||||
const mask = (1 << remainingBits) - 1;
|
||||
result.push((~chunk) & mask);
|
||||
} else {
|
||||
result.push((~chunk) & ArrayBigInt.CHUNK_MASK);
|
||||
}
|
||||
} else {
|
||||
result.push((~chunk) & ArrayBigInt.CHUNK_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
shiftLeft(bits: number): IBigIntLike {
|
||||
if (bits === 0) {
|
||||
return this.clone();
|
||||
}
|
||||
|
||||
if (bits < 0) {
|
||||
return this.shiftRight(-bits);
|
||||
}
|
||||
|
||||
const chunkShift = Math.floor(bits / ArrayBigInt.CHUNK_SIZE);
|
||||
const bitShift = bits % ArrayBigInt.CHUNK_SIZE;
|
||||
|
||||
const result: number[] = new Array(chunkShift).fill(0);
|
||||
|
||||
if (bitShift === 0) {
|
||||
// 整块移位
|
||||
result.push(...this.chunks);
|
||||
} else {
|
||||
// 部分位移位
|
||||
let carry = 0;
|
||||
for (const chunk of this.chunks) {
|
||||
const shifted = (chunk << bitShift) | carry;
|
||||
result.push(shifted & ArrayBigInt.CHUNK_MASK);
|
||||
carry = chunk >>> (ArrayBigInt.CHUNK_SIZE - bitShift);
|
||||
}
|
||||
|
||||
if (carry > 0) {
|
||||
result.push(carry);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
shiftRight(bits: number): IBigIntLike {
|
||||
if (bits === 0) {
|
||||
return this.clone();
|
||||
}
|
||||
|
||||
if (bits < 0) {
|
||||
return this.shiftLeft(-bits);
|
||||
}
|
||||
|
||||
const chunkShift = Math.floor(bits / ArrayBigInt.CHUNK_SIZE);
|
||||
const bitShift = bits % ArrayBigInt.CHUNK_SIZE;
|
||||
|
||||
if (chunkShift >= this.chunks.length) {
|
||||
return new ArrayBigInt(0);
|
||||
}
|
||||
|
||||
const result: number[] = [];
|
||||
|
||||
if (bitShift === 0) {
|
||||
// 整块移位
|
||||
for (let i = chunkShift; i < this.chunks.length; i++) {
|
||||
result.push(this.chunks[i]);
|
||||
}
|
||||
} else {
|
||||
// 部分位移位
|
||||
let carry = 0;
|
||||
for (let i = this.chunks.length - 1; i >= chunkShift; i--) {
|
||||
const chunk = this.chunks[i];
|
||||
const shifted = (carry << (ArrayBigInt.CHUNK_SIZE - bitShift)) | (chunk >>> bitShift);
|
||||
result.unshift(shifted);
|
||||
carry = chunk & ((1 << bitShift) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result.length > 0 ? result : [0]);
|
||||
}
|
||||
|
||||
equals(other: IBigIntLike): boolean {
|
||||
if (!(other instanceof ArrayBigInt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.chunks.length !== other.chunks.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.chunks.length; i++) {
|
||||
if (this.chunks[i] !== other.chunks[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isZero(): boolean {
|
||||
return this.chunks.length === 1 && this.chunks[0] === 0;
|
||||
}
|
||||
|
||||
clone(): IBigIntLike {
|
||||
return new ArrayBigInt(this.chunks.slice());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BigInt工厂类
|
||||
*
|
||||
* 自动检测运行时环境的BigInt支持情况,并提供统一的创建接口。
|
||||
* 在支持BigInt的环境中使用原生实现,在不支持的环境中使用兼容实现。
|
||||
*/
|
||||
export class BigIntFactory {
|
||||
private static _supportsBigInt: boolean | null = null;
|
||||
private static _cachedZero: IBigIntLike | null = null;
|
||||
private static _cachedOne: IBigIntLike | null = null;
|
||||
// 缓存检测结果以避免重复检测
|
||||
|
||||
/**
|
||||
* 检查是否支持原生BigInt
|
||||
* @returns 是否支持原生BigInt
|
||||
*/
|
||||
public static isNativeSupported(): boolean {
|
||||
if (this._supportsBigInt === null) {
|
||||
this._supportsBigInt = this.detectBigIntSupport();
|
||||
}
|
||||
return this._supportsBigInt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测BigInt支持情况
|
||||
* @returns 是否支持BigInt
|
||||
*/
|
||||
private static detectBigIntSupport(): boolean {
|
||||
try {
|
||||
// 检查BigInt构造函数是否存在
|
||||
if (typeof BigInt === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查基本BigInt操作
|
||||
const test1 = BigInt(1);
|
||||
const test2 = BigInt(2);
|
||||
const result = test1 | test2;
|
||||
|
||||
// 检查字面量支持
|
||||
const literal = eval('1n'); // 使用eval避免语法错误
|
||||
|
||||
// 检查类型
|
||||
if (typeof result !== 'bigint' || typeof literal !== 'bigint') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查基本运算
|
||||
const shifted = test1 << BigInt(1);
|
||||
const compared = test1 === BigInt(1);
|
||||
|
||||
return typeof shifted === 'bigint' && compared === true;
|
||||
} catch (error) {
|
||||
// 任何异常都表示不支持
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建BigInt兼容值
|
||||
* @param value 初始值
|
||||
* @returns IBigIntLike实例
|
||||
*/
|
||||
public static create(value: number | string | bigint = 0): IBigIntLike {
|
||||
if (this.isNativeSupported()) {
|
||||
let bigintValue: bigint;
|
||||
|
||||
if (typeof value === 'bigint') {
|
||||
bigintValue = value;
|
||||
} else if (typeof value === 'string') {
|
||||
bigintValue = BigInt(value);
|
||||
} else {
|
||||
bigintValue = BigInt(value);
|
||||
}
|
||||
|
||||
return new NativeBigInt(bigintValue);
|
||||
} else {
|
||||
// 转换bigint类型到兼容类型
|
||||
let compatValue: number | string;
|
||||
|
||||
if (typeof value === 'bigint') {
|
||||
compatValue = value.toString();
|
||||
} else {
|
||||
compatValue = value;
|
||||
}
|
||||
|
||||
return new ArrayBigInt(compatValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建零值
|
||||
* @returns 零值的IBigIntLike实例
|
||||
*/
|
||||
public static zero(): IBigIntLike {
|
||||
if (!this._cachedZero) {
|
||||
this._cachedZero = this.create(0);
|
||||
}
|
||||
return this._cachedZero;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建1值
|
||||
* @returns 1值的IBigIntLike实例
|
||||
*/
|
||||
public static one(): IBigIntLike {
|
||||
if (!this._cachedOne) {
|
||||
this._cachedOne = this.create(1);
|
||||
}
|
||||
return this._cachedOne;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从二进制字符串创建
|
||||
* @param binary 二进制字符串
|
||||
* @returns IBigIntLike实例
|
||||
*/
|
||||
public static fromBinaryString(binary: string): IBigIntLike {
|
||||
if (this.isNativeSupported()) {
|
||||
const value = BigInt('0b' + binary);
|
||||
return new NativeBigInt(value);
|
||||
} else {
|
||||
return new ArrayBigInt('0b' + binary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从十六进制字符串创建
|
||||
* @param hex 十六进制字符串
|
||||
* @returns IBigIntLike实例
|
||||
*/
|
||||
public static fromHexString(hex: string): IBigIntLike {
|
||||
if (this.isNativeSupported()) {
|
||||
const cleanHex = hex.replace(/^0x/i, '');
|
||||
const value = BigInt('0x' + cleanHex);
|
||||
return new NativeBigInt(value);
|
||||
} else {
|
||||
return new ArrayBigInt(hex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取环境信息
|
||||
* @returns 环境信息对象
|
||||
*/
|
||||
public static getEnvironmentInfo(): EnvironmentInfo {
|
||||
return {
|
||||
supportsBigInt: this.isNativeSupported(),
|
||||
environment: this.detectEnvironment(),
|
||||
jsEngine: this.detectJSEngine()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测运行环境
|
||||
* @returns 环境类型
|
||||
*/
|
||||
private static detectEnvironment(): string {
|
||||
if (typeof window !== 'undefined') {
|
||||
// 浏览器环境
|
||||
if (typeof navigator !== 'undefined') {
|
||||
const userAgent = navigator.userAgent;
|
||||
|
||||
if (userAgent.includes('Chrome')) {
|
||||
const match = userAgent.match(/Chrome\/(\d+)/);
|
||||
const version = match ? parseInt(match[1]) : 0;
|
||||
return `Chrome ${version}`;
|
||||
}
|
||||
|
||||
if (userAgent.includes('Firefox')) {
|
||||
const match = userAgent.match(/Firefox\/(\d+)/);
|
||||
const version = match ? parseInt(match[1]) : 0;
|
||||
return `Firefox ${version}`;
|
||||
}
|
||||
|
||||
if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
|
||||
const match = userAgent.match(/Version\/(\d+)/);
|
||||
const version = match ? parseInt(match[1]) : 0;
|
||||
return `Safari ${version}`;
|
||||
}
|
||||
|
||||
return 'Browser (Unknown)';
|
||||
}
|
||||
|
||||
return 'Browser';
|
||||
} else if (typeof global !== 'undefined') {
|
||||
// Node.js环境
|
||||
if (typeof process !== 'undefined' && process.version) {
|
||||
return `Node.js ${process.version}`;
|
||||
}
|
||||
return 'Node.js';
|
||||
} else if (typeof (globalThis as any).wx !== 'undefined') {
|
||||
// 微信小程序
|
||||
return 'WeChat MiniProgram';
|
||||
} else if (typeof (globalThis as any).cc !== 'undefined') {
|
||||
// Cocos Creator
|
||||
return 'Cocos Creator';
|
||||
} else if (typeof (globalThis as any).Laya !== 'undefined') {
|
||||
// Laya引擎
|
||||
return 'Laya Engine';
|
||||
}
|
||||
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测JavaScript引擎
|
||||
* @returns JS引擎信息
|
||||
*/
|
||||
private static detectJSEngine(): string {
|
||||
try {
|
||||
// V8引擎特征检测
|
||||
if (typeof process !== 'undefined' && process.versions && process.versions.v8) {
|
||||
return `V8 ${process.versions.v8}`;
|
||||
}
|
||||
|
||||
// SpiderMonkey特征检测
|
||||
if (typeof (globalThis as any).Components !== 'undefined') {
|
||||
return 'SpiderMonkey';
|
||||
}
|
||||
|
||||
// JavaScriptCore特征检测
|
||||
if (typeof window !== 'undefined' && typeof (window as any).safari !== 'undefined') {
|
||||
return 'JavaScriptCore';
|
||||
}
|
||||
|
||||
return 'Unknown';
|
||||
} catch {
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 环境信息接口
|
||||
*/
|
||||
export interface EnvironmentInfo {
|
||||
/** 是否支持BigInt */
|
||||
supportsBigInt: boolean;
|
||||
/** 运行环境 */
|
||||
environment: string;
|
||||
/** JavaScript引擎 */
|
||||
jsEngine: string;
|
||||
}
|
||||
321
packages/core/src/ECS/Utils/Bits.ts
Normal file
321
packages/core/src/ECS/Utils/Bits.ts
Normal file
@@ -0,0 +1,321 @@
|
||||
import { IBigIntLike, BigIntFactory } from './BigIntCompatibility';
|
||||
|
||||
/**
|
||||
* 高性能位操作类
|
||||
*
|
||||
* 基于BigInt实现,支持任意数量的位操作。
|
||||
* 自动适配运行环境,在不支持BigInt的环境中使用兼容实现。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const bits = new Bits();
|
||||
* bits.set(0);
|
||||
* bits.set(5);
|
||||
* console.log(bits.get(0)); // true
|
||||
* console.log(bits.get(1)); // false
|
||||
* ```
|
||||
*/
|
||||
export class Bits {
|
||||
private _value: IBigIntLike;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param initialValue 初始值,可以是IBigIntLike或数值
|
||||
*/
|
||||
constructor(initialValue?: IBigIntLike | number | string) {
|
||||
if (initialValue && typeof initialValue === 'object') {
|
||||
this._value = initialValue;
|
||||
} else {
|
||||
this._value = BigIntFactory.create(initialValue || 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指定位置的位为1
|
||||
* @param index 位索引(从0开始)
|
||||
* @throws {Error} 当索引为负数时抛出错误
|
||||
*/
|
||||
public set(index: number): void {
|
||||
if (index < 0) {
|
||||
throw new Error('Bit index cannot be negative');
|
||||
}
|
||||
const mask = BigIntFactory.one().shiftLeft(index);
|
||||
this._value = this._value.or(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定位置的位(设为0)
|
||||
* @param index 位索引(从0开始)
|
||||
* @throws {Error} 当索引为负数时抛出错误
|
||||
*/
|
||||
public clear(index: number): void {
|
||||
if (index < 0) {
|
||||
throw new Error('Bit index cannot be negative');
|
||||
}
|
||||
const mask = BigIntFactory.one().shiftLeft(index).not();
|
||||
this._value = this._value.and(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定位置的位值
|
||||
* @param index 位索引(从0开始)
|
||||
* @returns 位值(true表示1,false表示0)
|
||||
*/
|
||||
public get(index: number): boolean {
|
||||
if (index < 0) {
|
||||
return false;
|
||||
}
|
||||
const mask = BigIntFactory.one().shiftLeft(index);
|
||||
return !this._value.and(mask).isZero();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否包含所有指定的位
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 是否包含所有指定的位
|
||||
*/
|
||||
public containsAll(other: Bits): boolean {
|
||||
const intersection = this._value.and(other._value);
|
||||
return intersection.equals(other._value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否包含任意一个指定的位
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 是否包含任意一个指定的位
|
||||
*/
|
||||
public intersects(other: Bits): boolean {
|
||||
return !this._value.and(other._value).isZero();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否不包含任何指定的位
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 是否不包含任何指定的位
|
||||
*/
|
||||
public excludes(other: Bits): boolean {
|
||||
return !this.intersects(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有位
|
||||
*/
|
||||
public clearAll(): void {
|
||||
this._value = BigIntFactory.zero();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为空(没有设置任何位)
|
||||
* @returns 是否为空
|
||||
*/
|
||||
public isEmpty(): boolean {
|
||||
return this._value.isZero();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设置的位数量
|
||||
* @returns 设置为1的位数量
|
||||
*/
|
||||
public cardinality(): number {
|
||||
let count = 0;
|
||||
let value = this._value.clone();
|
||||
|
||||
while (!value.isZero()) {
|
||||
const one = BigIntFactory.one();
|
||||
if (!value.and(one).isZero()) {
|
||||
count++;
|
||||
}
|
||||
value = value.shiftRight(1);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 位运算:与
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 新的Bits对象,包含与运算结果
|
||||
*/
|
||||
public and(other: Bits): Bits {
|
||||
return new Bits(this._value.and(other._value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 位运算:或
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 新的Bits对象,包含或运算结果
|
||||
*/
|
||||
public or(other: Bits): Bits {
|
||||
return new Bits(this._value.or(other._value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 位运算:异或
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 新的Bits对象,包含异或运算结果
|
||||
*/
|
||||
public xor(other: Bits): Bits {
|
||||
return new Bits(this._value.xor(other._value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 位运算:非
|
||||
* @param maxBits 最大位数限制,默认64位
|
||||
* @returns 新的Bits对象,包含非运算结果
|
||||
*/
|
||||
public not(maxBits: number = 64): Bits {
|
||||
return new Bits(this._value.not(maxBits));
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制另一个Bits对象
|
||||
* @param other 要复制的Bits对象
|
||||
*/
|
||||
public copyFrom(other: Bits): void {
|
||||
this._value = other._value.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建当前Bits的副本
|
||||
* @returns 新的Bits对象副本
|
||||
*/
|
||||
public clone(): Bits {
|
||||
return new Bits(this._value.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始值
|
||||
* @returns 原始的IBigIntLike值
|
||||
*/
|
||||
public getValue(): IBigIntLike {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置原始值
|
||||
* @param value 新的值,可以是IBigIntLike或数值
|
||||
*/
|
||||
public setValue(value: IBigIntLike | number | string): void {
|
||||
if (typeof value === 'object') {
|
||||
this._value = value;
|
||||
} else {
|
||||
this._value = BigIntFactory.create(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调试信息
|
||||
* @returns 返回显示设置位索引的字符串
|
||||
*/
|
||||
public toString(): string {
|
||||
const bits: string[] = [];
|
||||
let index = 0;
|
||||
let value = this._value.clone();
|
||||
|
||||
while (!value.isZero()) {
|
||||
const one = BigIntFactory.one();
|
||||
if (!value.and(one).isZero()) {
|
||||
bits.push(index.toString());
|
||||
}
|
||||
value = value.shiftRight(1);
|
||||
index++;
|
||||
}
|
||||
|
||||
return `Bits[${bits.join(', ')}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二进制表示
|
||||
* @param maxBits 最大位数,默认64位
|
||||
* @returns 二进制字符串表示
|
||||
*/
|
||||
public toBinaryString(maxBits: number = 64): string {
|
||||
let result = '';
|
||||
for (let i = maxBits - 1; i >= 0; i--) {
|
||||
result += this.get(i) ? '1' : '0';
|
||||
if (i % 8 === 0 && i > 0) {
|
||||
result += ' ';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取十六进制表示
|
||||
* @returns 十六进制字符串表示
|
||||
*/
|
||||
public toHexString(): string {
|
||||
return '0x' + this._value.toString(16).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从二进制字符串创建Bits
|
||||
* @param binaryString 二进制字符串
|
||||
* @returns 新的Bits对象
|
||||
*/
|
||||
public static fromBinaryString(binaryString: string): Bits {
|
||||
const cleanString = binaryString.replace(/\s/g, '');
|
||||
const value = BigIntFactory.fromBinaryString(cleanString);
|
||||
return new Bits(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从十六进制字符串创建Bits
|
||||
* @param hexString 十六进制字符串
|
||||
* @returns 新的Bits对象
|
||||
*/
|
||||
public static fromHexString(hexString: string): Bits {
|
||||
const value = BigIntFactory.fromHexString(hexString);
|
||||
return new Bits(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个Bits对象是否相等
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 是否相等
|
||||
*/
|
||||
public equals(other: Bits): boolean {
|
||||
return this._value.equals(other._value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最高位的索引
|
||||
* @returns 最高位的索引,如果为空则返回-1
|
||||
*/
|
||||
public getHighestBitIndex(): number {
|
||||
if (this._value.isZero()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
let value = this._value.clone();
|
||||
|
||||
while (!value.shiftRight(1).isZero()) {
|
||||
value = value.shiftRight(1);
|
||||
index++;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最低位的索引
|
||||
* @returns 最低位的索引,如果为空则返回-1
|
||||
*/
|
||||
public getLowestBitIndex(): number {
|
||||
if (this._value.isZero()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
let value = this._value.clone();
|
||||
const one = BigIntFactory.one();
|
||||
|
||||
while (value.and(one).isZero()) {
|
||||
value = value.shiftRight(1);
|
||||
index++;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
99
packages/core/src/ECS/Utils/ComponentTypeManager.ts
Normal file
99
packages/core/src/ECS/Utils/ComponentTypeManager.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { Component } from '../Component';
|
||||
import { Bits } from './Bits';
|
||||
|
||||
/**
|
||||
* 组件类型管理器
|
||||
* 负责管理组件类型的注册和ID分配
|
||||
*/
|
||||
export class ComponentTypeManager {
|
||||
private static _instance: ComponentTypeManager;
|
||||
private _componentTypes = new Map<Function, number>();
|
||||
private _typeNames = new Map<number, string>();
|
||||
private _nextTypeId = 0;
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
public static get instance(): ComponentTypeManager {
|
||||
if (!ComponentTypeManager._instance) {
|
||||
ComponentTypeManager._instance = new ComponentTypeManager();
|
||||
}
|
||||
return ComponentTypeManager._instance;
|
||||
}
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* 获取组件类型的ID
|
||||
* @param componentType 组件类型构造函数
|
||||
* @returns 组件类型ID
|
||||
*/
|
||||
public getTypeId<T extends Component>(componentType: new (...args: unknown[]) => T): number {
|
||||
let typeId = this._componentTypes.get(componentType);
|
||||
|
||||
if (typeId === undefined) {
|
||||
typeId = this._nextTypeId++;
|
||||
this._componentTypes.set(componentType, typeId);
|
||||
this._typeNames.set(typeId, componentType.name);
|
||||
}
|
||||
|
||||
return typeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型名称
|
||||
* @param typeId 组件类型ID
|
||||
* @returns 组件类型名称
|
||||
*/
|
||||
public getTypeName(typeId: number): string {
|
||||
return this._typeNames.get(typeId) || 'Unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建包含指定组件类型的Bits对象
|
||||
* @param componentTypes 组件类型构造函数数组
|
||||
* @returns Bits对象
|
||||
*/
|
||||
public createBits(...componentTypes: (new (...args: unknown[]) => Component)[]): Bits {
|
||||
const bits = new Bits();
|
||||
|
||||
for (const componentType of componentTypes) {
|
||||
const typeId = this.getTypeId(componentType);
|
||||
bits.set(typeId);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体的组件位掩码
|
||||
* @param components 组件数组
|
||||
* @returns Bits对象
|
||||
*/
|
||||
public getEntityBits(components: Component[]): Bits {
|
||||
const bits = new Bits();
|
||||
|
||||
for (const component of components) {
|
||||
const typeId = this.getTypeId(component.constructor as new (...args: unknown[]) => Component);
|
||||
bits.set(typeId);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置管理器(主要用于测试)
|
||||
*/
|
||||
public reset(): void {
|
||||
this._componentTypes.clear();
|
||||
this._typeNames.clear();
|
||||
this._nextTypeId = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已注册的组件类型数量
|
||||
*/
|
||||
public get registeredTypeCount(): number {
|
||||
return this._componentTypes.size;
|
||||
}
|
||||
}
|
||||
304
packages/core/src/ECS/Utils/EntityList.ts
Normal file
304
packages/core/src/ECS/Utils/EntityList.ts
Normal file
@@ -0,0 +1,304 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
|
||||
/**
|
||||
* 高性能实体列表管理器
|
||||
* 管理场景中的所有实体,支持快速查找和批量操作
|
||||
*/
|
||||
export class EntityList {
|
||||
public buffer: Entity[] = [];
|
||||
private _scene: any; // 临时使用any,避免循环依赖
|
||||
|
||||
// 索引映射,提升查找性能
|
||||
private _idToEntity = new Map<number, Entity>();
|
||||
private _nameToEntities = new Map<string, Entity[]>();
|
||||
|
||||
// 延迟操作队列
|
||||
private _entitiesToAdd: Entity[] = [];
|
||||
private _entitiesToRemove: Entity[] = [];
|
||||
private _isUpdating = false;
|
||||
|
||||
public get count(): number {
|
||||
return this.buffer.length;
|
||||
}
|
||||
|
||||
constructor(scene: any) {
|
||||
this._scene = scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加实体(立即添加或延迟添加)
|
||||
* @param entity 要添加的实体
|
||||
*/
|
||||
public add(entity: Entity): void {
|
||||
if (this._isUpdating) {
|
||||
// 如果正在更新中,延迟添加
|
||||
this._entitiesToAdd.push(entity);
|
||||
} else {
|
||||
this.addImmediate(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即添加实体
|
||||
* @param entity 要添加的实体
|
||||
*/
|
||||
private addImmediate(entity: Entity): void {
|
||||
// 检查是否已存在
|
||||
if (this._idToEntity.has(entity.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.buffer.push(entity);
|
||||
this._idToEntity.set(entity.id, entity);
|
||||
|
||||
// 更新名称索引
|
||||
this.updateNameIndex(entity, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除实体(立即移除或延迟移除)
|
||||
* @param entity 要移除的实体
|
||||
*/
|
||||
public remove(entity: Entity): void {
|
||||
if (this._isUpdating) {
|
||||
// 如果正在更新中,延迟移除
|
||||
this._entitiesToRemove.push(entity);
|
||||
} else {
|
||||
this.removeImmediate(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即移除实体
|
||||
* @param entity 要移除的实体
|
||||
*/
|
||||
private removeImmediate(entity: Entity): void {
|
||||
const index = this.buffer.indexOf(entity);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除所有实体
|
||||
*/
|
||||
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();
|
||||
this._entitiesToAdd.length = 0;
|
||||
this._entitiesToRemove.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新实体列表,处理延迟操作
|
||||
*/
|
||||
public updateLists(): void {
|
||||
// 处理延迟添加的实体
|
||||
if (this._entitiesToAdd.length > 0) {
|
||||
for (const entity of this._entitiesToAdd) {
|
||||
this.addImmediate(entity);
|
||||
}
|
||||
this._entitiesToAdd.length = 0;
|
||||
}
|
||||
|
||||
// 处理延迟移除的实体
|
||||
if (this._entitiesToRemove.length > 0) {
|
||||
for (const entity of this._entitiesToRemove) {
|
||||
this.removeImmediate(entity);
|
||||
}
|
||||
this._entitiesToRemove.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新所有实体
|
||||
*/
|
||||
public update(): void {
|
||||
this._isUpdating = true;
|
||||
|
||||
try {
|
||||
for (let i = 0; i < this.buffer.length; i++) {
|
||||
const entity = this.buffer[i];
|
||||
if (entity.enabled && !entity.isDestroyed) {
|
||||
entity.update();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this._isUpdating = false;
|
||||
}
|
||||
|
||||
// 处理延迟操作
|
||||
this.updateLists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查找实体(使用索引,O(1)复杂度)
|
||||
* @param name 实体名称
|
||||
* @returns 找到的第一个实体或null
|
||||
*/
|
||||
public findEntity(name: string): Entity | null {
|
||||
const entities = this._nameToEntities.get(name);
|
||||
return entities && entities.length > 0 ? entities[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查找所有实体
|
||||
* @param name 实体名称
|
||||
* @returns 找到的所有实体数组
|
||||
*/
|
||||
public findEntitiesByName(name: string): Entity[] {
|
||||
return this._nameToEntities.get(name) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查找实体(使用索引,O(1)复杂度)
|
||||
* @param id 实体ID
|
||||
* @returns 找到的实体或null
|
||||
*/
|
||||
public findEntityById(id: number): Entity | null {
|
||||
return this._idToEntity.get(id) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签查找实体
|
||||
* @param tag 标签
|
||||
* @returns 找到的所有实体数组
|
||||
*/
|
||||
public findEntitiesByTag(tag: number): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
|
||||
for (const entity of this.buffer) {
|
||||
if (entity.tag === tag) {
|
||||
result.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据组件类型查找实体
|
||||
* @param componentType 组件类型
|
||||
* @returns 找到的所有实体数组
|
||||
*/
|
||||
public findEntitiesWithComponent<T extends Component>(componentType: new (...args: unknown[]) => T): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
|
||||
for (const entity of this.buffer) {
|
||||
if (entity.hasComponent(componentType)) {
|
||||
result.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作:对所有实体执行指定操作
|
||||
* @param action 要执行的操作
|
||||
*/
|
||||
public forEach(action: (entity: Entity) => void): void {
|
||||
for (const entity of this.buffer) {
|
||||
action(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作:对符合条件的实体执行指定操作
|
||||
* @param predicate 筛选条件
|
||||
* @param action 要执行的操作
|
||||
*/
|
||||
public forEachWhere(predicate: (entity: Entity) => boolean, action: (entity: Entity) => void): void {
|
||||
for (const entity of this.buffer) {
|
||||
if (predicate(entity)) {
|
||||
action(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新名称索引
|
||||
* @param entity 实体
|
||||
* @param isAdd 是否为添加操作
|
||||
*/
|
||||
private updateNameIndex(entity: Entity, isAdd: boolean): void {
|
||||
if (!entity.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAdd) {
|
||||
let entities = this._nameToEntities.get(entity.name);
|
||||
if (!entities) {
|
||||
entities = [];
|
||||
this._nameToEntities.set(entity.name, entities);
|
||||
}
|
||||
entities.push(entity);
|
||||
} else {
|
||||
const entities = this._nameToEntities.get(entity.name);
|
||||
if (entities) {
|
||||
const index = entities.indexOf(entity);
|
||||
if (index !== -1) {
|
||||
entities.splice(index, 1);
|
||||
|
||||
// 如果数组为空,删除映射
|
||||
if (entities.length === 0) {
|
||||
this._nameToEntities.delete(entity.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体列表的统计信息
|
||||
* @returns 统计信息
|
||||
*/
|
||||
public getStats(): {
|
||||
totalEntities: number;
|
||||
activeEntities: number;
|
||||
pendingAdd: number;
|
||||
pendingRemove: number;
|
||||
nameIndexSize: number;
|
||||
} {
|
||||
let activeCount = 0;
|
||||
for (const entity of this.buffer) {
|
||||
if (entity.enabled && !entity.isDestroyed) {
|
||||
activeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalEntities: this.buffer.length,
|
||||
activeEntities: activeCount,
|
||||
pendingAdd: this._entitiesToAdd.length,
|
||||
pendingRemove: this._entitiesToRemove.length,
|
||||
nameIndexSize: this._nameToEntities.size
|
||||
};
|
||||
}
|
||||
}
|
||||
111
packages/core/src/ECS/Utils/EntityProcessorList.ts
Normal file
111
packages/core/src/ECS/Utils/EntityProcessorList.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { EntitySystem } from '../Systems/EntitySystem';
|
||||
|
||||
/**
|
||||
* 实体处理器列表管理器
|
||||
* 管理场景中的所有实体系统
|
||||
*/
|
||||
export class EntityProcessorList {
|
||||
private _processors: EntitySystem[] = [];
|
||||
private _isDirty = false;
|
||||
|
||||
/**
|
||||
* 设置为脏状态,需要重新排序
|
||||
*/
|
||||
public setDirty(): void {
|
||||
this._isDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加实体处理器
|
||||
* @param processor 要添加的处理器
|
||||
*/
|
||||
public add(processor: EntitySystem): void {
|
||||
this._processors.push(processor);
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除实体处理器
|
||||
* @param processor 要移除的处理器
|
||||
*/
|
||||
public remove(processor: EntitySystem): void {
|
||||
const index = this._processors.indexOf(processor);
|
||||
if (index !== -1) {
|
||||
this._processors.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的处理器
|
||||
* @param type 处理器类型
|
||||
*/
|
||||
public getProcessor<T extends EntitySystem>(type: new (...args: unknown[]) => T): T | null {
|
||||
for (const processor of this._processors) {
|
||||
if (processor instanceof type) {
|
||||
return processor as T;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始处理
|
||||
*
|
||||
* 对所有处理器进行排序以确保正确的执行顺序。
|
||||
*/
|
||||
public begin(): void {
|
||||
this.sortProcessors();
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束处理
|
||||
*/
|
||||
public end(): void {
|
||||
// 清理处理器
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新所有处理器
|
||||
*/
|
||||
public update(): void {
|
||||
this.sortProcessors();
|
||||
for (const processor of this._processors) {
|
||||
try {
|
||||
processor.update();
|
||||
} catch (error) {
|
||||
console.error(`Error in processor ${processor.constructor.name}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后期更新所有处理器
|
||||
*/
|
||||
public lateUpdate(): void {
|
||||
for (const processor of this._processors) {
|
||||
processor.lateUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序处理器
|
||||
*/
|
||||
private sortProcessors(): void {
|
||||
if (this._isDirty) {
|
||||
this._processors.sort((a, b) => a.updateOrder - b.updateOrder);
|
||||
this._isDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取处理器列表 */
|
||||
public get processors() {
|
||||
return this._processors;
|
||||
}
|
||||
|
||||
/** 获取处理器数量 */
|
||||
public get count() {
|
||||
return this._processors.length;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
405
packages/core/src/ECS/Utils/IdentifierPool.ts
Normal file
405
packages/core/src/ECS/Utils/IdentifierPool.ts
Normal file
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* 世代式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,因为版本已递增
|
||||
* ```
|
||||
*/
|
||||
export class IdentifierPool {
|
||||
/**
|
||||
* 下一个可用的索引
|
||||
*/
|
||||
private _nextAvailableIndex = 0;
|
||||
|
||||
/**
|
||||
* 空闲的索引列表
|
||||
*/
|
||||
private _freeIndices: number[] = [];
|
||||
|
||||
/**
|
||||
* 每个索引对应的世代版本
|
||||
* 动态扩展的Map,按需分配内存
|
||||
*/
|
||||
private _generations = new Map<number, number>();
|
||||
|
||||
/**
|
||||
* 延迟回收队列
|
||||
* 防止在同一帧内立即重用ID,避免时序问题
|
||||
*/
|
||||
private _pendingRecycle: Array<{
|
||||
index: number;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 统计信息
|
||||
*/
|
||||
private _stats = {
|
||||
totalAllocated: 0,
|
||||
totalRecycled: 0,
|
||||
currentActive: 0,
|
||||
memoryExpansions: 0
|
||||
};
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @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()!;
|
||||
} else {
|
||||
// 分配新索引
|
||||
if (this._nextAvailableIndex > IdentifierPool.MAX_INDEX) {
|
||||
throw new Error(
|
||||
`实体索引已达到框架设计限制 (${IdentifierPool.MAX_INDEX})。` +
|
||||
`这意味着您已经分配了超过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
|
||||
);
|
||||
|
||||
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是否有效
|
||||
*/
|
||||
public isValid(id: number): boolean {
|
||||
const index = this._unpackIndex(id);
|
||||
const generation = this._unpackGeneration(id);
|
||||
return this._isValidId(index, generation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
*
|
||||
* @returns 池的当前状态统计
|
||||
*/
|
||||
public getStats(): {
|
||||
/** 已分配的总索引数 */
|
||||
totalAllocated: number;
|
||||
/** 总计回收次数 */
|
||||
totalRecycled: number;
|
||||
/** 当前活跃实体数 */
|
||||
currentActive: number;
|
||||
/** 当前空闲的索引数 */
|
||||
currentlyFree: number;
|
||||
/** 等待回收的ID数 */
|
||||
pendingRecycle: number;
|
||||
/** 理论最大实体数(设计限制) */
|
||||
maxPossibleEntities: number;
|
||||
/** 当前使用的最大索引 */
|
||||
maxUsedIndex: number;
|
||||
/** 内存使用(字节) */
|
||||
memoryUsage: number;
|
||||
/** 内存扩展次数 */
|
||||
memoryExpansions: number;
|
||||
/** 平均世代版本 */
|
||||
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
|
||||
: 1;
|
||||
|
||||
return {
|
||||
totalAllocated: this._stats.totalAllocated,
|
||||
totalRecycled: this._stats.totalRecycled,
|
||||
currentActive: this._stats.currentActive,
|
||||
currentlyFree: this._freeIndices.length,
|
||||
pendingRecycle: this._pendingRecycle.length,
|
||||
maxPossibleEntities: IdentifierPool.MAX_INDEX + 1,
|
||||
maxUsedIndex: this._nextAvailableIndex - 1,
|
||||
memoryUsage: this._calculateMemoryUsage(),
|
||||
memoryExpansions: this._stats.memoryExpansions,
|
||||
averageGeneration: Math.round(averageGeneration * 100) / 100,
|
||||
generationStorageSize: this._generations.size
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制执行延迟回收处理
|
||||
*
|
||||
* 在某些情况下可能需要立即处理延迟回收队列,
|
||||
* 比如内存压力大或者需要精确的统计信息时。
|
||||
*/
|
||||
public forceProcessDelayedRecycle(): void {
|
||||
this._processDelayedRecycle(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期的延迟回收项
|
||||
*
|
||||
* 将超过延迟时间的回收项真正回收到空闲列表中。
|
||||
*
|
||||
* @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) {
|
||||
readyToRecycle.push(item);
|
||||
} else {
|
||||
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
|
||||
*/
|
||||
private _preAllocateGenerations(startIndex: number, count: number): void {
|
||||
for (let i = 0; i < count; i++) {
|
||||
const index = startIndex + i;
|
||||
if (index <= IdentifierPool.MAX_INDEX) {
|
||||
this._generations.set(index, 1);
|
||||
}
|
||||
}
|
||||
this._stats.memoryExpansions++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保指定索引的世代信息存在
|
||||
*
|
||||
* @param index 索引
|
||||
* @private
|
||||
*/
|
||||
private _ensureGenerationCapacity(index: number): void {
|
||||
if (!this._generations.has(index)) {
|
||||
// 计算需要扩展的起始位置
|
||||
const expansionStart = Math.floor(index / this._expansionBlockSize) * this._expansionBlockSize;
|
||||
|
||||
// 预分配一个块
|
||||
this._preAllocateGenerations(expansionStart, this._expansionBlockSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算内存使用量
|
||||
*
|
||||
* @returns 内存使用字节数
|
||||
* @private
|
||||
*/
|
||||
private _calculateMemoryUsage(): number {
|
||||
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
|
||||
* @private
|
||||
*/
|
||||
private _packId(index: number, generation: number): number {
|
||||
return (generation << 16) | index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ID中解包索引
|
||||
*
|
||||
* @param id 32位ID
|
||||
* @returns 索引部分(16位)
|
||||
* @private
|
||||
*/
|
||||
private _unpackIndex(id: number): number {
|
||||
return id & 0xFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ID中解包世代版本
|
||||
*
|
||||
* @param id 32位ID
|
||||
* @returns 世代版本部分(16位)
|
||||
* @private
|
||||
*/
|
||||
private _unpackGeneration(id: number): number {
|
||||
return (id >>> 16) & 0xFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部ID有效性检查
|
||||
*
|
||||
* @param index 索引
|
||||
* @param generation 世代版本
|
||||
* @returns 是否有效
|
||||
* @private
|
||||
*/
|
||||
private _isValidId(index: number, generation: number): boolean {
|
||||
if (index < 0 || index >= this._nextAvailableIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const currentGeneration = this._generations.get(index);
|
||||
return currentGeneration !== undefined && currentGeneration === generation;
|
||||
}
|
||||
}
|
||||
295
packages/core/src/ECS/Utils/Matcher.ts
Normal file
295
packages/core/src/ECS/Utils/Matcher.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
|
||||
/**
|
||||
* 查询条件类型
|
||||
*/
|
||||
interface QueryCondition {
|
||||
all: ComponentType[];
|
||||
any: ComponentType[];
|
||||
none: ComponentType[];
|
||||
tag?: number; // 按标签查询
|
||||
name?: string; // 按名称查询
|
||||
component?: ComponentType; // 单组件查询
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体匹配条件描述符
|
||||
*
|
||||
* 用于描述实体查询条件,不执行实际查询
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const matcher = Matcher.all(Position, Velocity)
|
||||
* .any(Health, Shield)
|
||||
* .none(Dead);
|
||||
*
|
||||
* // 获取查询条件
|
||||
* const condition = matcher.getCondition();
|
||||
* ```
|
||||
*/
|
||||
export class Matcher {
|
||||
private readonly condition: QueryCondition = {
|
||||
all: [],
|
||||
any: [],
|
||||
none: []
|
||||
};
|
||||
|
||||
private constructor() {
|
||||
// 私有构造函数,只能通过静态方法创建
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建匹配器,要求所有指定的组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public static all(...types: ComponentType[]): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.all(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建匹配器,要求至少一个指定的组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public static any(...types: ComponentType[]): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.any(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建匹配器,排除指定的组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public static none(...types: ComponentType[]): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.none(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建按标签查询的匙配器
|
||||
* @param tag 标签值
|
||||
*/
|
||||
public static byTag(tag: number): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.withTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建按名称查询的匙配器
|
||||
* @param name 实体名称
|
||||
*/
|
||||
public static byName(name: string): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.withName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单组件查询的匙配器
|
||||
* @param componentType 组件类型
|
||||
*/
|
||||
public static byComponent(componentType: ComponentType): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.withComponent(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建复杂查询构建器
|
||||
*/
|
||||
public static complex(): Matcher {
|
||||
return new Matcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空匙配器(向后兼容)
|
||||
*/
|
||||
public static empty(): Matcher {
|
||||
return new Matcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* 必须包含所有指定组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public all(...types: ComponentType[]): Matcher {
|
||||
this.condition.all.push(...types);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 必须包含至少一个指定组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public any(...types: ComponentType[]): Matcher {
|
||||
this.condition.any.push(...types);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 不能包含任何指定组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public none(...types: ComponentType[]): Matcher {
|
||||
this.condition.none.push(...types);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 排除指定组件(别名方法)
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public exclude(...types: ComponentType[]): Matcher {
|
||||
return this.none(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 至少包含其中之一(别名方法)
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public one(...types: ComponentType[]): Matcher {
|
||||
return this.any(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按标签查询
|
||||
* @param tag 标签值
|
||||
*/
|
||||
public withTag(tag: number): Matcher {
|
||||
this.condition.tag = tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按名称查询
|
||||
* @param name 实体名称
|
||||
*/
|
||||
public withName(name: string): Matcher {
|
||||
this.condition.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单组件查询
|
||||
* @param componentType 组件类型
|
||||
*/
|
||||
public withComponent(componentType: ComponentType): Matcher {
|
||||
this.condition.component = componentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除标签条件
|
||||
*/
|
||||
public withoutTag(): Matcher {
|
||||
delete this.condition.tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除名称条件
|
||||
*/
|
||||
public withoutName(): Matcher {
|
||||
delete this.condition.name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除单组件条件
|
||||
*/
|
||||
public withoutComponent(): Matcher {
|
||||
delete this.condition.component;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询条件(只读)
|
||||
*/
|
||||
public getCondition(): Readonly<QueryCondition> {
|
||||
return {
|
||||
all: [...this.condition.all],
|
||||
any: [...this.condition.any],
|
||||
none: [...this.condition.none],
|
||||
tag: this.condition.tag,
|
||||
name: this.condition.name,
|
||||
component: this.condition.component
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为空条件
|
||||
*/
|
||||
public isEmpty(): boolean {
|
||||
return this.condition.all.length === 0 &&
|
||||
this.condition.any.length === 0 &&
|
||||
this.condition.none.length === 0 &&
|
||||
this.condition.tag === undefined &&
|
||||
this.condition.name === undefined &&
|
||||
this.condition.component === undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置所有条件
|
||||
*/
|
||||
public reset(): Matcher {
|
||||
this.condition.all.length = 0;
|
||||
this.condition.any.length = 0;
|
||||
this.condition.none.length = 0;
|
||||
delete this.condition.tag;
|
||||
delete this.condition.name;
|
||||
delete this.condition.component;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆匹配器
|
||||
*/
|
||||
public clone(): Matcher {
|
||||
const cloned = new Matcher();
|
||||
cloned.condition.all.push(...this.condition.all);
|
||||
cloned.condition.any.push(...this.condition.any);
|
||||
cloned.condition.none.push(...this.condition.none);
|
||||
if (this.condition.tag !== undefined) {
|
||||
cloned.condition.tag = this.condition.tag;
|
||||
}
|
||||
if (this.condition.name !== undefined) {
|
||||
cloned.condition.name = this.condition.name;
|
||||
}
|
||||
if (this.condition.component !== undefined) {
|
||||
cloned.condition.component = this.condition.component;
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串表示
|
||||
*/
|
||||
public toString(): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
if (this.condition.all.length > 0) {
|
||||
parts.push(`all(${this.condition.all.map(t => t.name).join(', ')})`);
|
||||
}
|
||||
|
||||
if (this.condition.any.length > 0) {
|
||||
parts.push(`any(${this.condition.any.map(t => t.name).join(', ')})`);
|
||||
}
|
||||
|
||||
if (this.condition.none.length > 0) {
|
||||
parts.push(`none(${this.condition.none.map(t => t.name).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(${this.condition.component.name})`);
|
||||
}
|
||||
|
||||
return `Matcher[${parts.join(' & ')}]`;
|
||||
}
|
||||
|
||||
}
|
||||
8
packages/core/src/ECS/Utils/index.ts
Normal file
8
packages/core/src/ECS/Utils/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// ECS工具类导出
|
||||
export { EntityList } from './EntityList';
|
||||
export { EntityProcessorList } from './EntityProcessorList';
|
||||
export { IdentifierPool } from './IdentifierPool';
|
||||
export { Matcher } from './Matcher';
|
||||
export { Bits } from './Bits';
|
||||
export { ComponentTypeManager } from './ComponentTypeManager';
|
||||
export { BigIntFactory } from './BigIntCompatibility';
|
||||
Reference in New Issue
Block a user