12850 lines
364 KiB
JavaScript
12850 lines
364 KiB
JavaScript
(function polyfill() {
|
||
const relList = document.createElement("link").relList;
|
||
if (relList && relList.supports && relList.supports("modulepreload")) {
|
||
return;
|
||
}
|
||
for (const link of document.querySelectorAll('link[rel="modulepreload"]')) {
|
||
processPreload(link);
|
||
}
|
||
new MutationObserver((mutations) => {
|
||
for (const mutation of mutations) {
|
||
if (mutation.type !== "childList") {
|
||
continue;
|
||
}
|
||
for (const node of mutation.addedNodes) {
|
||
if (node.tagName === "LINK" && node.rel === "modulepreload")
|
||
processPreload(node);
|
||
}
|
||
}
|
||
}).observe(document, { childList: true, subtree: true });
|
||
function getFetchOpts(link) {
|
||
const fetchOpts = {};
|
||
if (link.integrity) fetchOpts.integrity = link.integrity;
|
||
if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy;
|
||
if (link.crossOrigin === "use-credentials")
|
||
fetchOpts.credentials = "include";
|
||
else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit";
|
||
else fetchOpts.credentials = "same-origin";
|
||
return fetchOpts;
|
||
}
|
||
function processPreload(link) {
|
||
if (link.ep)
|
||
return;
|
||
link.ep = true;
|
||
const fetchOpts = getFetchOpts(link);
|
||
fetch(link.href, fetchOpts);
|
||
}
|
||
})();
|
||
class GlobalManager {
|
||
constructor() {
|
||
this._enabled = false;
|
||
}
|
||
/**
|
||
* 获取或设置管理器是否启用
|
||
*/
|
||
get enabled() {
|
||
return this._enabled;
|
||
}
|
||
set enabled(value) {
|
||
this.setEnabled(value);
|
||
}
|
||
/**
|
||
* 设置管理器是否启用
|
||
* @param isEnabled 如果为true,则启用管理器;否则禁用管理器
|
||
*/
|
||
setEnabled(isEnabled) {
|
||
if (this._enabled != isEnabled) {
|
||
this._enabled = isEnabled;
|
||
if (this._enabled) {
|
||
this.onEnabled();
|
||
} else {
|
||
this.onDisabled();
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 在启用管理器时调用的回调方法
|
||
*/
|
||
onEnabled() {
|
||
}
|
||
/**
|
||
* 在禁用管理器时调用的回调方法
|
||
*/
|
||
onDisabled() {
|
||
}
|
||
/**
|
||
* 更新管理器状态的方法
|
||
*/
|
||
update() {
|
||
}
|
||
}
|
||
class Time {
|
||
/**
|
||
* 使用外部引擎提供的deltaTime更新时间信息
|
||
* @param deltaTime 外部引擎提供的帧时间间隔(秒)
|
||
*/
|
||
static update(deltaTime) {
|
||
this.unscaledDeltaTime = deltaTime;
|
||
this.deltaTime = deltaTime * this.timeScale;
|
||
this.unscaledTotalTime += this.unscaledDeltaTime;
|
||
this.totalTime += this.deltaTime;
|
||
this.frameCount++;
|
||
}
|
||
/**
|
||
* 场景改变时重置时间
|
||
*/
|
||
static sceneChanged() {
|
||
this.frameCount = 0;
|
||
this.totalTime = 0;
|
||
this.unscaledTotalTime = 0;
|
||
this.deltaTime = 0;
|
||
this.unscaledDeltaTime = 0;
|
||
}
|
||
/**
|
||
* 检查指定的时间间隔是否已经过去
|
||
* @param interval 时间间隔(秒)
|
||
* @param lastTime 上次检查的时间
|
||
* @returns 是否已经过去指定时间
|
||
*/
|
||
static checkEvery(interval, lastTime) {
|
||
return this.totalTime - lastTime >= interval;
|
||
}
|
||
}
|
||
Time.deltaTime = 0;
|
||
Time.unscaledDeltaTime = 0;
|
||
Time.totalTime = 0;
|
||
Time.unscaledTotalTime = 0;
|
||
Time.timeScale = 1;
|
||
Time.frameCount = 0;
|
||
class Timer {
|
||
constructor() {
|
||
this._timeInSeconds = 0;
|
||
this._repeats = false;
|
||
this._isDone = false;
|
||
this._elapsedTime = 0;
|
||
}
|
||
getContext() {
|
||
return this.context;
|
||
}
|
||
/**
|
||
* 定时器是否已完成
|
||
*/
|
||
get isDone() {
|
||
return this._isDone;
|
||
}
|
||
/**
|
||
* 定时器已运行的时间
|
||
*/
|
||
get elapsedTime() {
|
||
return this._elapsedTime;
|
||
}
|
||
reset() {
|
||
this._elapsedTime = 0;
|
||
}
|
||
stop() {
|
||
this._isDone = true;
|
||
}
|
||
tick() {
|
||
if (!this._isDone && this._elapsedTime > this._timeInSeconds) {
|
||
this._elapsedTime -= this._timeInSeconds;
|
||
this._onTime(this);
|
||
if (!this._isDone && !this._repeats)
|
||
this._isDone = true;
|
||
}
|
||
this._elapsedTime += Time.deltaTime;
|
||
return this._isDone;
|
||
}
|
||
initialize(timeInsSeconds, repeats, context, onTime) {
|
||
this._timeInSeconds = timeInsSeconds;
|
||
this._repeats = repeats;
|
||
this.context = context;
|
||
this._onTime = onTime.bind(context);
|
||
}
|
||
/**
|
||
* 空出对象引用,以便在js需要时GC可以清理它们的引用
|
||
*/
|
||
unload() {
|
||
this.context = null;
|
||
this._onTime = null;
|
||
}
|
||
}
|
||
class TimerManager extends GlobalManager {
|
||
constructor() {
|
||
super(...arguments);
|
||
this._timers = [];
|
||
}
|
||
update() {
|
||
for (let i = this._timers.length - 1; i >= 0; i--) {
|
||
if (this._timers[i].tick()) {
|
||
this._timers[i].unload();
|
||
this._timers.splice(i, 1);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 调度一个一次性或重复的计时器,该计时器将调用已传递的动作
|
||
* @param timeInSeconds
|
||
* @param repeats
|
||
* @param context
|
||
* @param onTime
|
||
*/
|
||
schedule(timeInSeconds, repeats, context, onTime) {
|
||
let timer = new Timer();
|
||
timer.initialize(timeInSeconds, repeats, context, onTime);
|
||
this._timers.push(timer);
|
||
return timer;
|
||
}
|
||
}
|
||
var PerformanceWarningType;
|
||
(function(PerformanceWarningType2) {
|
||
PerformanceWarningType2["HIGH_EXECUTION_TIME"] = "high_execution_time";
|
||
PerformanceWarningType2["HIGH_MEMORY_USAGE"] = "high_memory_usage";
|
||
PerformanceWarningType2["HIGH_CPU_USAGE"] = "high_cpu_usage";
|
||
PerformanceWarningType2["FREQUENT_GC"] = "frequent_gc";
|
||
PerformanceWarningType2["LOW_FPS"] = "low_fps";
|
||
PerformanceWarningType2["HIGH_ENTITY_COUNT"] = "high_entity_count";
|
||
})(PerformanceWarningType || (PerformanceWarningType = {}));
|
||
class PerformanceMonitor {
|
||
/**
|
||
* 获取单例实例
|
||
*/
|
||
static get instance() {
|
||
if (!PerformanceMonitor._instance) {
|
||
PerformanceMonitor._instance = new PerformanceMonitor();
|
||
}
|
||
return PerformanceMonitor._instance;
|
||
}
|
||
constructor() {
|
||
this._systemData = /* @__PURE__ */ new Map();
|
||
this._systemStats = /* @__PURE__ */ new Map();
|
||
this._warnings = [];
|
||
this._isEnabled = false;
|
||
this._maxRecentSamples = 60;
|
||
this._maxWarnings = 100;
|
||
this._thresholds = {
|
||
executionTime: { warning: 16.67, critical: 33.33 },
|
||
// 60fps和30fps对应的帧时间
|
||
memoryUsage: { warning: 100, critical: 200 },
|
||
// MB
|
||
cpuUsage: { warning: 70, critical: 90 },
|
||
// 百分比
|
||
fps: { warning: 45, critical: 30 },
|
||
entityCount: { warning: 1e3, critical: 5e3 }
|
||
};
|
||
this._fpsHistory = [];
|
||
this._lastFrameTime = 0;
|
||
this._frameCount = 0;
|
||
this._fpsUpdateInterval = 1e3;
|
||
this._lastFpsUpdate = 0;
|
||
this._currentFps = 60;
|
||
this._memoryCheckInterval = 5e3;
|
||
this._lastMemoryCheck = 0;
|
||
this._memoryHistory = [];
|
||
this._gcCount = 0;
|
||
this._lastGcCheck = 0;
|
||
this._gcCheckInterval = 1e3;
|
||
}
|
||
/**
|
||
* 启用性能监控
|
||
*/
|
||
enable() {
|
||
this._isEnabled = true;
|
||
}
|
||
/**
|
||
* 禁用性能监控
|
||
*/
|
||
disable() {
|
||
this._isEnabled = false;
|
||
}
|
||
/**
|
||
* 检查是否启用了性能监控
|
||
*/
|
||
get isEnabled() {
|
||
return this._isEnabled;
|
||
}
|
||
/**
|
||
* 开始监控系统性能
|
||
* @param systemName 系统名称
|
||
* @returns 开始时间戳
|
||
*/
|
||
startMonitoring(systemName) {
|
||
if (!this._isEnabled) {
|
||
return 0;
|
||
}
|
||
return performance.now();
|
||
}
|
||
/**
|
||
* 结束监控并记录性能数据
|
||
* @param systemName 系统名称
|
||
* @param startTime 开始时间戳
|
||
* @param entityCount 处理的实体数量
|
||
*/
|
||
endMonitoring(systemName, startTime, entityCount = 0) {
|
||
if (!this._isEnabled || startTime === 0) {
|
||
return;
|
||
}
|
||
const endTime = performance.now();
|
||
const executionTime = endTime - startTime;
|
||
const averageTimePerEntity = entityCount > 0 ? executionTime / entityCount : 0;
|
||
const data = {
|
||
name: systemName,
|
||
executionTime,
|
||
entityCount,
|
||
averageTimePerEntity,
|
||
lastUpdateTime: endTime
|
||
};
|
||
this._systemData.set(systemName, data);
|
||
this.updateStats(systemName, executionTime);
|
||
}
|
||
/**
|
||
* 更新系统统计信息
|
||
* @param systemName 系统名称
|
||
* @param executionTime 执行时间
|
||
*/
|
||
updateStats(systemName, executionTime) {
|
||
let stats = this._systemStats.get(systemName);
|
||
if (!stats) {
|
||
stats = {
|
||
totalTime: 0,
|
||
averageTime: 0,
|
||
minTime: Number.MAX_VALUE,
|
||
maxTime: 0,
|
||
executionCount: 0,
|
||
recentTimes: [],
|
||
standardDeviation: 0,
|
||
percentile95: 0,
|
||
percentile99: 0
|
||
};
|
||
this._systemStats.set(systemName, stats);
|
||
}
|
||
stats.totalTime += executionTime;
|
||
stats.executionCount++;
|
||
stats.averageTime = stats.totalTime / stats.executionCount;
|
||
stats.minTime = Math.min(stats.minTime, executionTime);
|
||
stats.maxTime = Math.max(stats.maxTime, executionTime);
|
||
stats.recentTimes.push(executionTime);
|
||
if (stats.recentTimes.length > this._maxRecentSamples) {
|
||
stats.recentTimes.shift();
|
||
}
|
||
this.calculateAdvancedStats(stats);
|
||
}
|
||
/**
|
||
* 计算高级统计信息
|
||
* @param stats 统计信息对象
|
||
*/
|
||
calculateAdvancedStats(stats) {
|
||
if (stats.recentTimes.length === 0)
|
||
return;
|
||
const mean = stats.recentTimes.reduce((a, b) => a + b, 0) / stats.recentTimes.length;
|
||
const variance = stats.recentTimes.reduce((acc, time) => acc + Math.pow(time - mean, 2), 0) / stats.recentTimes.length;
|
||
stats.standardDeviation = Math.sqrt(variance);
|
||
const sortedTimes = [...stats.recentTimes].sort((a, b) => a - b);
|
||
const len = sortedTimes.length;
|
||
stats.percentile95 = sortedTimes[Math.floor(len * 0.95)] || 0;
|
||
stats.percentile99 = sortedTimes[Math.floor(len * 0.99)] || 0;
|
||
}
|
||
/**
|
||
* 获取系统的当前性能数据
|
||
* @param systemName 系统名称
|
||
* @returns 性能数据或undefined
|
||
*/
|
||
getSystemData(systemName) {
|
||
return this._systemData.get(systemName);
|
||
}
|
||
/**
|
||
* 获取系统的统计信息
|
||
* @param systemName 系统名称
|
||
* @returns 统计信息或undefined
|
||
*/
|
||
getSystemStats(systemName) {
|
||
return this._systemStats.get(systemName);
|
||
}
|
||
/**
|
||
* 获取所有系统的性能数据
|
||
* @returns 所有系统的性能数据
|
||
*/
|
||
getAllSystemData() {
|
||
return new Map(this._systemData);
|
||
}
|
||
/**
|
||
* 获取所有系统的统计信息
|
||
* @returns 所有系统的统计信息
|
||
*/
|
||
getAllSystemStats() {
|
||
return new Map(this._systemStats);
|
||
}
|
||
/**
|
||
* 获取性能报告
|
||
* @returns 格式化的性能报告字符串
|
||
*/
|
||
getPerformanceReport() {
|
||
if (!this._isEnabled) {
|
||
return "Performance monitoring is disabled.";
|
||
}
|
||
const lines = [];
|
||
lines.push("=== ECS Performance Report ===");
|
||
lines.push("");
|
||
const sortedSystems = Array.from(this._systemStats.entries()).sort((a, b) => b[1].averageTime - a[1].averageTime);
|
||
for (const [systemName, stats] of sortedSystems) {
|
||
const data = this._systemData.get(systemName);
|
||
lines.push(`System: ${systemName}`);
|
||
lines.push(` Current: ${data?.executionTime.toFixed(2)}ms (${data?.entityCount} entities)`);
|
||
lines.push(` Average: ${stats.averageTime.toFixed(2)}ms`);
|
||
lines.push(` Min/Max: ${stats.minTime.toFixed(2)}ms / ${stats.maxTime.toFixed(2)}ms`);
|
||
lines.push(` Total: ${stats.totalTime.toFixed(2)}ms (${stats.executionCount} calls)`);
|
||
if (data?.averageTimePerEntity && data.averageTimePerEntity > 0) {
|
||
lines.push(` Per Entity: ${data.averageTimePerEntity.toFixed(4)}ms`);
|
||
}
|
||
lines.push("");
|
||
}
|
||
const totalCurrentTime = Array.from(this._systemData.values()).reduce((sum, data) => sum + data.executionTime, 0);
|
||
lines.push(`Total Frame Time: ${totalCurrentTime.toFixed(2)}ms`);
|
||
lines.push(`Systems Count: ${this._systemData.size}`);
|
||
return lines.join("\n");
|
||
}
|
||
/**
|
||
* 重置所有性能数据
|
||
*/
|
||
reset() {
|
||
this._systemData.clear();
|
||
this._systemStats.clear();
|
||
}
|
||
/**
|
||
* 重置指定系统的性能数据
|
||
* @param systemName 系统名称
|
||
*/
|
||
resetSystem(systemName) {
|
||
this._systemData.delete(systemName);
|
||
this._systemStats.delete(systemName);
|
||
}
|
||
/**
|
||
* 获取性能警告
|
||
* @param thresholdMs 警告阈值(毫秒)
|
||
* @returns 超过阈值的系统列表
|
||
*/
|
||
getPerformanceWarnings(thresholdMs = 16.67) {
|
||
const warnings = [];
|
||
for (const [systemName, data] of this._systemData.entries()) {
|
||
if (data.executionTime > thresholdMs) {
|
||
warnings.push(`${systemName}: ${data.executionTime.toFixed(2)}ms (>${thresholdMs}ms)`);
|
||
}
|
||
}
|
||
return warnings;
|
||
}
|
||
/**
|
||
* 设置最大保留样本数
|
||
* @param maxSamples 最大样本数
|
||
*/
|
||
setMaxRecentSamples(maxSamples) {
|
||
this._maxRecentSamples = maxSamples;
|
||
for (const stats of this._systemStats.values()) {
|
||
while (stats.recentTimes.length > maxSamples) {
|
||
stats.recentTimes.shift();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
class Pool {
|
||
/**
|
||
* 构造函数
|
||
* @param createFn 创建对象的函数
|
||
* @param maxSize 池的最大大小,默认100
|
||
* @param estimatedObjectSize 估算的单个对象大小(字节),默认1024
|
||
*/
|
||
constructor(createFn, maxSize = 100, estimatedObjectSize = 1024) {
|
||
this._objects = [];
|
||
this._createFn = createFn;
|
||
this._maxSize = maxSize;
|
||
this._objectSize = estimatedObjectSize;
|
||
this._stats = {
|
||
size: 0,
|
||
maxSize,
|
||
totalCreated: 0,
|
||
totalObtained: 0,
|
||
totalReleased: 0,
|
||
hitRate: 0,
|
||
estimatedMemoryUsage: 0
|
||
};
|
||
}
|
||
/**
|
||
* 获取指定类型的对象池
|
||
* @param type 对象类型
|
||
* @param maxSize 池的最大大小
|
||
* @param estimatedObjectSize 估算的单个对象大小
|
||
* @returns 对象池实例
|
||
*/
|
||
static getPool(type, maxSize = 100, estimatedObjectSize = 1024) {
|
||
let pool = this._pools.get(type);
|
||
if (!pool) {
|
||
pool = new Pool(() => new type(), maxSize, estimatedObjectSize);
|
||
this._pools.set(type, pool);
|
||
}
|
||
return pool;
|
||
}
|
||
/**
|
||
* 从池中获取对象
|
||
* @returns 对象实例
|
||
*/
|
||
obtain() {
|
||
this._stats.totalObtained++;
|
||
if (this._objects.length > 0) {
|
||
const obj = this._objects.pop();
|
||
this._stats.size--;
|
||
this._updateHitRate();
|
||
this._updateMemoryUsage();
|
||
return obj;
|
||
}
|
||
this._stats.totalCreated++;
|
||
this._updateHitRate();
|
||
return this._createFn();
|
||
}
|
||
/**
|
||
* 释放对象回池中
|
||
* @param obj 要释放的对象
|
||
*/
|
||
release(obj) {
|
||
if (!obj)
|
||
return;
|
||
this._stats.totalReleased++;
|
||
if (this._stats.size < this._maxSize) {
|
||
obj.reset();
|
||
this._objects.push(obj);
|
||
this._stats.size++;
|
||
this._updateMemoryUsage();
|
||
}
|
||
}
|
||
/**
|
||
* 获取池统计信息
|
||
* @returns 统计信息对象
|
||
*/
|
||
getStats() {
|
||
return { ...this._stats };
|
||
}
|
||
/**
|
||
* 清空池
|
||
*/
|
||
clear() {
|
||
for (const obj of this._objects) {
|
||
obj.reset();
|
||
}
|
||
this._objects.length = 0;
|
||
this._stats.size = 0;
|
||
this._updateMemoryUsage();
|
||
}
|
||
/**
|
||
* 压缩池(移除多余的对象)
|
||
* @param targetSize 目标大小,默认为当前大小的一半
|
||
*/
|
||
compact(targetSize) {
|
||
const target = targetSize ?? Math.floor(this._objects.length / 2);
|
||
while (this._objects.length > target) {
|
||
const obj = this._objects.pop();
|
||
if (obj) {
|
||
obj.reset();
|
||
this._stats.size--;
|
||
}
|
||
}
|
||
this._updateMemoryUsage();
|
||
}
|
||
/**
|
||
* 预填充池
|
||
* @param count 预填充的对象数量
|
||
*/
|
||
prewarm(count) {
|
||
const actualCount = Math.min(count, this._maxSize - this._objects.length);
|
||
for (let i = 0; i < actualCount; i++) {
|
||
const obj = this._createFn();
|
||
obj.reset();
|
||
this._objects.push(obj);
|
||
this._stats.totalCreated++;
|
||
this._stats.size++;
|
||
}
|
||
this._updateMemoryUsage();
|
||
}
|
||
/**
|
||
* 设置最大池大小
|
||
* @param maxSize 新的最大大小
|
||
*/
|
||
setMaxSize(maxSize) {
|
||
this._maxSize = maxSize;
|
||
this._stats.maxSize = maxSize;
|
||
if (this._objects.length > maxSize) {
|
||
this.compact(maxSize);
|
||
}
|
||
}
|
||
/**
|
||
* 获取池中可用对象数量
|
||
* @returns 可用对象数量
|
||
*/
|
||
getAvailableCount() {
|
||
return this._objects.length;
|
||
}
|
||
/**
|
||
* 检查池是否为空
|
||
* @returns 如果池为空返回true
|
||
*/
|
||
isEmpty() {
|
||
return this._objects.length === 0;
|
||
}
|
||
/**
|
||
* 检查池是否已满
|
||
* @returns 如果池已满返回true
|
||
*/
|
||
isFull() {
|
||
return this._objects.length >= this._maxSize;
|
||
}
|
||
/**
|
||
* 获取所有已注册的池类型
|
||
* @returns 所有池类型的数组
|
||
*/
|
||
static getAllPoolTypes() {
|
||
return Array.from(this._pools.keys());
|
||
}
|
||
/**
|
||
* 获取所有池的统计信息
|
||
* @returns 包含所有池统计信息的对象
|
||
*/
|
||
static getAllPoolStats() {
|
||
const stats = {};
|
||
for (const [type, pool] of this._pools) {
|
||
const typeName = type.name || type.toString();
|
||
stats[typeName] = pool.getStats();
|
||
}
|
||
return stats;
|
||
}
|
||
/**
|
||
* 压缩所有池
|
||
*/
|
||
static compactAllPools() {
|
||
for (const pool of this._pools.values()) {
|
||
pool.compact();
|
||
}
|
||
}
|
||
/**
|
||
* 清空所有池
|
||
*/
|
||
static clearAllPools() {
|
||
for (const pool of this._pools.values()) {
|
||
pool.clear();
|
||
}
|
||
this._pools.clear();
|
||
}
|
||
/**
|
||
* 获取全局池统计信息的格式化字符串
|
||
* @returns 格式化的统计信息字符串
|
||
*/
|
||
static getGlobalStatsString() {
|
||
const stats = this.getAllPoolStats();
|
||
const lines = ["=== Object Pool Global Statistics ===", ""];
|
||
if (Object.keys(stats).length === 0) {
|
||
lines.push("No pools registered");
|
||
return lines.join("\n");
|
||
}
|
||
for (const [typeName, stat] of Object.entries(stats)) {
|
||
lines.push(`${typeName}:`);
|
||
lines.push(` Size: ${stat.size}/${stat.maxSize}`);
|
||
lines.push(` Hit Rate: ${(stat.hitRate * 100).toFixed(1)}%`);
|
||
lines.push(` Total Created: ${stat.totalCreated}`);
|
||
lines.push(` Total Obtained: ${stat.totalObtained}`);
|
||
lines.push(` Memory: ${(stat.estimatedMemoryUsage / 1024).toFixed(1)} KB`);
|
||
lines.push("");
|
||
}
|
||
return lines.join("\n");
|
||
}
|
||
/**
|
||
* 更新命中率
|
||
*/
|
||
_updateHitRate() {
|
||
if (this._stats.totalObtained === 0) {
|
||
this._stats.hitRate = 0;
|
||
} else {
|
||
const hits = this._stats.totalObtained - this._stats.totalCreated;
|
||
this._stats.hitRate = hits / this._stats.totalObtained;
|
||
}
|
||
}
|
||
/**
|
||
* 更新内存使用估算
|
||
*/
|
||
_updateMemoryUsage() {
|
||
this._stats.estimatedMemoryUsage = this._stats.size * this._objectSize;
|
||
}
|
||
}
|
||
Pool._pools = /* @__PURE__ */ new Map();
|
||
class PoolManager {
|
||
constructor() {
|
||
this.pools = /* @__PURE__ */ new Map();
|
||
this.autoCompactInterval = 6e4;
|
||
this.lastCompactTime = 0;
|
||
}
|
||
static getInstance() {
|
||
if (!PoolManager.instance) {
|
||
PoolManager.instance = new PoolManager();
|
||
}
|
||
return PoolManager.instance;
|
||
}
|
||
/**
|
||
* 注册池
|
||
* @param name 池名称
|
||
* @param pool 池实例
|
||
*/
|
||
registerPool(name, pool) {
|
||
this.pools.set(name, pool);
|
||
}
|
||
/**
|
||
* 获取池
|
||
* @param name 池名称
|
||
* @returns 池实例
|
||
*/
|
||
getPool(name) {
|
||
return this.pools.get(name) || null;
|
||
}
|
||
/**
|
||
* 更新池管理器(应在游戏循环中调用)
|
||
*/
|
||
update() {
|
||
const now = Date.now();
|
||
if (now - this.lastCompactTime > this.autoCompactInterval) {
|
||
this.compactAllPools();
|
||
this.lastCompactTime = now;
|
||
}
|
||
}
|
||
/**
|
||
* 创建或获取标准池
|
||
* @param name 池名称
|
||
* @param createFn 创建函数
|
||
* @param maxSize 最大大小
|
||
* @param estimatedObjectSize 估算对象大小
|
||
* @returns 池实例
|
||
*/
|
||
createPool(name, createFn, maxSize = 100, estimatedObjectSize = 1024) {
|
||
let pool = this.pools.get(name);
|
||
if (!pool) {
|
||
pool = new Pool(createFn, maxSize, estimatedObjectSize);
|
||
this.pools.set(name, pool);
|
||
}
|
||
return pool;
|
||
}
|
||
/**
|
||
* 移除池
|
||
* @param name 池名称
|
||
* @returns 是否成功移除
|
||
*/
|
||
removePool(name) {
|
||
const pool = this.pools.get(name);
|
||
if (pool) {
|
||
pool.clear();
|
||
this.pools.delete(name);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
/**
|
||
* 获取所有池名称
|
||
* @returns 池名称数组
|
||
*/
|
||
getPoolNames() {
|
||
return Array.from(this.pools.keys());
|
||
}
|
||
/**
|
||
* 获取池数量
|
||
* @returns 池数量
|
||
*/
|
||
getPoolCount() {
|
||
return this.pools.size;
|
||
}
|
||
/**
|
||
* 压缩所有池
|
||
*/
|
||
compactAllPools() {
|
||
for (const pool of this.pools.values()) {
|
||
pool.compact();
|
||
}
|
||
}
|
||
/**
|
||
* 清空所有池
|
||
*/
|
||
clearAllPools() {
|
||
for (const pool of this.pools.values()) {
|
||
pool.clear();
|
||
}
|
||
}
|
||
/**
|
||
* 获取所有池的统计信息
|
||
* @returns 统计信息映射
|
||
*/
|
||
getAllStats() {
|
||
const stats = /* @__PURE__ */ new Map();
|
||
for (const [name, pool] of this.pools) {
|
||
stats.set(name, pool.getStats());
|
||
}
|
||
return stats;
|
||
}
|
||
/**
|
||
* 获取总体统计信息
|
||
* @returns 总体统计信息
|
||
*/
|
||
getGlobalStats() {
|
||
let totalSize = 0;
|
||
let totalMaxSize = 0;
|
||
let totalCreated = 0;
|
||
let totalObtained = 0;
|
||
let totalReleased = 0;
|
||
let totalMemoryUsage = 0;
|
||
for (const pool of this.pools.values()) {
|
||
const stats = pool.getStats();
|
||
totalSize += stats.size;
|
||
totalMaxSize += stats.maxSize;
|
||
totalCreated += stats.totalCreated;
|
||
totalObtained += stats.totalObtained;
|
||
totalReleased += stats.totalReleased;
|
||
totalMemoryUsage += stats.estimatedMemoryUsage;
|
||
}
|
||
const hitRate = totalObtained === 0 ? 0 : (totalObtained - totalCreated) / totalObtained;
|
||
return {
|
||
size: totalSize,
|
||
maxSize: totalMaxSize,
|
||
totalCreated,
|
||
totalObtained,
|
||
totalReleased,
|
||
hitRate,
|
||
estimatedMemoryUsage: totalMemoryUsage
|
||
};
|
||
}
|
||
/**
|
||
* 获取格式化的统计信息字符串
|
||
* @returns 格式化字符串
|
||
*/
|
||
getStatsString() {
|
||
const lines = ["=== Pool Manager Statistics ===", ""];
|
||
if (this.pools.size === 0) {
|
||
lines.push("No pools registered");
|
||
return lines.join("\n");
|
||
}
|
||
const globalStats = this.getGlobalStats();
|
||
lines.push(`Total Pools: ${this.pools.size}`);
|
||
lines.push(`Global Hit Rate: ${(globalStats.hitRate * 100).toFixed(1)}%`);
|
||
lines.push(`Global Memory Usage: ${(globalStats.estimatedMemoryUsage / 1024).toFixed(1)} KB`);
|
||
lines.push("");
|
||
for (const [name, pool] of this.pools) {
|
||
const stats = pool.getStats();
|
||
lines.push(`${name}:`);
|
||
lines.push(` Size: ${stats.size}/${stats.maxSize}`);
|
||
lines.push(` Hit Rate: ${(stats.hitRate * 100).toFixed(1)}%`);
|
||
lines.push(` Memory: ${(stats.estimatedMemoryUsage / 1024).toFixed(1)} KB`);
|
||
lines.push("");
|
||
}
|
||
return lines.join("\n");
|
||
}
|
||
/**
|
||
* 设置自动压缩间隔
|
||
* @param intervalMs 间隔毫秒数
|
||
*/
|
||
setAutoCompactInterval(intervalMs) {
|
||
this.autoCompactInterval = intervalMs;
|
||
}
|
||
/**
|
||
* 预填充所有池
|
||
*/
|
||
prewarmAllPools() {
|
||
for (const pool of this.pools.values()) {
|
||
const stats = pool.getStats();
|
||
const prewarmCount = Math.floor(stats.maxSize * 0.2);
|
||
pool.prewarm(prewarmCount);
|
||
}
|
||
}
|
||
/**
|
||
* 重置池管理器
|
||
*/
|
||
reset() {
|
||
this.clearAllPools();
|
||
this.pools.clear();
|
||
this.lastCompactTime = 0;
|
||
}
|
||
}
|
||
class BitMask64Utils {
|
||
/**
|
||
* 根据位索引创建64位掩码
|
||
* @param bitIndex 位索引,范围 [0, 63]
|
||
* @returns 包含指定位设置为1的掩码
|
||
* @throws 当位索引超出范围时抛出错误
|
||
*/
|
||
static create(bitIndex) {
|
||
if (bitIndex < 0 || bitIndex >= 64) {
|
||
throw new Error(`Bit index ${bitIndex} out of range [0, 63]`);
|
||
}
|
||
if (bitIndex < 32) {
|
||
return { lo: 1 << bitIndex, hi: 0 };
|
||
} else {
|
||
return { lo: 0, hi: 1 << bitIndex - 32 };
|
||
}
|
||
}
|
||
/**
|
||
* 从32位数字创建64位掩码
|
||
* @param value 32位数字值
|
||
* @returns 低32位为输入值、高32位为0的掩码
|
||
*/
|
||
static fromNumber(value) {
|
||
return { lo: value >>> 0, hi: 0 };
|
||
}
|
||
/**
|
||
* 检查掩码是否包含任意指定的位
|
||
* @param mask 要检查的掩码
|
||
* @param bits 指定的位模式
|
||
* @returns 如果掩码包含bits中的任意位则返回true
|
||
*/
|
||
static hasAny(mask, bits) {
|
||
return (mask.lo & bits.lo) !== 0 || (mask.hi & bits.hi) !== 0;
|
||
}
|
||
/**
|
||
* 检查掩码是否包含所有指定的位
|
||
* @param mask 要检查的掩码
|
||
* @param bits 指定的位模式
|
||
* @returns 如果掩码包含bits中的所有位则返回true
|
||
*/
|
||
static hasAll(mask, bits) {
|
||
return (mask.lo & bits.lo) === bits.lo && (mask.hi & bits.hi) === bits.hi;
|
||
}
|
||
/**
|
||
* 检查掩码是否不包含任何指定的位
|
||
* @param mask 要检查的掩码
|
||
* @param bits 指定的位模式
|
||
* @returns 如果掩码不包含bits中的任何位则返回true
|
||
*/
|
||
static hasNone(mask, bits) {
|
||
return (mask.lo & bits.lo) === 0 && (mask.hi & bits.hi) === 0;
|
||
}
|
||
/**
|
||
* 检查掩码是否为零
|
||
* @param mask 要检查的掩码
|
||
* @returns 如果掩码所有位都为0则返回true
|
||
*/
|
||
static isZero(mask) {
|
||
return mask.lo === 0 && mask.hi === 0;
|
||
}
|
||
/**
|
||
* 检查两个掩码是否相等
|
||
* @param a 第一个掩码
|
||
* @param b 第二个掩码
|
||
* @returns 如果两个掩码完全相等则返回true
|
||
*/
|
||
static equals(a, b) {
|
||
return a.lo === b.lo && a.hi === b.hi;
|
||
}
|
||
/**
|
||
* 设置掩码中指定位为1
|
||
* @param mask 要修改的掩码(原地修改)
|
||
* @param bitIndex 位索引,范围 [0, 63]
|
||
* @throws 当位索引超出范围时抛出错误
|
||
*/
|
||
static setBit(mask, bitIndex) {
|
||
if (bitIndex < 0 || bitIndex >= 64) {
|
||
throw new Error(`Bit index ${bitIndex} out of range [0, 63]`);
|
||
}
|
||
if (bitIndex < 32) {
|
||
mask.lo |= 1 << bitIndex;
|
||
} else {
|
||
mask.hi |= 1 << bitIndex - 32;
|
||
}
|
||
}
|
||
/**
|
||
* 清除掩码中指定位为0
|
||
* @param mask 要修改的掩码(原地修改)
|
||
* @param bitIndex 位索引,范围 [0, 63]
|
||
* @throws 当位索引超出范围时抛出错误
|
||
*/
|
||
static clearBit(mask, bitIndex) {
|
||
if (bitIndex < 0 || bitIndex >= 64) {
|
||
throw new Error(`Bit index ${bitIndex} out of range [0, 63]`);
|
||
}
|
||
if (bitIndex < 32) {
|
||
mask.lo &= ~(1 << bitIndex);
|
||
} else {
|
||
mask.hi &= ~(1 << bitIndex - 32);
|
||
}
|
||
}
|
||
/**
|
||
* 对目标掩码执行按位或操作
|
||
* @param target 目标掩码(原地修改)
|
||
* @param other 用于按位或的掩码
|
||
*/
|
||
static orInPlace(target, other) {
|
||
target.lo |= other.lo;
|
||
target.hi |= other.hi;
|
||
}
|
||
/**
|
||
* 对目标掩码执行按位与操作
|
||
* @param target 目标掩码(原地修改)
|
||
* @param other 用于按位与的掩码
|
||
*/
|
||
static andInPlace(target, other) {
|
||
target.lo &= other.lo;
|
||
target.hi &= other.hi;
|
||
}
|
||
/**
|
||
* 对目标掩码执行按位异或操作
|
||
* @param target 目标掩码(原地修改)
|
||
* @param other 用于按位异或的掩码
|
||
*/
|
||
static xorInPlace(target, other) {
|
||
target.lo ^= other.lo;
|
||
target.hi ^= other.hi;
|
||
}
|
||
/**
|
||
* 清除掩码的所有位为0
|
||
* @param mask 要清除的掩码(原地修改)
|
||
*/
|
||
static clear(mask) {
|
||
mask.lo = 0;
|
||
mask.hi = 0;
|
||
}
|
||
/**
|
||
* 将源掩码的值复制到目标掩码
|
||
* @param source 源掩码
|
||
* @param target 目标掩码(原地修改)
|
||
*/
|
||
static copy(source, target) {
|
||
target.lo = source.lo;
|
||
target.hi = source.hi;
|
||
}
|
||
/**
|
||
* 创建掩码的深拷贝
|
||
* @param mask 要拷贝的掩码
|
||
* @returns 新的掩码对象,内容与源掩码相同
|
||
*/
|
||
static clone(mask) {
|
||
return { lo: mask.lo, hi: mask.hi };
|
||
}
|
||
/**
|
||
* 将掩码转换为字符串表示
|
||
* @param mask 要转换的掩码
|
||
* @param radix 进制,支持2(二进制)或16(十六进制),默认为2
|
||
* @returns 掩码的字符串表示,二进制不带前缀,十六进制带0x前缀
|
||
* @throws 当进制不支持时抛出错误
|
||
*/
|
||
static toString(mask, radix = 2) {
|
||
if (radix === 2) {
|
||
if (mask.hi === 0) {
|
||
return mask.lo.toString(2);
|
||
} else {
|
||
const hiBits = mask.hi.toString(2);
|
||
const loBits = mask.lo.toString(2).padStart(32, "0");
|
||
return hiBits + loBits;
|
||
}
|
||
} else if (radix === 16) {
|
||
if (mask.hi === 0) {
|
||
return "0x" + mask.lo.toString(16).toUpperCase();
|
||
} else {
|
||
const hiBits = mask.hi.toString(16).toUpperCase();
|
||
const loBits = mask.lo.toString(16).toUpperCase().padStart(8, "0");
|
||
return "0x" + hiBits + loBits;
|
||
}
|
||
} else {
|
||
throw new Error("Only radix 2 and 16 are supported");
|
||
}
|
||
}
|
||
/**
|
||
* 计算掩码中设置为1的位数
|
||
* @param mask 要计算的掩码
|
||
* @returns 掩码中1的位数
|
||
*/
|
||
static popCount(mask) {
|
||
let count = 0;
|
||
let lo = mask.lo;
|
||
let hi = mask.hi;
|
||
while (lo) {
|
||
lo &= lo - 1;
|
||
count++;
|
||
}
|
||
while (hi) {
|
||
hi &= hi - 1;
|
||
count++;
|
||
}
|
||
return count;
|
||
}
|
||
}
|
||
BitMask64Utils.ZERO = { lo: 0, hi: 0 };
|
||
var LogLevel;
|
||
(function(LogLevel2) {
|
||
LogLevel2[LogLevel2["Debug"] = 0] = "Debug";
|
||
LogLevel2[LogLevel2["Info"] = 1] = "Info";
|
||
LogLevel2[LogLevel2["Warn"] = 2] = "Warn";
|
||
LogLevel2[LogLevel2["Error"] = 3] = "Error";
|
||
LogLevel2[LogLevel2["Fatal"] = 4] = "Fatal";
|
||
LogLevel2[LogLevel2["None"] = 5] = "None";
|
||
})(LogLevel || (LogLevel = {}));
|
||
const Colors = {
|
||
RED: "\x1B[31m",
|
||
GREEN: "\x1B[32m",
|
||
YELLOW: "\x1B[33m",
|
||
// 亮色版本
|
||
BRIGHT_BLACK: "\x1B[90m",
|
||
BRIGHT_RED: "\x1B[91m",
|
||
// 特殊
|
||
RESET: "\x1B[0m"
|
||
};
|
||
class ConsoleLogger {
|
||
constructor(config = {}) {
|
||
this._config = {
|
||
level: LogLevel.Info,
|
||
enableTimestamp: true,
|
||
enableColors: typeof window === "undefined",
|
||
...config
|
||
};
|
||
}
|
||
/**
|
||
* 输出调试级别日志
|
||
* @param message 日志消息
|
||
* @param args 附加参数
|
||
*/
|
||
debug(message, ...args) {
|
||
this.log(LogLevel.Debug, message, ...args);
|
||
}
|
||
/**
|
||
* 输出信息级别日志
|
||
* @param message 日志消息
|
||
* @param args 附加参数
|
||
*/
|
||
info(message, ...args) {
|
||
this.log(LogLevel.Info, message, ...args);
|
||
}
|
||
/**
|
||
* 输出警告级别日志
|
||
* @param message 日志消息
|
||
* @param args 附加参数
|
||
*/
|
||
warn(message, ...args) {
|
||
this.log(LogLevel.Warn, message, ...args);
|
||
}
|
||
/**
|
||
* 输出错误级别日志
|
||
* @param message 日志消息
|
||
* @param args 附加参数
|
||
*/
|
||
error(message, ...args) {
|
||
this.log(LogLevel.Error, message, ...args);
|
||
}
|
||
/**
|
||
* 输出致命错误级别日志
|
||
* @param message 日志消息
|
||
* @param args 附加参数
|
||
*/
|
||
fatal(message, ...args) {
|
||
this.log(LogLevel.Fatal, message, ...args);
|
||
}
|
||
/**
|
||
* 设置日志级别
|
||
* @param level 日志级别
|
||
*/
|
||
setLevel(level) {
|
||
this._config.level = level;
|
||
}
|
||
/**
|
||
* 设置颜色配置
|
||
* @param colors 颜色配置
|
||
*/
|
||
setColors(colors) {
|
||
if (Object.keys(colors).length === 0) {
|
||
delete this._config.colors;
|
||
} else {
|
||
this._config.colors = {
|
||
...this._config.colors,
|
||
...colors
|
||
};
|
||
}
|
||
}
|
||
/**
|
||
* 设置日志前缀
|
||
* @param prefix 前缀字符串
|
||
*/
|
||
setPrefix(prefix) {
|
||
this._config.prefix = prefix;
|
||
}
|
||
/**
|
||
* 内部日志输出方法
|
||
* @param level 日志级别
|
||
* @param message 日志消息
|
||
* @param args 附加参数
|
||
*/
|
||
log(level, message, ...args) {
|
||
if (level < this._config.level) {
|
||
return;
|
||
}
|
||
let formattedMessage = message;
|
||
if (this._config.enableTimestamp) {
|
||
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
||
formattedMessage = `[${timestamp}] ${formattedMessage}`;
|
||
}
|
||
if (this._config.prefix) {
|
||
formattedMessage = `[${this._config.prefix}] ${formattedMessage}`;
|
||
}
|
||
const levelName = LogLevel[level].toUpperCase();
|
||
formattedMessage = `[${levelName}] ${formattedMessage}`;
|
||
if (this._config.output) {
|
||
this._config.output(level, formattedMessage);
|
||
} else {
|
||
this.outputToConsole(level, formattedMessage, ...args);
|
||
}
|
||
}
|
||
/**
|
||
* 输出到控制台
|
||
* @param level 日志级别
|
||
* @param message 格式化后的消息
|
||
* @param args 附加参数
|
||
*/
|
||
outputToConsole(level, message, ...args) {
|
||
const colors = this._config.enableColors ? this.getColors() : null;
|
||
switch (level) {
|
||
case LogLevel.Debug:
|
||
if (colors) {
|
||
console.debug(`${colors.debug}${message}${colors.reset}`, ...args);
|
||
} else {
|
||
console.debug(message, ...args);
|
||
}
|
||
break;
|
||
case LogLevel.Info:
|
||
if (colors) {
|
||
console.info(`${colors.info}${message}${colors.reset}`, ...args);
|
||
} else {
|
||
console.info(message, ...args);
|
||
}
|
||
break;
|
||
case LogLevel.Warn:
|
||
if (colors) {
|
||
console.warn(`${colors.warn}${message}${colors.reset}`, ...args);
|
||
} else {
|
||
console.warn(message, ...args);
|
||
}
|
||
break;
|
||
case LogLevel.Error:
|
||
if (colors) {
|
||
console.error(`${colors.error}${message}${colors.reset}`, ...args);
|
||
} else {
|
||
console.error(message, ...args);
|
||
}
|
||
break;
|
||
case LogLevel.Fatal:
|
||
if (colors) {
|
||
console.error(`${colors.fatal}${message}${colors.reset}`, ...args);
|
||
} else {
|
||
console.error(message, ...args);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
/**
|
||
* 获取控制台颜色配置
|
||
* @returns 颜色配置对象
|
||
*/
|
||
getColors() {
|
||
const defaultColors = {
|
||
debug: Colors.BRIGHT_BLACK,
|
||
// 灰色
|
||
info: Colors.GREEN,
|
||
// 绿色
|
||
warn: Colors.YELLOW,
|
||
// 黄色
|
||
error: Colors.RED,
|
||
// 红色
|
||
fatal: Colors.BRIGHT_RED,
|
||
// 亮红色
|
||
reset: Colors.RESET
|
||
// 重置
|
||
};
|
||
return {
|
||
...defaultColors,
|
||
...this._config.colors
|
||
};
|
||
}
|
||
}
|
||
class LoggerManager {
|
||
constructor() {
|
||
this._loggers = /* @__PURE__ */ new Map();
|
||
this._defaultLevel = LogLevel.Info;
|
||
this._defaultLogger = new ConsoleLogger({
|
||
level: this._defaultLevel
|
||
});
|
||
}
|
||
/**
|
||
* 获取日志管理器实例
|
||
* @returns 日志管理器实例
|
||
*/
|
||
static getInstance() {
|
||
if (!LoggerManager._instance) {
|
||
LoggerManager._instance = new LoggerManager();
|
||
}
|
||
return LoggerManager._instance;
|
||
}
|
||
/**
|
||
* 获取或创建日志器
|
||
* @param name 日志器名称
|
||
* @returns 日志器实例
|
||
*/
|
||
getLogger(name) {
|
||
if (!name) {
|
||
return this._defaultLogger;
|
||
}
|
||
if (!this._loggers.has(name)) {
|
||
const logger2 = new ConsoleLogger({
|
||
prefix: name,
|
||
level: this._defaultLevel
|
||
});
|
||
this._loggers.set(name, logger2);
|
||
}
|
||
return this._loggers.get(name);
|
||
}
|
||
/**
|
||
* 设置日志器
|
||
* @param name 日志器名称
|
||
* @param logger 日志器实例
|
||
*/
|
||
setLogger(name, logger2) {
|
||
this._loggers.set(name, logger2);
|
||
}
|
||
/**
|
||
* 设置全局日志级别
|
||
* @param level 日志级别
|
||
*/
|
||
setGlobalLevel(level) {
|
||
this._defaultLevel = level;
|
||
if (this._defaultLogger instanceof ConsoleLogger) {
|
||
this._defaultLogger.setLevel(level);
|
||
}
|
||
for (const logger2 of this._loggers.values()) {
|
||
if (logger2 instanceof ConsoleLogger) {
|
||
logger2.setLevel(level);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 创建子日志器
|
||
* @param parentName 父日志器名称
|
||
* @param childName 子日志器名称
|
||
* @returns 子日志器实例
|
||
*/
|
||
createChildLogger(parentName, childName) {
|
||
const fullName = `${parentName}.${childName}`;
|
||
return this.getLogger(fullName);
|
||
}
|
||
/**
|
||
* 设置全局颜色配置
|
||
* @param colors 颜色配置
|
||
*/
|
||
setGlobalColors(colors) {
|
||
if (this._defaultLogger instanceof ConsoleLogger) {
|
||
this._defaultLogger.setColors(colors);
|
||
}
|
||
for (const logger2 of this._loggers.values()) {
|
||
if (logger2 instanceof ConsoleLogger) {
|
||
logger2.setColors(colors);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 重置为默认颜色配置
|
||
*/
|
||
resetColors() {
|
||
if (this._defaultLogger instanceof ConsoleLogger) {
|
||
this._defaultLogger.setColors({});
|
||
}
|
||
for (const logger2 of this._loggers.values()) {
|
||
if (logger2 instanceof ConsoleLogger) {
|
||
logger2.setColors({});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
LoggerManager.getInstance().getLogger();
|
||
function createLogger(name) {
|
||
return LoggerManager.getInstance().getLogger(name);
|
||
}
|
||
class SoAStorage {
|
||
constructor(componentType) {
|
||
this.fields = /* @__PURE__ */ new Map();
|
||
this.stringFields = /* @__PURE__ */ new Map();
|
||
this.serializedFields = /* @__PURE__ */ new Map();
|
||
this.complexFields = /* @__PURE__ */ new Map();
|
||
this.entityToIndex = /* @__PURE__ */ new Map();
|
||
this.indexToEntity = [];
|
||
this.freeIndices = [];
|
||
this._size = 0;
|
||
this._capacity = 1e3;
|
||
this.type = componentType;
|
||
this.initializeFields(componentType);
|
||
}
|
||
initializeFields(componentType) {
|
||
const instance = new componentType();
|
||
const highPrecisionFields = componentType.__highPrecisionFields || /* @__PURE__ */ new Set();
|
||
const float64Fields = componentType.__float64Fields || /* @__PURE__ */ new Set();
|
||
const float32Fields = componentType.__float32Fields || /* @__PURE__ */ new Set();
|
||
const int32Fields = componentType.__int32Fields || /* @__PURE__ */ new Set();
|
||
const serializeMapFields = componentType.__serializeMapFields || /* @__PURE__ */ new Set();
|
||
const serializeSetFields = componentType.__serializeSetFields || /* @__PURE__ */ new Set();
|
||
const serializeArrayFields = componentType.__serializeArrayFields || /* @__PURE__ */ new Set();
|
||
for (const key in instance) {
|
||
if (instance.hasOwnProperty(key) && key !== "id") {
|
||
const value = instance[key];
|
||
const type = typeof value;
|
||
if (type === "number") {
|
||
if (highPrecisionFields.has(key)) ;
|
||
else if (float64Fields.has(key)) {
|
||
this.fields.set(key, new Float64Array(this._capacity));
|
||
} else if (int32Fields.has(key)) {
|
||
this.fields.set(key, new Int32Array(this._capacity));
|
||
} else if (float32Fields.has(key)) {
|
||
this.fields.set(key, new Float32Array(this._capacity));
|
||
} else {
|
||
this.fields.set(key, new Float32Array(this._capacity));
|
||
}
|
||
} else if (type === "boolean") {
|
||
this.fields.set(key, new Float32Array(this._capacity));
|
||
} else if (type === "string") {
|
||
this.stringFields.set(key, new Array(this._capacity));
|
||
} else if (type === "object" && value !== null) {
|
||
if (serializeMapFields.has(key) || serializeSetFields.has(key) || serializeArrayFields.has(key)) {
|
||
this.serializedFields.set(key, new Array(this._capacity));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
addComponent(entityId, component) {
|
||
if (this.entityToIndex.has(entityId)) {
|
||
const index2 = this.entityToIndex.get(entityId);
|
||
this.updateComponentAtIndex(index2, component);
|
||
return;
|
||
}
|
||
let index;
|
||
if (this.freeIndices.length > 0) {
|
||
index = this.freeIndices.pop();
|
||
} else {
|
||
index = this._size;
|
||
if (index >= this._capacity) {
|
||
this.resize(this._capacity * 2);
|
||
}
|
||
}
|
||
this.entityToIndex.set(entityId, index);
|
||
this.indexToEntity[index] = entityId;
|
||
this.updateComponentAtIndex(index, component);
|
||
this._size++;
|
||
}
|
||
updateComponentAtIndex(index, component) {
|
||
const entityId = this.indexToEntity[index];
|
||
const complexFieldMap = /* @__PURE__ */ new Map();
|
||
const highPrecisionFields = this.type.__highPrecisionFields || /* @__PURE__ */ new Set();
|
||
const serializeMapFields = this.type.__serializeMapFields || /* @__PURE__ */ new Set();
|
||
const serializeSetFields = this.type.__serializeSetFields || /* @__PURE__ */ new Set();
|
||
const serializeArrayFields = this.type.__serializeArrayFields || /* @__PURE__ */ new Set();
|
||
const deepCopyFields = this.type.__deepCopyFields || /* @__PURE__ */ new Set();
|
||
for (const key in component) {
|
||
if (component.hasOwnProperty(key) && key !== "id") {
|
||
const value = component[key];
|
||
const type = typeof value;
|
||
if (type === "number") {
|
||
if (highPrecisionFields.has(key) || !this.fields.has(key)) {
|
||
complexFieldMap.set(key, value);
|
||
} else {
|
||
const array = this.fields.get(key);
|
||
array[index] = value;
|
||
}
|
||
} else if (type === "boolean" && this.fields.has(key)) {
|
||
const array = this.fields.get(key);
|
||
array[index] = value ? 1 : 0;
|
||
} else if (this.stringFields.has(key)) {
|
||
const stringArray = this.stringFields.get(key);
|
||
stringArray[index] = String(value);
|
||
} else if (this.serializedFields.has(key)) {
|
||
const serializedArray = this.serializedFields.get(key);
|
||
serializedArray[index] = this.serializeValue(value, key, serializeMapFields, serializeSetFields, serializeArrayFields);
|
||
} else {
|
||
if (deepCopyFields.has(key)) {
|
||
complexFieldMap.set(key, this.deepClone(value));
|
||
} else {
|
||
complexFieldMap.set(key, value);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (complexFieldMap.size > 0) {
|
||
this.complexFields.set(entityId, complexFieldMap);
|
||
}
|
||
}
|
||
/**
|
||
* 序列化值为JSON字符串
|
||
*/
|
||
serializeValue(value, key, mapFields, setFields, arrayFields) {
|
||
try {
|
||
if (mapFields.has(key) && value instanceof Map) {
|
||
return JSON.stringify(Array.from(value.entries()));
|
||
} else if (setFields.has(key) && value instanceof Set) {
|
||
return JSON.stringify(Array.from(value));
|
||
} else if (arrayFields.has(key) && Array.isArray(value)) {
|
||
return JSON.stringify(value);
|
||
} else {
|
||
return JSON.stringify(value);
|
||
}
|
||
} catch (error) {
|
||
SoAStorage._logger.warn(`SoA序列化字段 ${key} 失败:`, error);
|
||
return "{}";
|
||
}
|
||
}
|
||
/**
|
||
* 反序列化JSON字符串为值
|
||
*/
|
||
deserializeValue(serialized, key, mapFields, setFields, arrayFields) {
|
||
try {
|
||
const parsed = JSON.parse(serialized);
|
||
if (mapFields.has(key)) {
|
||
return new Map(parsed);
|
||
} else if (setFields.has(key)) {
|
||
return new Set(parsed);
|
||
} else if (arrayFields.has(key)) {
|
||
return parsed;
|
||
} else {
|
||
return parsed;
|
||
}
|
||
} catch (error) {
|
||
SoAStorage._logger.warn(`SoA反序列化字段 ${key} 失败:`, error);
|
||
return null;
|
||
}
|
||
}
|
||
/**
|
||
* 深拷贝对象
|
||
*/
|
||
deepClone(obj) {
|
||
if (obj === null || typeof obj !== "object") {
|
||
return obj;
|
||
}
|
||
if (obj instanceof Date) {
|
||
return new Date(obj.getTime());
|
||
}
|
||
if (obj instanceof Array) {
|
||
return obj.map((item) => this.deepClone(item));
|
||
}
|
||
if (obj instanceof Map) {
|
||
const cloned2 = /* @__PURE__ */ new Map();
|
||
for (const [key, value] of obj.entries()) {
|
||
cloned2.set(key, this.deepClone(value));
|
||
}
|
||
return cloned2;
|
||
}
|
||
if (obj instanceof Set) {
|
||
const cloned2 = /* @__PURE__ */ new Set();
|
||
for (const value of obj.values()) {
|
||
cloned2.add(this.deepClone(value));
|
||
}
|
||
return cloned2;
|
||
}
|
||
const cloned = {};
|
||
for (const key in obj) {
|
||
if (obj.hasOwnProperty(key)) {
|
||
cloned[key] = this.deepClone(obj[key]);
|
||
}
|
||
}
|
||
return cloned;
|
||
}
|
||
getComponent(entityId) {
|
||
const index = this.entityToIndex.get(entityId);
|
||
if (index === void 0) {
|
||
return null;
|
||
}
|
||
const component = new this.type();
|
||
const serializeMapFields = this.type.__serializeMapFields || /* @__PURE__ */ new Set();
|
||
const serializeSetFields = this.type.__serializeSetFields || /* @__PURE__ */ new Set();
|
||
const serializeArrayFields = this.type.__serializeArrayFields || /* @__PURE__ */ new Set();
|
||
for (const [fieldName, array] of this.fields.entries()) {
|
||
const value = array[index];
|
||
const fieldType = this.getFieldType(fieldName);
|
||
if (fieldType === "boolean") {
|
||
component[fieldName] = value === 1;
|
||
} else {
|
||
component[fieldName] = value;
|
||
}
|
||
}
|
||
for (const [fieldName, stringArray] of this.stringFields.entries()) {
|
||
component[fieldName] = stringArray[index];
|
||
}
|
||
for (const [fieldName, serializedArray] of this.serializedFields.entries()) {
|
||
const serialized = serializedArray[index];
|
||
if (serialized) {
|
||
component[fieldName] = this.deserializeValue(serialized, fieldName, serializeMapFields, serializeSetFields, serializeArrayFields);
|
||
}
|
||
}
|
||
const complexFieldMap = this.complexFields.get(entityId);
|
||
if (complexFieldMap) {
|
||
for (const [fieldName, value] of complexFieldMap.entries()) {
|
||
component[fieldName] = value;
|
||
}
|
||
}
|
||
return component;
|
||
}
|
||
getFieldType(fieldName) {
|
||
const tempInstance = new this.type();
|
||
const value = tempInstance[fieldName];
|
||
return typeof value;
|
||
}
|
||
hasComponent(entityId) {
|
||
return this.entityToIndex.has(entityId);
|
||
}
|
||
removeComponent(entityId) {
|
||
const index = this.entityToIndex.get(entityId);
|
||
if (index === void 0) {
|
||
return null;
|
||
}
|
||
const component = this.getComponent(entityId);
|
||
this.complexFields.delete(entityId);
|
||
this.entityToIndex.delete(entityId);
|
||
this.freeIndices.push(index);
|
||
this._size--;
|
||
return component;
|
||
}
|
||
resize(newCapacity) {
|
||
for (const [fieldName, oldArray] of this.fields.entries()) {
|
||
let newArray;
|
||
if (oldArray instanceof Float32Array) {
|
||
newArray = new Float32Array(newCapacity);
|
||
} else if (oldArray instanceof Float64Array) {
|
||
newArray = new Float64Array(newCapacity);
|
||
} else {
|
||
newArray = new Int32Array(newCapacity);
|
||
}
|
||
newArray.set(oldArray);
|
||
this.fields.set(fieldName, newArray);
|
||
}
|
||
for (const [fieldName, oldArray] of this.stringFields.entries()) {
|
||
const newArray = new Array(newCapacity);
|
||
for (let i = 0; i < oldArray.length; i++) {
|
||
newArray[i] = oldArray[i];
|
||
}
|
||
this.stringFields.set(fieldName, newArray);
|
||
}
|
||
for (const [fieldName, oldArray] of this.serializedFields.entries()) {
|
||
const newArray = new Array(newCapacity);
|
||
for (let i = 0; i < oldArray.length; i++) {
|
||
newArray[i] = oldArray[i];
|
||
}
|
||
this.serializedFields.set(fieldName, newArray);
|
||
}
|
||
this._capacity = newCapacity;
|
||
}
|
||
getActiveIndices() {
|
||
return Array.from(this.entityToIndex.values());
|
||
}
|
||
getFieldArray(fieldName) {
|
||
return this.fields.get(fieldName) || null;
|
||
}
|
||
getTypedFieldArray(fieldName) {
|
||
return this.fields.get(String(fieldName)) || null;
|
||
}
|
||
getEntityIndex(entityId) {
|
||
return this.entityToIndex.get(entityId);
|
||
}
|
||
getEntityIdByIndex(index) {
|
||
return this.indexToEntity[index];
|
||
}
|
||
size() {
|
||
return this._size;
|
||
}
|
||
clear() {
|
||
this.entityToIndex.clear();
|
||
this.indexToEntity = [];
|
||
this.freeIndices = [];
|
||
this.complexFields.clear();
|
||
this._size = 0;
|
||
for (const array of this.fields.values()) {
|
||
array.fill(0);
|
||
}
|
||
for (const stringArray of this.stringFields.values()) {
|
||
for (let i = 0; i < stringArray.length; i++) {
|
||
stringArray[i] = void 0;
|
||
}
|
||
}
|
||
for (const serializedArray of this.serializedFields.values()) {
|
||
for (let i = 0; i < serializedArray.length; i++) {
|
||
serializedArray[i] = void 0;
|
||
}
|
||
}
|
||
}
|
||
compact() {
|
||
if (this.freeIndices.length === 0) {
|
||
return;
|
||
}
|
||
const activeEntries = Array.from(this.entityToIndex.entries()).sort((a, b) => a[1] - b[1]);
|
||
const newEntityToIndex = /* @__PURE__ */ new Map();
|
||
const newIndexToEntity = [];
|
||
for (let newIndex = 0; newIndex < activeEntries.length; newIndex++) {
|
||
const [entityId, oldIndex] = activeEntries[newIndex];
|
||
newEntityToIndex.set(entityId, newIndex);
|
||
newIndexToEntity[newIndex] = entityId;
|
||
if (newIndex !== oldIndex) {
|
||
for (const [, array] of this.fields.entries()) {
|
||
array[newIndex] = array[oldIndex];
|
||
}
|
||
for (const [, stringArray] of this.stringFields.entries()) {
|
||
stringArray[newIndex] = stringArray[oldIndex];
|
||
}
|
||
for (const [, serializedArray] of this.serializedFields.entries()) {
|
||
serializedArray[newIndex] = serializedArray[oldIndex];
|
||
}
|
||
}
|
||
}
|
||
this.entityToIndex = newEntityToIndex;
|
||
this.indexToEntity = newIndexToEntity;
|
||
this.freeIndices = [];
|
||
this._size = activeEntries.length;
|
||
}
|
||
getStats() {
|
||
let totalMemory = 0;
|
||
const fieldStats = /* @__PURE__ */ new Map();
|
||
for (const [fieldName, array] of this.fields.entries()) {
|
||
let bytesPerElement;
|
||
let typeName;
|
||
if (array instanceof Float32Array) {
|
||
bytesPerElement = 4;
|
||
typeName = "float32";
|
||
} else if (array instanceof Float64Array) {
|
||
bytesPerElement = 8;
|
||
typeName = "float64";
|
||
} else {
|
||
bytesPerElement = 4;
|
||
typeName = "int32";
|
||
}
|
||
const memory = array.length * bytesPerElement;
|
||
totalMemory += memory;
|
||
fieldStats.set(fieldName, {
|
||
size: this._size,
|
||
capacity: array.length,
|
||
type: typeName,
|
||
memory
|
||
});
|
||
}
|
||
return {
|
||
size: this._size,
|
||
capacity: this._capacity,
|
||
usedSlots: this._size,
|
||
// 兼容原测试
|
||
fragmentation: this.freeIndices.length / this._capacity,
|
||
memoryUsage: totalMemory,
|
||
fieldStats
|
||
};
|
||
}
|
||
/**
|
||
* 执行向量化批量操作
|
||
* @param operation 操作函数,接收字段数组和活跃索引
|
||
*/
|
||
performVectorizedOperation(operation) {
|
||
const activeIndices = this.getActiveIndices();
|
||
operation(this.fields, activeIndices);
|
||
}
|
||
}
|
||
SoAStorage._logger = createLogger("SoAStorage");
|
||
const COMPONENT_TYPE_NAME = Symbol("ComponentTypeName");
|
||
const SYSTEM_TYPE_NAME = Symbol("SystemTypeName");
|
||
function ECSComponent(typeName) {
|
||
return function(target) {
|
||
if (!typeName || typeof typeName !== "string") {
|
||
throw new Error("ECSComponent装饰器必须提供有效的类型名称");
|
||
}
|
||
target[COMPONENT_TYPE_NAME] = typeName;
|
||
return target;
|
||
};
|
||
}
|
||
function ECSSystem(typeName) {
|
||
return function(target) {
|
||
if (!typeName || typeof typeName !== "string") {
|
||
throw new Error("ECSSystem装饰器必须提供有效的类型名称");
|
||
}
|
||
target[SYSTEM_TYPE_NAME] = typeName;
|
||
return target;
|
||
};
|
||
}
|
||
function getComponentTypeName(componentType) {
|
||
const decoratorName = componentType[COMPONENT_TYPE_NAME];
|
||
if (decoratorName) {
|
||
return decoratorName;
|
||
}
|
||
return componentType.name || "UnknownComponent";
|
||
}
|
||
function getSystemTypeName(systemType) {
|
||
const decoratorName = systemType[SYSTEM_TYPE_NAME];
|
||
if (decoratorName) {
|
||
return decoratorName;
|
||
}
|
||
return systemType.name || "UnknownSystem";
|
||
}
|
||
function getComponentInstanceTypeName(component) {
|
||
return getComponentTypeName(component.constructor);
|
||
}
|
||
function getSystemInstanceTypeName(system) {
|
||
return getSystemTypeName(system.constructor);
|
||
}
|
||
class ComponentRegistry {
|
||
/**
|
||
* 注册组件类型并分配位掩码
|
||
* @param componentType 组件类型
|
||
* @returns 分配的位索引
|
||
*/
|
||
static register(componentType) {
|
||
const typeName = getComponentTypeName(componentType);
|
||
if (this.componentTypes.has(componentType)) {
|
||
const existingIndex = this.componentTypes.get(componentType);
|
||
return existingIndex;
|
||
}
|
||
if (this.nextBitIndex >= this.maxComponents) {
|
||
throw new Error(`Maximum number of component types (${this.maxComponents}) exceeded`);
|
||
}
|
||
const bitIndex = this.nextBitIndex++;
|
||
this.componentTypes.set(componentType, bitIndex);
|
||
this.componentNameToType.set(typeName, componentType);
|
||
this.componentNameToId.set(typeName, bitIndex);
|
||
return bitIndex;
|
||
}
|
||
/**
|
||
* 获取组件类型的位掩码
|
||
* @param componentType 组件类型
|
||
* @returns 位掩码
|
||
*/
|
||
static getBitMask(componentType) {
|
||
const bitIndex = this.componentTypes.get(componentType);
|
||
if (bitIndex === void 0) {
|
||
const typeName = getComponentTypeName(componentType);
|
||
throw new Error(`Component type ${typeName} is not registered`);
|
||
}
|
||
return BitMask64Utils.create(bitIndex);
|
||
}
|
||
/**
|
||
* 获取组件类型的位索引
|
||
* @param componentType 组件类型
|
||
* @returns 位索引
|
||
*/
|
||
static getBitIndex(componentType) {
|
||
const bitIndex = this.componentTypes.get(componentType);
|
||
if (bitIndex === void 0) {
|
||
const typeName = getComponentTypeName(componentType);
|
||
throw new Error(`Component type ${typeName} is not registered`);
|
||
}
|
||
return bitIndex;
|
||
}
|
||
/**
|
||
* 检查组件类型是否已注册
|
||
* @param componentType 组件类型
|
||
* @returns 是否已注册
|
||
*/
|
||
static isRegistered(componentType) {
|
||
return this.componentTypes.has(componentType);
|
||
}
|
||
/**
|
||
* 通过名称获取组件类型
|
||
* @param componentName 组件名称
|
||
* @returns 组件类型构造函数
|
||
*/
|
||
static getComponentType(componentName) {
|
||
return this.componentNameToType.get(componentName) || null;
|
||
}
|
||
/**
|
||
* 获取所有已注册的组件类型
|
||
* @returns 组件类型映射
|
||
*/
|
||
static getAllRegisteredTypes() {
|
||
return new Map(this.componentTypes);
|
||
}
|
||
/**
|
||
* 获取所有组件名称到类型的映射
|
||
* @returns 名称到类型的映射
|
||
*/
|
||
static getAllComponentNames() {
|
||
return new Map(this.componentNameToType);
|
||
}
|
||
/**
|
||
* 通过名称获取组件类型ID
|
||
* @param componentName 组件名称
|
||
* @returns 组件类型ID
|
||
*/
|
||
static getComponentId(componentName) {
|
||
return this.componentNameToId.get(componentName);
|
||
}
|
||
/**
|
||
* 注册组件类型(通过名称)
|
||
* @param componentName 组件名称
|
||
* @returns 分配的组件ID
|
||
*/
|
||
static registerComponentByName(componentName) {
|
||
if (this.componentNameToId.has(componentName)) {
|
||
return this.componentNameToId.get(componentName);
|
||
}
|
||
if (this.nextBitIndex >= this.maxComponents) {
|
||
throw new Error(`Maximum number of component types (${this.maxComponents}) exceeded`);
|
||
}
|
||
const bitIndex = this.nextBitIndex++;
|
||
this.componentNameToId.set(componentName, bitIndex);
|
||
return bitIndex;
|
||
}
|
||
/**
|
||
* 创建单个组件的掩码
|
||
* @param componentName 组件名称
|
||
* @returns 组件掩码
|
||
*/
|
||
static createSingleComponentMask(componentName) {
|
||
const cacheKey = `single:${componentName}`;
|
||
if (this.maskCache.has(cacheKey)) {
|
||
return this.maskCache.get(cacheKey);
|
||
}
|
||
const componentId = this.getComponentId(componentName);
|
||
if (componentId === void 0) {
|
||
throw new Error(`Component type ${componentName} is not registered`);
|
||
}
|
||
const mask = BitMask64Utils.create(componentId);
|
||
this.maskCache.set(cacheKey, mask);
|
||
return mask;
|
||
}
|
||
/**
|
||
* 创建多个组件的掩码
|
||
* @param componentNames 组件名称数组
|
||
* @returns 组合掩码
|
||
*/
|
||
static createComponentMask(componentNames) {
|
||
const sortedNames = [...componentNames].sort();
|
||
const cacheKey = `multi:${sortedNames.join(",")}`;
|
||
if (this.maskCache.has(cacheKey)) {
|
||
return this.maskCache.get(cacheKey);
|
||
}
|
||
let mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||
for (const name of componentNames) {
|
||
const componentId = this.getComponentId(name);
|
||
if (componentId !== void 0) {
|
||
const componentMask = BitMask64Utils.create(componentId);
|
||
BitMask64Utils.orInPlace(mask, componentMask);
|
||
}
|
||
}
|
||
this.maskCache.set(cacheKey, mask);
|
||
return mask;
|
||
}
|
||
/**
|
||
* 清除掩码缓存
|
||
*/
|
||
static clearMaskCache() {
|
||
this.maskCache.clear();
|
||
}
|
||
/**
|
||
* 重置注册表(用于测试)
|
||
*/
|
||
static reset() {
|
||
this.componentTypes.clear();
|
||
this.componentNameToType.clear();
|
||
this.componentNameToId.clear();
|
||
this.maskCache.clear();
|
||
this.nextBitIndex = 0;
|
||
}
|
||
}
|
||
ComponentRegistry._logger = createLogger("ComponentStorage");
|
||
ComponentRegistry.componentTypes = /* @__PURE__ */ new Map();
|
||
ComponentRegistry.componentNameToType = /* @__PURE__ */ new Map();
|
||
ComponentRegistry.componentNameToId = /* @__PURE__ */ new Map();
|
||
ComponentRegistry.maskCache = /* @__PURE__ */ new Map();
|
||
ComponentRegistry.nextBitIndex = 0;
|
||
ComponentRegistry.maxComponents = 64;
|
||
class ComponentStorage {
|
||
constructor(componentType) {
|
||
this.dense = [];
|
||
this.entityIds = [];
|
||
this.entityToIndex = /* @__PURE__ */ new Map();
|
||
this.componentType = componentType;
|
||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||
ComponentRegistry.register(componentType);
|
||
}
|
||
}
|
||
/**
|
||
* 添加组件
|
||
* @param entityId 实体ID
|
||
* @param component 组件实例
|
||
*/
|
||
addComponent(entityId, component) {
|
||
if (this.entityToIndex.has(entityId)) {
|
||
throw new Error(`Entity ${entityId} already has component ${getComponentTypeName(this.componentType)}`);
|
||
}
|
||
const index = this.dense.length;
|
||
this.dense.push(component);
|
||
this.entityIds.push(entityId);
|
||
this.entityToIndex.set(entityId, index);
|
||
}
|
||
/**
|
||
* 获取组件
|
||
* @param entityId 实体ID
|
||
* @returns 组件实例或null
|
||
*/
|
||
getComponent(entityId) {
|
||
const index = this.entityToIndex.get(entityId);
|
||
return index !== void 0 ? this.dense[index] : null;
|
||
}
|
||
/**
|
||
* 检查实体是否有此组件
|
||
* @param entityId 实体ID
|
||
* @returns 是否有组件
|
||
*/
|
||
hasComponent(entityId) {
|
||
return this.entityToIndex.has(entityId);
|
||
}
|
||
/**
|
||
* 移除组件
|
||
* @param entityId 实体ID
|
||
* @returns 被移除的组件或null
|
||
*/
|
||
removeComponent(entityId) {
|
||
const index = this.entityToIndex.get(entityId);
|
||
if (index === void 0) {
|
||
return null;
|
||
}
|
||
const component = this.dense[index];
|
||
const lastIndex = this.dense.length - 1;
|
||
if (index !== lastIndex) {
|
||
const lastComponent = this.dense[lastIndex];
|
||
const lastEntityId = this.entityIds[lastIndex];
|
||
this.dense[index] = lastComponent;
|
||
this.entityIds[index] = lastEntityId;
|
||
this.entityToIndex.set(lastEntityId, index);
|
||
}
|
||
this.dense.pop();
|
||
this.entityIds.pop();
|
||
this.entityToIndex.delete(entityId);
|
||
return component;
|
||
}
|
||
/**
|
||
* 高效遍历所有组件
|
||
* @param callback 回调函数
|
||
*/
|
||
forEach(callback) {
|
||
for (let i = 0; i < this.dense.length; i++) {
|
||
callback(this.dense[i], this.entityIds[i], i);
|
||
}
|
||
}
|
||
/**
|
||
* 获取所有组件
|
||
* @returns 组件数组
|
||
*/
|
||
getDenseArray() {
|
||
return {
|
||
components: [...this.dense],
|
||
entityIds: [...this.entityIds]
|
||
};
|
||
}
|
||
/**
|
||
* 清空所有组件
|
||
*/
|
||
clear() {
|
||
this.dense.length = 0;
|
||
this.entityIds.length = 0;
|
||
this.entityToIndex.clear();
|
||
}
|
||
/**
|
||
* 获取组件数量
|
||
*/
|
||
get size() {
|
||
return this.dense.length;
|
||
}
|
||
/**
|
||
* 获取组件类型
|
||
*/
|
||
get type() {
|
||
return this.componentType;
|
||
}
|
||
/**
|
||
* 获取存储统计信息
|
||
*/
|
||
getStats() {
|
||
const totalSlots = this.dense.length;
|
||
const usedSlots = this.dense.length;
|
||
const freeSlots = 0;
|
||
const fragmentation = 0;
|
||
return {
|
||
totalSlots,
|
||
usedSlots,
|
||
freeSlots,
|
||
fragmentation
|
||
};
|
||
}
|
||
}
|
||
class ComponentStorageManager {
|
||
constructor() {
|
||
this.storages = /* @__PURE__ */ new Map();
|
||
}
|
||
/**
|
||
* 检查组件类型是否启用SoA存储
|
||
* @param componentType 组件类型
|
||
* @returns 是否为SoA存储
|
||
*/
|
||
isSoAStorage(componentType) {
|
||
const storage = this.storages.get(componentType);
|
||
return storage instanceof SoAStorage;
|
||
}
|
||
/**
|
||
* 获取SoA存储器(类型安全)
|
||
* @param componentType 组件类型
|
||
* @returns SoA存储器或null
|
||
*/
|
||
getSoAStorage(componentType) {
|
||
const storage = this.getStorage(componentType);
|
||
return storage instanceof SoAStorage ? storage : null;
|
||
}
|
||
/**
|
||
* 直接获取SoA字段数组(类型安全)
|
||
* @param componentType 组件类型
|
||
* @param fieldName 字段名
|
||
* @returns TypedArray或null
|
||
*/
|
||
getFieldArray(componentType, fieldName) {
|
||
const soaStorage = this.getSoAStorage(componentType);
|
||
return soaStorage ? soaStorage.getFieldArray(fieldName) : null;
|
||
}
|
||
/**
|
||
* 直接获取SoA字段数组(类型安全,带字段名检查)
|
||
* @param componentType 组件类型
|
||
* @param fieldName 字段名(类型检查)
|
||
* @returns TypedArray或null
|
||
*/
|
||
getTypedFieldArray(componentType, fieldName) {
|
||
const soaStorage = this.getSoAStorage(componentType);
|
||
return soaStorage ? soaStorage.getTypedFieldArray(fieldName) : null;
|
||
}
|
||
/**
|
||
* 获取SoA存储的活跃索引
|
||
* @param componentType 组件类型
|
||
* @returns 活跃索引数组或空数组
|
||
*/
|
||
getActiveIndices(componentType) {
|
||
const soaStorage = this.getSoAStorage(componentType);
|
||
return soaStorage ? soaStorage.getActiveIndices() : [];
|
||
}
|
||
/**
|
||
* 获取实体在SoA存储中的索引
|
||
* @param componentType 组件类型
|
||
* @param entityId 实体ID
|
||
* @returns 存储索引或undefined
|
||
*/
|
||
getEntityIndex(componentType, entityId) {
|
||
const soaStorage = this.getSoAStorage(componentType);
|
||
return soaStorage ? soaStorage.getEntityIndex(entityId) : void 0;
|
||
}
|
||
/**
|
||
* 根据索引获取实体ID
|
||
* @param componentType 组件类型
|
||
* @param index 存储索引
|
||
* @returns 实体ID或undefined
|
||
*/
|
||
getEntityIdByIndex(componentType, index) {
|
||
const soaStorage = this.getSoAStorage(componentType);
|
||
return soaStorage ? soaStorage.getEntityIdByIndex(index) : void 0;
|
||
}
|
||
/**
|
||
* 获取或创建组件存储器(默认原始存储)
|
||
* @param componentType 组件类型
|
||
* @returns 组件存储器
|
||
*/
|
||
getStorage(componentType) {
|
||
let storage = this.storages.get(componentType);
|
||
if (!storage) {
|
||
const enableSoA = componentType.__enableSoA;
|
||
if (enableSoA) {
|
||
storage = new SoAStorage(componentType);
|
||
ComponentStorageManager._logger.info(`为 ${getComponentTypeName(componentType)} 启用SoA优化(适用于大规模批量操作)`);
|
||
} else {
|
||
storage = new ComponentStorage(componentType);
|
||
}
|
||
this.storages.set(componentType, storage);
|
||
}
|
||
return storage;
|
||
}
|
||
/**
|
||
* 添加组件
|
||
* @param entityId 实体ID
|
||
* @param component 组件实例
|
||
*/
|
||
addComponent(entityId, component) {
|
||
const componentType = component.constructor;
|
||
const storage = this.getStorage(componentType);
|
||
storage.addComponent(entityId, component);
|
||
}
|
||
/**
|
||
* 获取组件
|
||
* @param entityId 实体ID
|
||
* @param componentType 组件类型
|
||
* @returns 组件实例或null
|
||
*/
|
||
getComponent(entityId, componentType) {
|
||
const storage = this.storages.get(componentType);
|
||
return storage ? storage.getComponent(entityId) : null;
|
||
}
|
||
/**
|
||
* 检查实体是否有组件
|
||
* @param entityId 实体ID
|
||
* @param componentType 组件类型
|
||
* @returns 是否有组件
|
||
*/
|
||
hasComponent(entityId, componentType) {
|
||
const storage = this.storages.get(componentType);
|
||
return storage ? storage.hasComponent(entityId) : false;
|
||
}
|
||
/**
|
||
* 移除组件
|
||
* @param entityId 实体ID
|
||
* @param componentType 组件类型
|
||
* @returns 被移除的组件或null
|
||
*/
|
||
removeComponent(entityId, componentType) {
|
||
const storage = this.storages.get(componentType);
|
||
return storage ? storage.removeComponent(entityId) : null;
|
||
}
|
||
/**
|
||
* 移除实体的所有组件
|
||
* @param entityId 实体ID
|
||
*/
|
||
removeAllComponents(entityId) {
|
||
for (const storage of this.storages.values()) {
|
||
storage.removeComponent(entityId);
|
||
}
|
||
}
|
||
/**
|
||
* 获取实体的组件位掩码
|
||
* @param entityId 实体ID
|
||
* @returns 组件位掩码
|
||
*/
|
||
getComponentMask(entityId) {
|
||
let mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||
for (const [componentType, storage] of this.storages.entries()) {
|
||
if (storage.hasComponent(entityId)) {
|
||
const componentMask = ComponentRegistry.getBitMask(componentType);
|
||
BitMask64Utils.orInPlace(mask, componentMask);
|
||
}
|
||
}
|
||
return mask;
|
||
}
|
||
/**
|
||
* 获取所有存储器的统计信息
|
||
*/
|
||
getAllStats() {
|
||
const stats = /* @__PURE__ */ new Map();
|
||
for (const [componentType, storage] of this.storages.entries()) {
|
||
const typeName = getComponentTypeName(componentType);
|
||
stats.set(typeName, storage.getStats());
|
||
}
|
||
return stats;
|
||
}
|
||
/**
|
||
* 清空所有存储器
|
||
*/
|
||
clear() {
|
||
for (const storage of this.storages.values()) {
|
||
storage.clear();
|
||
}
|
||
this.storages.clear();
|
||
}
|
||
}
|
||
ComponentStorageManager._logger = createLogger("ComponentStorage");
|
||
class EntityComparer {
|
||
/**
|
||
* 比较两个实体
|
||
*
|
||
* @param self - 第一个实体
|
||
* @param other - 第二个实体
|
||
* @returns 比较结果,负数表示self优先级更高,正数表示other优先级更高,0表示相等
|
||
*/
|
||
compare(self2, other) {
|
||
let compare = self2.updateOrder - other.updateOrder;
|
||
if (compare == 0)
|
||
compare = self2.id - other.id;
|
||
return compare;
|
||
}
|
||
}
|
||
class Entity {
|
||
/**
|
||
* 通知Scene中的QuerySystem实体组件发生变动
|
||
*
|
||
* @param entity 发生组件变动的实体
|
||
*/
|
||
static notifyQuerySystems(entity) {
|
||
if (entity.scene && entity.scene.querySystem) {
|
||
entity.scene.querySystem.updateEntity(entity);
|
||
entity.scene.clearSystemEntityCaches();
|
||
}
|
||
}
|
||
/**
|
||
* 构造函数
|
||
*
|
||
* @param name - 实体名称
|
||
* @param id - 实体唯一标识符
|
||
*/
|
||
constructor(name, id) {
|
||
this.components = [];
|
||
this.scene = null;
|
||
this.updateInterval = 1;
|
||
this._isDestroyed = false;
|
||
this._parent = null;
|
||
this._children = [];
|
||
this._active = true;
|
||
this._tag = 0;
|
||
this._enabled = true;
|
||
this._updateOrder = 0;
|
||
this._componentMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||
this._componentsByTypeId = [];
|
||
this._componentDenseIndexByTypeId = [];
|
||
this.name = name;
|
||
this.id = id;
|
||
}
|
||
/**
|
||
* 获取销毁状态
|
||
* @returns 如果实体已被销毁则返回true
|
||
*/
|
||
get isDestroyed() {
|
||
return this._isDestroyed;
|
||
}
|
||
/**
|
||
* 获取父实体
|
||
* @returns 父实体,如果没有父实体则返回null
|
||
*/
|
||
get parent() {
|
||
return this._parent;
|
||
}
|
||
/**
|
||
* 获取子实体数组的只读副本
|
||
*
|
||
* @returns 子实体数组的副本
|
||
*/
|
||
get children() {
|
||
return [...this._children];
|
||
}
|
||
/**
|
||
* 获取子实体数量
|
||
*
|
||
* @returns 子实体的数量
|
||
*/
|
||
get childCount() {
|
||
return this._children.length;
|
||
}
|
||
/**
|
||
* 获取活跃状态
|
||
*
|
||
* @returns 如果实体处于活跃状态则返回true
|
||
*/
|
||
get active() {
|
||
return this._active;
|
||
}
|
||
/**
|
||
* 设置活跃状态
|
||
*
|
||
* 设置实体的活跃状态,会影响子实体的有效活跃状态。
|
||
*
|
||
* @param value - 新的活跃状态
|
||
*/
|
||
set active(value) {
|
||
if (this._active !== value) {
|
||
this._active = value;
|
||
this.onActiveChanged();
|
||
}
|
||
}
|
||
/**
|
||
* 获取实体的有效活跃状态
|
||
*
|
||
* 考虑父实体的活跃状态,只有当实体本身和所有父实体都处于活跃状态时才返回true。
|
||
*
|
||
* @returns 有效的活跃状态
|
||
*/
|
||
get activeInHierarchy() {
|
||
if (!this._active)
|
||
return false;
|
||
if (this._parent)
|
||
return this._parent.activeInHierarchy;
|
||
return true;
|
||
}
|
||
/**
|
||
* 获取实体标签
|
||
*
|
||
* @returns 实体的数字标签
|
||
*/
|
||
get tag() {
|
||
return this._tag;
|
||
}
|
||
/**
|
||
* 设置实体标签
|
||
*
|
||
* @param value - 新的标签值
|
||
*/
|
||
set tag(value) {
|
||
this._tag = value;
|
||
}
|
||
/**
|
||
* 获取启用状态
|
||
*
|
||
* @returns 如果实体已启用则返回true
|
||
*/
|
||
get enabled() {
|
||
return this._enabled;
|
||
}
|
||
/**
|
||
* 设置启用状态
|
||
*
|
||
* @param value - 新的启用状态
|
||
*/
|
||
set enabled(value) {
|
||
this._enabled = value;
|
||
}
|
||
/**
|
||
* 获取更新顺序
|
||
*
|
||
* @returns 实体的更新顺序值
|
||
*/
|
||
get updateOrder() {
|
||
return this._updateOrder;
|
||
}
|
||
/**
|
||
* 设置更新顺序
|
||
*
|
||
* @param value - 新的更新顺序值
|
||
*/
|
||
set updateOrder(value) {
|
||
this._updateOrder = value;
|
||
}
|
||
/**
|
||
* 获取组件位掩码
|
||
*
|
||
* @returns 实体的组件位掩码
|
||
*/
|
||
get componentMask() {
|
||
return this._componentMask;
|
||
}
|
||
/**
|
||
* 创建并添加组件
|
||
*
|
||
* @param componentType - 组件类型
|
||
* @param args - 组件构造函数参数
|
||
* @returns 创建的组件实例
|
||
*/
|
||
createComponent(componentType, ...args) {
|
||
const component = new componentType(...args);
|
||
return this.addComponent(component);
|
||
}
|
||
/**
|
||
* 内部添加组件方法(不进行重复检查,用于初始化)
|
||
*
|
||
* @param component - 要添加的组件实例
|
||
* @returns 添加的组件实例
|
||
*/
|
||
addComponentInternal(component) {
|
||
const componentType = component.constructor;
|
||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||
ComponentRegistry.register(componentType);
|
||
}
|
||
const typeId = ComponentRegistry.getBitIndex(componentType);
|
||
component.entity = this;
|
||
this._componentsByTypeId[typeId] = component;
|
||
const denseIndex = this.components.length;
|
||
this._componentDenseIndexByTypeId[typeId] = denseIndex;
|
||
this.components.push(component);
|
||
const componentMask = ComponentRegistry.getBitMask(componentType);
|
||
BitMask64Utils.orInPlace(this._componentMask, componentMask);
|
||
return component;
|
||
}
|
||
/**
|
||
* 添加组件到实体
|
||
*
|
||
* @param component - 要添加的组件实例
|
||
* @returns 添加的组件实例
|
||
* @throws {Error} 如果组件类型已存在
|
||
*/
|
||
addComponent(component) {
|
||
const componentType = component.constructor;
|
||
if (this.hasComponent(componentType)) {
|
||
throw new Error(`Entity ${this.name} already has component ${getComponentTypeName(componentType)}`);
|
||
}
|
||
this.addComponentInternal(component);
|
||
if (this.scene && this.scene.componentStorageManager) {
|
||
this.scene.componentStorageManager.addComponent(this.id, component);
|
||
}
|
||
component.onAddedToEntity();
|
||
if (Entity.eventBus) {
|
||
Entity.eventBus.emitComponentAdded({
|
||
timestamp: Date.now(),
|
||
source: "Entity",
|
||
entityId: this.id,
|
||
entityName: this.name,
|
||
entityTag: this.tag?.toString(),
|
||
componentType: getComponentTypeName(componentType),
|
||
component
|
||
});
|
||
}
|
||
Entity.notifyQuerySystems(this);
|
||
return component;
|
||
}
|
||
/**
|
||
* 获取指定类型的组件
|
||
*
|
||
* @param type - 组件类型
|
||
* @returns 组件实例或null
|
||
*/
|
||
getComponent(type) {
|
||
if (!ComponentRegistry.isRegistered(type)) {
|
||
return null;
|
||
}
|
||
const mask = ComponentRegistry.getBitMask(type);
|
||
if (BitMask64Utils.hasNone(this._componentMask, mask)) {
|
||
return null;
|
||
}
|
||
const typeId = ComponentRegistry.getBitIndex(type);
|
||
const component = this._componentsByTypeId[typeId];
|
||
if (component && component.constructor === type) {
|
||
return component;
|
||
}
|
||
if (this.scene && this.scene.componentStorageManager) {
|
||
const storageComponent = this.scene.componentStorageManager.getComponent(this.id, type);
|
||
if (storageComponent) {
|
||
this._componentsByTypeId[typeId] = storageComponent;
|
||
if (!this.components.includes(storageComponent)) {
|
||
const denseIndex = this.components.length;
|
||
this._componentDenseIndexByTypeId[typeId] = denseIndex;
|
||
this.components.push(storageComponent);
|
||
}
|
||
return storageComponent;
|
||
}
|
||
}
|
||
for (let i = 0; i < this.components.length; i++) {
|
||
const component2 = this.components[i];
|
||
if (component2 instanceof type) {
|
||
this._componentsByTypeId[typeId] = component2;
|
||
this._componentDenseIndexByTypeId[typeId] = i;
|
||
return component2;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
/**
|
||
* 检查实体是否有指定类型的组件
|
||
*
|
||
* @param type - 组件类型
|
||
* @returns 如果有该组件则返回true
|
||
*/
|
||
hasComponent(type) {
|
||
if (!ComponentRegistry.isRegistered(type)) {
|
||
return false;
|
||
}
|
||
const mask = ComponentRegistry.getBitMask(type);
|
||
return BitMask64Utils.hasAny(this._componentMask, mask);
|
||
}
|
||
/**
|
||
* 获取或创建指定类型的组件
|
||
*
|
||
* @param type - 组件类型
|
||
* @param args - 组件构造函数参数(仅在创建时使用)
|
||
* @returns 组件实例
|
||
*/
|
||
getOrCreateComponent(type, ...args) {
|
||
let component = this.getComponent(type);
|
||
if (!component) {
|
||
component = this.createComponent(type, ...args);
|
||
}
|
||
return component;
|
||
}
|
||
/**
|
||
* 移除指定的组件
|
||
*
|
||
* @param component - 要移除的组件实例
|
||
*/
|
||
removeComponent(component) {
|
||
const componentType = component.constructor;
|
||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||
return;
|
||
}
|
||
const typeId = ComponentRegistry.getBitIndex(componentType);
|
||
this._componentsByTypeId[typeId] = void 0;
|
||
BitMask64Utils.clearBit(this._componentMask, typeId);
|
||
const denseIndex = this._componentDenseIndexByTypeId[typeId];
|
||
if (denseIndex !== void 0 && denseIndex < this.components.length) {
|
||
const lastIndex = this.components.length - 1;
|
||
if (denseIndex !== lastIndex) {
|
||
const lastComponent = this.components[lastIndex];
|
||
this.components[denseIndex] = lastComponent;
|
||
const lastComponentType = lastComponent.constructor;
|
||
const lastTypeId = ComponentRegistry.getBitIndex(lastComponentType);
|
||
this._componentDenseIndexByTypeId[lastTypeId] = denseIndex;
|
||
}
|
||
this.components.pop();
|
||
}
|
||
this._componentDenseIndexByTypeId[typeId] = -1;
|
||
if (this.scene && this.scene.componentStorageManager) {
|
||
this.scene.componentStorageManager.removeComponent(this.id, componentType);
|
||
}
|
||
if (component.onRemovedFromEntity) {
|
||
component.onRemovedFromEntity();
|
||
}
|
||
if (Entity.eventBus) {
|
||
Entity.eventBus.emitComponentRemoved({
|
||
timestamp: Date.now(),
|
||
source: "Entity",
|
||
entityId: this.id,
|
||
entityName: this.name,
|
||
entityTag: this.tag?.toString(),
|
||
componentType: getComponentTypeName(componentType),
|
||
component
|
||
});
|
||
}
|
||
component.entity = null;
|
||
Entity.notifyQuerySystems(this);
|
||
}
|
||
/**
|
||
* 移除指定类型的组件
|
||
*
|
||
* @param type - 组件类型
|
||
* @returns 被移除的组件实例或null
|
||
*/
|
||
removeComponentByType(type) {
|
||
const component = this.getComponent(type);
|
||
if (component) {
|
||
this.removeComponent(component);
|
||
return component;
|
||
}
|
||
return null;
|
||
}
|
||
/**
|
||
* 移除所有组件
|
||
*/
|
||
removeAllComponents() {
|
||
const componentsToRemove = [...this.components];
|
||
this._componentsByTypeId.length = 0;
|
||
this._componentDenseIndexByTypeId.length = 0;
|
||
BitMask64Utils.clear(this._componentMask);
|
||
for (const component of componentsToRemove) {
|
||
const componentType = component.constructor;
|
||
if (this.scene && this.scene.componentStorageManager) {
|
||
this.scene.componentStorageManager.removeComponent(this.id, componentType);
|
||
}
|
||
component.onRemovedFromEntity();
|
||
component.entity = null;
|
||
}
|
||
this.components.length = 0;
|
||
Entity.notifyQuerySystems(this);
|
||
}
|
||
/**
|
||
* 批量添加组件
|
||
*
|
||
* @param components - 要添加的组件数组
|
||
* @returns 添加的组件数组
|
||
*/
|
||
addComponents(components) {
|
||
const addedComponents = [];
|
||
for (const component of components) {
|
||
try {
|
||
addedComponents.push(this.addComponent(component));
|
||
} catch (error) {
|
||
Entity._logger.warn(`添加组件失败 ${getComponentInstanceTypeName(component)}:`, error);
|
||
}
|
||
}
|
||
return addedComponents;
|
||
}
|
||
/**
|
||
* 批量移除组件类型
|
||
*
|
||
* @param componentTypes - 要移除的组件类型数组
|
||
* @returns 被移除的组件数组
|
||
*/
|
||
removeComponentsByTypes(componentTypes) {
|
||
const removedComponents = [];
|
||
for (const componentType of componentTypes) {
|
||
removedComponents.push(this.removeComponentByType(componentType));
|
||
}
|
||
return removedComponents;
|
||
}
|
||
/**
|
||
* 获取所有指定类型的组件
|
||
*
|
||
* @param type - 组件类型
|
||
* @returns 组件实例数组
|
||
*/
|
||
getComponents(type) {
|
||
const result = [];
|
||
for (const component of this.components) {
|
||
if (component instanceof type) {
|
||
result.push(component);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 添加子实体
|
||
*
|
||
* @param child - 要添加的子实体
|
||
* @returns 添加的子实体
|
||
*/
|
||
addChild(child) {
|
||
if (child === this) {
|
||
throw new Error("Entity cannot be its own child");
|
||
}
|
||
if (child._parent === this) {
|
||
return child;
|
||
}
|
||
if (child._parent) {
|
||
child._parent.removeChild(child);
|
||
}
|
||
child._parent = this;
|
||
this._children.push(child);
|
||
if (!child.scene && this.scene) {
|
||
child.scene = this.scene;
|
||
this.scene.addEntity(child);
|
||
}
|
||
return child;
|
||
}
|
||
/**
|
||
* 移除子实体
|
||
*
|
||
* @param child - 要移除的子实体
|
||
* @returns 是否成功移除
|
||
*/
|
||
removeChild(child) {
|
||
const index = this._children.indexOf(child);
|
||
if (index === -1) {
|
||
return false;
|
||
}
|
||
this._children.splice(index, 1);
|
||
child._parent = null;
|
||
return true;
|
||
}
|
||
/**
|
||
* 移除所有子实体
|
||
*/
|
||
removeAllChildren() {
|
||
const childrenToRemove = [...this._children];
|
||
for (const child of childrenToRemove) {
|
||
this.removeChild(child);
|
||
}
|
||
}
|
||
/**
|
||
* 根据名称查找子实体
|
||
*
|
||
* @param name - 子实体名称
|
||
* @param recursive - 是否递归查找
|
||
* @returns 找到的子实体或null
|
||
*/
|
||
findChild(name, recursive = false) {
|
||
for (const child of this._children) {
|
||
if (child.name === name) {
|
||
return child;
|
||
}
|
||
}
|
||
if (recursive) {
|
||
for (const child of this._children) {
|
||
const found = child.findChild(name, true);
|
||
if (found) {
|
||
return found;
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
/**
|
||
* 根据标签查找子实体
|
||
*
|
||
* @param tag - 标签
|
||
* @param recursive - 是否递归查找
|
||
* @returns 找到的子实体数组
|
||
*/
|
||
findChildrenByTag(tag, recursive = false) {
|
||
const result = [];
|
||
for (const child of this._children) {
|
||
if (child.tag === tag) {
|
||
result.push(child);
|
||
}
|
||
}
|
||
if (recursive) {
|
||
for (const child of this._children) {
|
||
result.push(...child.findChildrenByTag(tag, true));
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 获取根实体
|
||
*
|
||
* @returns 层次结构的根实体
|
||
*/
|
||
getRoot() {
|
||
let root = this;
|
||
while (root._parent) {
|
||
root = root._parent;
|
||
}
|
||
return root;
|
||
}
|
||
/**
|
||
* 检查是否是指定实体的祖先
|
||
*
|
||
* @param entity - 要检查的实体
|
||
* @returns 如果是祖先则返回true
|
||
*/
|
||
isAncestorOf(entity) {
|
||
let current = entity._parent;
|
||
while (current) {
|
||
if (current === this) {
|
||
return true;
|
||
}
|
||
current = current._parent;
|
||
}
|
||
return false;
|
||
}
|
||
/**
|
||
* 检查是否是指定实体的后代
|
||
*
|
||
* @param entity - 要检查的实体
|
||
* @returns 如果是后代则返回true
|
||
*/
|
||
isDescendantOf(entity) {
|
||
return entity.isAncestorOf(this);
|
||
}
|
||
/**
|
||
* 获取层次深度
|
||
*
|
||
* @returns 在层次结构中的深度(根实体为0)
|
||
*/
|
||
getDepth() {
|
||
let depth = 0;
|
||
let current = this._parent;
|
||
while (current) {
|
||
depth++;
|
||
current = current._parent;
|
||
}
|
||
return depth;
|
||
}
|
||
/**
|
||
* 遍历所有子实体(深度优先)
|
||
*
|
||
* @param callback - 对每个子实体执行的回调函数
|
||
* @param recursive - 是否递归遍历
|
||
*/
|
||
forEachChild(callback, recursive = false) {
|
||
this._children.forEach((child, index) => {
|
||
callback(child, index);
|
||
if (recursive) {
|
||
child.forEachChild(callback, true);
|
||
}
|
||
});
|
||
}
|
||
/**
|
||
* 活跃状态改变时的回调
|
||
*/
|
||
onActiveChanged() {
|
||
for (const component of this.components) {
|
||
if ("onActiveChanged" in component && typeof component.onActiveChanged === "function") {
|
||
component.onActiveChanged();
|
||
}
|
||
}
|
||
if (this.scene && this.scene.eventSystem) {
|
||
this.scene.eventSystem.emitSync("entity:activeChanged", {
|
||
entity: this,
|
||
active: this._active,
|
||
activeInHierarchy: this.activeInHierarchy
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 更新实体
|
||
*
|
||
* 调用所有组件的更新方法,并更新子实体。
|
||
*/
|
||
update() {
|
||
if (!this.activeInHierarchy || this._isDestroyed) {
|
||
return;
|
||
}
|
||
for (const component of this.components) {
|
||
if (component.enabled) {
|
||
component.update();
|
||
}
|
||
}
|
||
for (const child of this._children) {
|
||
child.update();
|
||
}
|
||
}
|
||
/**
|
||
* 销毁实体
|
||
*
|
||
* 移除所有组件、子实体并标记为已销毁。
|
||
*/
|
||
destroy() {
|
||
if (this._isDestroyed) {
|
||
return;
|
||
}
|
||
this._isDestroyed = true;
|
||
const childrenToDestroy = [...this._children];
|
||
for (const child of childrenToDestroy) {
|
||
child.destroy();
|
||
}
|
||
if (this._parent) {
|
||
this._parent.removeChild(this);
|
||
}
|
||
this.removeAllComponents();
|
||
if (this.scene) {
|
||
if (this.scene.querySystem) {
|
||
this.scene.querySystem.removeEntity(this);
|
||
}
|
||
if (this.scene.entities) {
|
||
this.scene.entities.remove(this);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 比较实体
|
||
*
|
||
* @param other - 另一个实体
|
||
* @returns 比较结果
|
||
*/
|
||
compareTo(other) {
|
||
return EntityComparer.prototype.compare(this, other);
|
||
}
|
||
/**
|
||
* 获取实体的字符串表示
|
||
*
|
||
* @returns 实体的字符串描述
|
||
*/
|
||
toString() {
|
||
return `Entity[${this.name}:${this.id}]`;
|
||
}
|
||
/**
|
||
* 获取实体的调试信息(包含组件缓存信息)
|
||
*
|
||
* @returns 包含实体详细信息的对象
|
||
*/
|
||
getDebugInfo() {
|
||
return {
|
||
name: this.name,
|
||
id: this.id,
|
||
enabled: this._enabled,
|
||
active: this._active,
|
||
activeInHierarchy: this.activeInHierarchy,
|
||
destroyed: this._isDestroyed,
|
||
componentCount: this.components.length,
|
||
componentTypes: this.components.map((c) => getComponentInstanceTypeName(c)),
|
||
componentMask: BitMask64Utils.toString(this._componentMask, 2),
|
||
// 二进制表示
|
||
parentId: this._parent?.id || null,
|
||
childCount: this._children.length,
|
||
childIds: this._children.map((c) => c.id),
|
||
depth: this.getDepth(),
|
||
indexMappingSize: this._componentsByTypeId.filter((c) => c !== void 0).length,
|
||
denseIndexMappingSize: this._componentDenseIndexByTypeId.filter((idx) => idx !== -1 && idx !== void 0).length
|
||
};
|
||
}
|
||
}
|
||
Entity._logger = createLogger("Entity");
|
||
Entity.entityComparer = new EntityComparer();
|
||
Entity.eventBus = null;
|
||
class EntityBuilder {
|
||
constructor(scene, storageManager) {
|
||
this.scene = scene;
|
||
this.storageManager = storageManager;
|
||
this.entity = new Entity("", scene.identifierPool.checkOut());
|
||
}
|
||
/**
|
||
* 设置实体名称
|
||
* @param name 实体名称
|
||
* @returns 实体构建器
|
||
*/
|
||
named(name) {
|
||
this.entity.name = name;
|
||
return this;
|
||
}
|
||
/**
|
||
* 设置实体标签
|
||
* @param tag 标签
|
||
* @returns 实体构建器
|
||
*/
|
||
tagged(tag) {
|
||
this.entity.tag = tag;
|
||
return this;
|
||
}
|
||
/**
|
||
* 添加组件
|
||
* @param component 组件实例
|
||
* @returns 实体构建器
|
||
*/
|
||
with(component) {
|
||
this.entity.addComponent(component);
|
||
return this;
|
||
}
|
||
/**
|
||
* 添加多个组件
|
||
* @param components 组件数组
|
||
* @returns 实体构建器
|
||
*/
|
||
withComponents(...components) {
|
||
for (const component of components) {
|
||
this.entity.addComponent(component);
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 条件性添加组件
|
||
* @param condition 条件
|
||
* @param component 组件实例
|
||
* @returns 实体构建器
|
||
*/
|
||
withIf(condition, component) {
|
||
if (condition) {
|
||
this.entity.addComponent(component);
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 使用工厂函数创建并添加组件
|
||
* @param factory 组件工厂函数
|
||
* @returns 实体构建器
|
||
*/
|
||
withFactory(factory) {
|
||
const component = factory();
|
||
this.entity.addComponent(component);
|
||
return this;
|
||
}
|
||
/**
|
||
* 配置组件属性
|
||
* @param componentType 组件类型
|
||
* @param configurator 配置函数
|
||
* @returns 实体构建器
|
||
*/
|
||
configure(componentType, configurator) {
|
||
const component = this.entity.getComponent(componentType);
|
||
if (component) {
|
||
configurator(component);
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 设置实体为启用状态
|
||
* @param enabled 是否启用
|
||
* @returns 实体构建器
|
||
*/
|
||
enabled(enabled = true) {
|
||
this.entity.enabled = enabled;
|
||
return this;
|
||
}
|
||
/**
|
||
* 设置实体为活跃状态
|
||
* @param active 是否活跃
|
||
* @returns 实体构建器
|
||
*/
|
||
active(active = true) {
|
||
this.entity.active = active;
|
||
return this;
|
||
}
|
||
/**
|
||
* 添加子实体
|
||
* @param childBuilder 子实体构建器
|
||
* @returns 实体构建器
|
||
*/
|
||
withChild(childBuilder) {
|
||
const child = childBuilder.build();
|
||
this.entity.addChild(child);
|
||
return this;
|
||
}
|
||
/**
|
||
* 批量添加子实体
|
||
* @param childBuilders 子实体构建器数组
|
||
* @returns 实体构建器
|
||
*/
|
||
withChildren(...childBuilders) {
|
||
for (const childBuilder of childBuilders) {
|
||
const child = childBuilder.build();
|
||
this.entity.addChild(child);
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 使用工厂函数创建子实体
|
||
* @param childFactory 子实体工厂函数
|
||
* @returns 实体构建器
|
||
*/
|
||
withChildFactory(childFactory) {
|
||
const childBuilder = childFactory(this.entity);
|
||
const child = childBuilder.build();
|
||
this.entity.addChild(child);
|
||
return this;
|
||
}
|
||
/**
|
||
* 条件性添加子实体
|
||
* @param condition 条件
|
||
* @param childBuilder 子实体构建器
|
||
* @returns 实体构建器
|
||
*/
|
||
withChildIf(condition, childBuilder) {
|
||
if (condition) {
|
||
const child = childBuilder.build();
|
||
this.entity.addChild(child);
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 构建并返回实体
|
||
* @returns 构建的实体
|
||
*/
|
||
build() {
|
||
return this.entity;
|
||
}
|
||
/**
|
||
* 构建实体并添加到场景
|
||
* @returns 构建的实体
|
||
*/
|
||
spawn() {
|
||
this.scene.addEntity(this.entity);
|
||
return this.entity;
|
||
}
|
||
/**
|
||
* 克隆当前构建器
|
||
* @returns 新的实体构建器
|
||
*/
|
||
clone() {
|
||
const newBuilder = new EntityBuilder(this.scene, this.storageManager);
|
||
newBuilder.entity = this.entity;
|
||
return newBuilder;
|
||
}
|
||
}
|
||
class EntityList {
|
||
get count() {
|
||
return this.buffer.length;
|
||
}
|
||
constructor(scene) {
|
||
this.buffer = [];
|
||
this._idToEntity = /* @__PURE__ */ new Map();
|
||
this._nameToEntities = /* @__PURE__ */ new Map();
|
||
this._entitiesToAdd = [];
|
||
this._entitiesToRemove = [];
|
||
this._isUpdating = false;
|
||
this._enableEntityDirectUpdate = false;
|
||
this._scene = scene;
|
||
}
|
||
/**
|
||
* 设置是否启用实体直接更新
|
||
*/
|
||
setEnableEntityDirectUpdate(enabled) {
|
||
this._enableEntityDirectUpdate = enabled;
|
||
}
|
||
/**
|
||
* 添加实体(立即添加或延迟添加)
|
||
* @param entity 要添加的实体
|
||
*/
|
||
add(entity) {
|
||
if (this._isUpdating) {
|
||
this._entitiesToAdd.push(entity);
|
||
} else {
|
||
this.addImmediate(entity);
|
||
}
|
||
}
|
||
/**
|
||
* 立即添加实体
|
||
* @param entity 要添加的实体
|
||
*/
|
||
addImmediate(entity) {
|
||
if (this._idToEntity.has(entity.id)) {
|
||
return;
|
||
}
|
||
this.buffer.push(entity);
|
||
this._idToEntity.set(entity.id, entity);
|
||
this.updateNameIndex(entity, true);
|
||
}
|
||
/**
|
||
* 移除实体(立即移除或延迟移除)
|
||
* @param entity 要移除的实体
|
||
*/
|
||
remove(entity) {
|
||
if (this._isUpdating) {
|
||
this._entitiesToRemove.push(entity);
|
||
} else {
|
||
this.removeImmediate(entity);
|
||
}
|
||
}
|
||
/**
|
||
* 立即移除实体
|
||
* @param entity 要移除的实体
|
||
*/
|
||
removeImmediate(entity) {
|
||
const index = this.buffer.indexOf(entity);
|
||
if (index !== -1) {
|
||
this.buffer.splice(index, 1);
|
||
this._idToEntity.delete(entity.id);
|
||
this.updateNameIndex(entity, false);
|
||
if (this._scene && this._scene.identifierPool) {
|
||
this._scene.identifierPool.checkIn(entity.id);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 移除所有实体
|
||
*/
|
||
removeAllEntities() {
|
||
const idsToRecycle = [];
|
||
for (let i = this.buffer.length - 1; i >= 0; i--) {
|
||
idsToRecycle.push(this.buffer[i].id);
|
||
this.buffer[i].destroy();
|
||
}
|
||
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;
|
||
}
|
||
/**
|
||
* 更新实体列表,处理延迟操作
|
||
*/
|
||
updateLists() {
|
||
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;
|
||
}
|
||
}
|
||
/**
|
||
* 更新实体列表和实体
|
||
*/
|
||
update() {
|
||
this._isUpdating = true;
|
||
try {
|
||
if (this._enableEntityDirectUpdate) {
|
||
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
|
||
*/
|
||
findEntity(name) {
|
||
const entities = this._nameToEntities.get(name);
|
||
return entities && entities.length > 0 ? entities[0] : null;
|
||
}
|
||
/**
|
||
* 根据名称查找所有实体
|
||
* @param name 实体名称
|
||
* @returns 找到的所有实体数组
|
||
*/
|
||
findEntitiesByName(name) {
|
||
return this._nameToEntities.get(name) || [];
|
||
}
|
||
/**
|
||
* 根据ID查找实体(使用索引,O(1)复杂度)
|
||
* @param id 实体ID
|
||
* @returns 找到的实体或null
|
||
*/
|
||
findEntityById(id) {
|
||
return this._idToEntity.get(id) || null;
|
||
}
|
||
/**
|
||
* 根据标签查找实体
|
||
* @param tag 标签
|
||
* @returns 找到的所有实体数组
|
||
*/
|
||
findEntitiesByTag(tag) {
|
||
const result = [];
|
||
for (const entity of this.buffer) {
|
||
if (entity.tag === tag) {
|
||
result.push(entity);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 根据组件类型查找实体
|
||
* @param componentType 组件类型
|
||
* @returns 找到的所有实体数组
|
||
*/
|
||
findEntitiesWithComponent(componentType) {
|
||
const result = [];
|
||
for (const entity of this.buffer) {
|
||
if (entity.hasComponent(componentType)) {
|
||
result.push(entity);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 批量操作:对所有实体执行指定操作
|
||
* @param action 要执行的操作
|
||
*/
|
||
forEach(action) {
|
||
for (const entity of this.buffer) {
|
||
action(entity);
|
||
}
|
||
}
|
||
/**
|
||
* 批量操作:对符合条件的实体执行指定操作
|
||
* @param predicate 筛选条件
|
||
* @param action 要执行的操作
|
||
*/
|
||
forEachWhere(predicate, action) {
|
||
for (const entity of this.buffer) {
|
||
if (predicate(entity)) {
|
||
action(entity);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 更新名称索引
|
||
* @param entity 实体
|
||
* @param isAdd 是否为添加操作
|
||
*/
|
||
updateNameIndex(entity, isAdd) {
|
||
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 统计信息
|
||
*/
|
||
getStats() {
|
||
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
|
||
};
|
||
}
|
||
}
|
||
class EntityProcessorList {
|
||
constructor() {
|
||
this._processors = [];
|
||
this._isDirty = false;
|
||
}
|
||
/**
|
||
* 设置为脏状态,需要重新排序
|
||
*/
|
||
setDirty() {
|
||
this._isDirty = true;
|
||
}
|
||
/**
|
||
* 添加实体处理器
|
||
* @param processor 要添加的处理器
|
||
*/
|
||
add(processor) {
|
||
this._processors.push(processor);
|
||
this.setDirty();
|
||
}
|
||
/**
|
||
* 移除实体处理器
|
||
* @param processor 要移除的处理器
|
||
*/
|
||
remove(processor) {
|
||
const index = this._processors.indexOf(processor);
|
||
if (index !== -1) {
|
||
this._processors.splice(index, 1);
|
||
}
|
||
}
|
||
/**
|
||
* 获取指定类型的处理器
|
||
* @param type 处理器类型
|
||
*/
|
||
getProcessor(type) {
|
||
for (const processor of this._processors) {
|
||
if (processor instanceof type) {
|
||
return processor;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
/**
|
||
* 开始处理
|
||
*
|
||
* 对所有处理器进行排序以确保正确的执行顺序。
|
||
*/
|
||
begin() {
|
||
this.sortProcessors();
|
||
}
|
||
/**
|
||
* 结束处理
|
||
*/
|
||
end() {
|
||
for (const processor of this._processors) {
|
||
try {
|
||
processor.reset();
|
||
} catch (error) {
|
||
EntityProcessorList._logger.error(`Error in processor ${getSystemInstanceTypeName(processor)}:`, error);
|
||
}
|
||
}
|
||
this._isDirty = false;
|
||
this._processors.length = 0;
|
||
}
|
||
/**
|
||
* 更新所有处理器
|
||
*/
|
||
update() {
|
||
this.sortProcessors();
|
||
for (const processor of this._processors) {
|
||
try {
|
||
processor.update();
|
||
} catch (error) {
|
||
EntityProcessorList._logger.error(`Error in processor ${getSystemInstanceTypeName(processor)}:`, error);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 后期更新所有处理器
|
||
*/
|
||
lateUpdate() {
|
||
for (const processor of this._processors) {
|
||
processor.lateUpdate();
|
||
}
|
||
}
|
||
/**
|
||
* 排序处理器
|
||
*/
|
||
sortProcessors() {
|
||
if (this._isDirty) {
|
||
this._processors.sort((a, b) => a.updateOrder - b.updateOrder);
|
||
this._isDirty = false;
|
||
}
|
||
}
|
||
/** 获取处理器列表 */
|
||
get processors() {
|
||
return this._processors;
|
||
}
|
||
/** 获取处理器数量 */
|
||
get count() {
|
||
return this._processors.length;
|
||
}
|
||
}
|
||
EntityProcessorList._logger = createLogger("EntityProcessorList");
|
||
class IdentifierPool {
|
||
/**
|
||
* 构造函数
|
||
*
|
||
* @param recycleDelay 延迟回收时间(毫秒),默认为100ms
|
||
* @param expansionBlockSize 内存扩展块大小,默认为1024
|
||
*/
|
||
constructor(recycleDelay = 100, expansionBlockSize = 1024) {
|
||
this._nextAvailableIndex = 0;
|
||
this._freeIndices = [];
|
||
this._generations = /* @__PURE__ */ new Map();
|
||
this._pendingRecycle = [];
|
||
this._recycleDelay = 100;
|
||
this._stats = {
|
||
totalAllocated: 0,
|
||
totalRecycled: 0,
|
||
currentActive: 0,
|
||
memoryExpansions: 0
|
||
};
|
||
this._recycleDelay = recycleDelay;
|
||
this._expansionBlockSize = expansionBlockSize;
|
||
this._preAllocateGenerations(0, this._expansionBlockSize);
|
||
}
|
||
/**
|
||
* 获取一个可用的ID
|
||
*
|
||
* 返回一个32位ID,高16位为世代版本,低16位为索引。
|
||
*
|
||
* @returns 新分配的实体ID
|
||
* @throws {Error} 当达到索引限制时抛出错误
|
||
*/
|
||
checkOut() {
|
||
this._processDelayedRecycle();
|
||
let index;
|
||
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是否有效且未被重复回收)
|
||
*/
|
||
checkIn(id) {
|
||
const index = this._unpackIndex(id);
|
||
const generation = this._unpackGeneration(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是否有效
|
||
*/
|
||
isValid(id) {
|
||
const index = this._unpackIndex(id);
|
||
const generation = this._unpackGeneration(id);
|
||
return this._isValidId(index, generation);
|
||
}
|
||
/**
|
||
* 获取统计信息
|
||
*
|
||
* @returns 池的当前状态统计
|
||
*/
|
||
getStats() {
|
||
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
|
||
};
|
||
}
|
||
/**
|
||
* 强制执行延迟回收处理
|
||
*
|
||
* 在某些情况下可能需要立即处理延迟回收队列,
|
||
* 比如内存压力大或者需要精确的统计信息时。
|
||
*/
|
||
forceProcessDelayedRecycle() {
|
||
this._processDelayedRecycle(true);
|
||
}
|
||
/**
|
||
* 清理过期的延迟回收项
|
||
*
|
||
* 将超过延迟时间的回收项真正回收到空闲列表中。
|
||
*
|
||
* @param forceAll 是否强制处理所有延迟回收项
|
||
* @private
|
||
*/
|
||
_processDelayedRecycle(forceAll = false) {
|
||
if (this._pendingRecycle.length === 0)
|
||
return;
|
||
const now = Date.now();
|
||
const readyToRecycle = [];
|
||
const stillPending = [];
|
||
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) {
|
||
if (this._isValidId(item.index, item.generation)) {
|
||
let newGeneration = item.generation + 1;
|
||
if (newGeneration > IdentifierPool.MAX_GENERATION) {
|
||
newGeneration = 1;
|
||
}
|
||
this._generations.set(item.index, newGeneration);
|
||
this._freeIndices.push(item.index);
|
||
}
|
||
}
|
||
this._pendingRecycle = stillPending;
|
||
}
|
||
/**
|
||
* 预分配世代信息
|
||
*
|
||
* @param startIndex 起始索引
|
||
* @param count 分配数量
|
||
* @private
|
||
*/
|
||
_preAllocateGenerations(startIndex, count) {
|
||
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
|
||
*/
|
||
_ensureGenerationCapacity(index) {
|
||
if (!this._generations.has(index)) {
|
||
const expansionStart = Math.floor(index / this._expansionBlockSize) * this._expansionBlockSize;
|
||
this._preAllocateGenerations(expansionStart, this._expansionBlockSize);
|
||
}
|
||
}
|
||
/**
|
||
* 计算内存使用量
|
||
*
|
||
* @returns 内存使用字节数
|
||
* @private
|
||
*/
|
||
_calculateMemoryUsage() {
|
||
const generationMapSize = this._generations.size * 16;
|
||
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
|
||
*/
|
||
_packId(index, generation) {
|
||
return generation << 16 | index;
|
||
}
|
||
/**
|
||
* 从ID中解包索引
|
||
*
|
||
* @param id 32位ID
|
||
* @returns 索引部分(16位)
|
||
* @private
|
||
*/
|
||
_unpackIndex(id) {
|
||
return id & 65535;
|
||
}
|
||
/**
|
||
* 从ID中解包世代版本
|
||
*
|
||
* @param id 32位ID
|
||
* @returns 世代版本部分(16位)
|
||
* @private
|
||
*/
|
||
_unpackGeneration(id) {
|
||
return id >>> 16 & 65535;
|
||
}
|
||
/**
|
||
* 内部ID有效性检查
|
||
*
|
||
* @param index 索引
|
||
* @param generation 世代版本
|
||
* @returns 是否有效
|
||
* @private
|
||
*/
|
||
_isValidId(index, generation) {
|
||
if (index < 0 || index >= this._nextAvailableIndex) {
|
||
return false;
|
||
}
|
||
const currentGeneration = this._generations.get(index);
|
||
return currentGeneration !== void 0 && currentGeneration === generation;
|
||
}
|
||
}
|
||
IdentifierPool.MAX_INDEX = 65535;
|
||
IdentifierPool.MAX_GENERATION = 65535;
|
||
class SparseSet {
|
||
constructor() {
|
||
this._dense = [];
|
||
this._sparse = /* @__PURE__ */ new Map();
|
||
}
|
||
/**
|
||
* 添加元素到集合
|
||
*
|
||
* @param item 要添加的元素
|
||
* @returns 是否成功添加(false表示元素已存在)
|
||
*/
|
||
add(item) {
|
||
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表示元素不存在)
|
||
*/
|
||
remove(item) {
|
||
const index = this._sparse.get(item);
|
||
if (index === void 0) {
|
||
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 元素是否存在
|
||
*/
|
||
has(item) {
|
||
return this._sparse.has(item);
|
||
}
|
||
/**
|
||
* 获取元素在密集数组中的索引
|
||
*
|
||
* @param item 要查询的元素
|
||
* @returns 索引,如果元素不存在则返回undefined
|
||
*/
|
||
getIndex(item) {
|
||
return this._sparse.get(item);
|
||
}
|
||
/**
|
||
* 根据索引获取元素
|
||
*
|
||
* @param index 索引
|
||
* @returns 元素,如果索引无效则返回undefined
|
||
*/
|
||
getByIndex(index) {
|
||
return this._dense[index];
|
||
}
|
||
/**
|
||
* 获取集合大小
|
||
*/
|
||
get size() {
|
||
return this._dense.length;
|
||
}
|
||
/**
|
||
* 检查集合是否为空
|
||
*/
|
||
get isEmpty() {
|
||
return this._dense.length === 0;
|
||
}
|
||
/**
|
||
* 遍历集合中的所有元素
|
||
*
|
||
* 保证遍历顺序与添加顺序一致(除非中间有删除操作)。
|
||
* 遍历性能优秀,因为数据在内存中连续存储。
|
||
*
|
||
* @param callback 遍历回调函数
|
||
*/
|
||
forEach(callback) {
|
||
for (let i = 0; i < this._dense.length; i++) {
|
||
callback(this._dense[i], i);
|
||
}
|
||
}
|
||
/**
|
||
* 映射集合中的所有元素
|
||
*
|
||
* @param callback 映射回调函数
|
||
* @returns 映射后的新数组
|
||
*/
|
||
map(callback) {
|
||
const result = [];
|
||
for (let i = 0; i < this._dense.length; i++) {
|
||
result.push(callback(this._dense[i], i));
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 过滤集合中的元素
|
||
*
|
||
* @param predicate 过滤条件
|
||
* @returns 满足条件的元素数组
|
||
*/
|
||
filter(predicate) {
|
||
const result = [];
|
||
for (let i = 0; i < this._dense.length; i++) {
|
||
if (predicate(this._dense[i], i)) {
|
||
result.push(this._dense[i]);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 查找第一个满足条件的元素
|
||
*
|
||
* @param predicate 查找条件
|
||
* @returns 找到的元素,如果没有则返回undefined
|
||
*/
|
||
find(predicate) {
|
||
for (let i = 0; i < this._dense.length; i++) {
|
||
if (predicate(this._dense[i], i)) {
|
||
return this._dense[i];
|
||
}
|
||
}
|
||
return void 0;
|
||
}
|
||
/**
|
||
* 检查是否存在满足条件的元素
|
||
*
|
||
* @param predicate 检查条件
|
||
* @returns 是否存在满足条件的元素
|
||
*/
|
||
some(predicate) {
|
||
for (let i = 0; i < this._dense.length; i++) {
|
||
if (predicate(this._dense[i], i)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
/**
|
||
* 检查是否所有元素都满足条件
|
||
*
|
||
* @param predicate 检查条件
|
||
* @returns 是否所有元素都满足条件
|
||
*/
|
||
every(predicate) {
|
||
for (let i = 0; i < this._dense.length; i++) {
|
||
if (!predicate(this._dense[i], i)) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
/**
|
||
* 获取密集数组的只读副本
|
||
*
|
||
* 返回数组的浅拷贝,确保外部无法直接修改内部数据。
|
||
*/
|
||
getDenseArray() {
|
||
return [...this._dense];
|
||
}
|
||
/**
|
||
* 获取密集数组的直接引用(内部使用)
|
||
*
|
||
* 警告:直接修改返回的数组会破坏数据结构的完整性。
|
||
* 仅在性能关键场景下使用,并确保不会修改数组内容。
|
||
*/
|
||
getDenseArrayUnsafe() {
|
||
return this._dense;
|
||
}
|
||
/**
|
||
* 清空集合
|
||
*/
|
||
clear() {
|
||
this._dense.length = 0;
|
||
this._sparse.clear();
|
||
}
|
||
/**
|
||
* 转换为数组
|
||
*/
|
||
toArray() {
|
||
return [...this._dense];
|
||
}
|
||
/**
|
||
* 转换为Set
|
||
*/
|
||
toSet() {
|
||
return new Set(this._dense);
|
||
}
|
||
/**
|
||
* 获取内存使用统计信息
|
||
*/
|
||
getMemoryStats() {
|
||
const denseArraySize = this._dense.length * 8;
|
||
const sparseMapSize = this._sparse.size * 16;
|
||
return {
|
||
denseArraySize,
|
||
sparseMapSize,
|
||
totalMemory: denseArraySize + sparseMapSize
|
||
};
|
||
}
|
||
/**
|
||
* 验证数据结构的完整性
|
||
*
|
||
* 调试用方法,检查内部数据结构是否一致。
|
||
*/
|
||
validate() {
|
||
if (this._dense.length !== this._sparse.size) {
|
||
return false;
|
||
}
|
||
for (let i = 0; i < this._dense.length; i++) {
|
||
const item = this._dense[i];
|
||
const mappedIndex = this._sparse.get(item);
|
||
if (mappedIndex !== i) {
|
||
return false;
|
||
}
|
||
}
|
||
for (const [item, index] of this._sparse) {
|
||
if (index >= this._dense.length || this._dense[index] !== item) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
class PoolableEntitySet extends Set {
|
||
constructor(...args) {
|
||
super();
|
||
}
|
||
reset() {
|
||
this.clear();
|
||
}
|
||
}
|
||
class ComponentSparseSet {
|
||
constructor() {
|
||
this._componentMasks = [];
|
||
this._componentToEntities = /* @__PURE__ */ new Map();
|
||
this._entities = new SparseSet();
|
||
}
|
||
/**
|
||
* 添加实体到组件索引
|
||
*
|
||
* 分析实体的组件组成,生成位掩码,并更新所有相关索引。
|
||
*
|
||
* @param entity 要添加的实体
|
||
*/
|
||
addEntity(entity) {
|
||
if (this._entities.has(entity)) {
|
||
this.removeEntity(entity);
|
||
}
|
||
let componentMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||
const entityComponents = /* @__PURE__ */ new Set();
|
||
for (const component of entity.components) {
|
||
const componentType = component.constructor;
|
||
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 要移除的实体
|
||
*/
|
||
removeEntity(entity) {
|
||
const entityIndex = this._entities.getIndex(entity);
|
||
if (entityIndex === void 0) {
|
||
return;
|
||
}
|
||
const entityComponents = this.getEntityComponentTypes(entity);
|
||
this.updateComponentMappings(entity, entityComponents, false);
|
||
this._entities.remove(entity);
|
||
const lastIndex = this._componentMasks.length - 1;
|
||
if (entityIndex !== lastIndex) {
|
||
this._componentMasks[entityIndex] = this._componentMasks[lastIndex];
|
||
}
|
||
this._componentMasks.pop();
|
||
}
|
||
/**
|
||
* 查询包含指定组件的所有实体
|
||
*
|
||
* @param componentType 组件类型
|
||
* @returns 包含该组件的实体集合
|
||
*/
|
||
queryByComponent(componentType) {
|
||
const entities = this._componentToEntities.get(componentType);
|
||
return entities ? new Set(entities) : /* @__PURE__ */ new Set();
|
||
}
|
||
/**
|
||
* 多组件查询(AND操作)
|
||
*
|
||
* 查找同时包含所有指定组件的实体。
|
||
*
|
||
* @param componentTypes 组件类型数组
|
||
* @returns 满足条件的实体集合
|
||
*/
|
||
queryMultipleAnd(componentTypes) {
|
||
if (componentTypes.length === 0) {
|
||
return /* @__PURE__ */ new Set();
|
||
}
|
||
if (componentTypes.length === 1) {
|
||
return this.queryByComponent(componentTypes[0]);
|
||
}
|
||
let targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||
for (const componentType of componentTypes) {
|
||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||
return /* @__PURE__ */ new Set();
|
||
}
|
||
const bitMask = ComponentRegistry.getBitMask(componentType);
|
||
BitMask64Utils.orInPlace(targetMask, bitMask);
|
||
}
|
||
const result = ComponentSparseSet._entitySetPool.obtain();
|
||
this._entities.forEach((entity, index) => {
|
||
const entityMask = this._componentMasks[index];
|
||
if (BitMask64Utils.hasAll(entityMask, targetMask)) {
|
||
result.add(entity);
|
||
}
|
||
});
|
||
return result;
|
||
}
|
||
/**
|
||
* 多组件查询(OR操作)
|
||
*
|
||
* 查找包含任意一个指定组件的实体。
|
||
*
|
||
* @param componentTypes 组件类型数组
|
||
* @returns 满足条件的实体集合
|
||
*/
|
||
queryMultipleOr(componentTypes) {
|
||
if (componentTypes.length === 0) {
|
||
return /* @__PURE__ */ new Set();
|
||
}
|
||
if (componentTypes.length === 1) {
|
||
return this.queryByComponent(componentTypes[0]);
|
||
}
|
||
let 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 /* @__PURE__ */ new Set();
|
||
}
|
||
const result = ComponentSparseSet._entitySetPool.obtain();
|
||
this._entities.forEach((entity, index) => {
|
||
const entityMask = this._componentMasks[index];
|
||
if (BitMask64Utils.hasAny(entityMask, targetMask)) {
|
||
result.add(entity);
|
||
}
|
||
});
|
||
return result;
|
||
}
|
||
/**
|
||
* 检查实体是否包含指定组件
|
||
*
|
||
* @param entity 实体
|
||
* @param componentType 组件类型
|
||
* @returns 是否包含该组件
|
||
*/
|
||
hasComponent(entity, componentType) {
|
||
const entityIndex = this._entities.getIndex(entity);
|
||
if (entityIndex === void 0) {
|
||
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
|
||
*/
|
||
getEntityMask(entity) {
|
||
const entityIndex = this._entities.getIndex(entity);
|
||
if (entityIndex === void 0) {
|
||
return void 0;
|
||
}
|
||
return this._componentMasks[entityIndex];
|
||
}
|
||
/**
|
||
* 获取所有实体
|
||
*
|
||
* @returns 所有实体的数组
|
||
*/
|
||
getAllEntities() {
|
||
return this._entities.toArray();
|
||
}
|
||
/**
|
||
* 获取实体数量
|
||
*/
|
||
get size() {
|
||
return this._entities.size;
|
||
}
|
||
/**
|
||
* 检查是否为空
|
||
*/
|
||
get isEmpty() {
|
||
return this._entities.isEmpty;
|
||
}
|
||
/**
|
||
* 遍历所有实体
|
||
*
|
||
* @param callback 遍历回调函数
|
||
*/
|
||
forEach(callback) {
|
||
this._entities.forEach((entity, index) => {
|
||
callback(entity, this._componentMasks[index], index);
|
||
});
|
||
}
|
||
/**
|
||
* 清空所有数据
|
||
*/
|
||
clear() {
|
||
this._entities.clear();
|
||
this._componentMasks.length = 0;
|
||
for (const entitySet of this._componentToEntities.values()) {
|
||
ComponentSparseSet._entitySetPool.release(entitySet);
|
||
}
|
||
this._componentToEntities.clear();
|
||
}
|
||
/**
|
||
* 获取内存使用统计
|
||
*/
|
||
getMemoryStats() {
|
||
const entitiesStats = this._entities.getMemoryStats();
|
||
const masksMemory = this._componentMasks.length * 16;
|
||
let mappingsMemory = this._componentToEntities.size * 16;
|
||
for (const entitySet of this._componentToEntities.values()) {
|
||
mappingsMemory += entitySet.size * 8;
|
||
}
|
||
return {
|
||
entitiesMemory: entitiesStats.totalMemory,
|
||
masksMemory,
|
||
mappingsMemory,
|
||
totalMemory: entitiesStats.totalMemory + masksMemory + mappingsMemory
|
||
};
|
||
}
|
||
/**
|
||
* 验证数据结构完整性
|
||
*/
|
||
validate() {
|
||
if (!this._entities.validate()) {
|
||
return false;
|
||
}
|
||
if (this._componentMasks.length !== this._entities.size) {
|
||
return false;
|
||
}
|
||
const allMappedEntities = /* @__PURE__ */ new Set();
|
||
for (const entitySet of this._componentToEntities.values()) {
|
||
for (const entity of entitySet) {
|
||
allMappedEntities.add(entity);
|
||
}
|
||
}
|
||
for (const entity of allMappedEntities) {
|
||
if (!this._entities.has(entity)) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
/**
|
||
* 获取实体的组件类型集合
|
||
*/
|
||
getEntityComponentTypes(entity) {
|
||
const componentTypes = /* @__PURE__ */ new Set();
|
||
for (const component of entity.components) {
|
||
componentTypes.add(component.constructor);
|
||
}
|
||
return componentTypes;
|
||
}
|
||
/**
|
||
* 更新组件类型到实体的映射
|
||
*/
|
||
updateComponentMappings(entity, componentTypes, add) {
|
||
for (const componentType of componentTypes) {
|
||
let entities = this._componentToEntities.get(componentType);
|
||
if (add) {
|
||
if (!entities) {
|
||
entities = ComponentSparseSet._entitySetPool.obtain();
|
||
this._componentToEntities.set(componentType, entities);
|
||
}
|
||
entities.add(entity);
|
||
} else {
|
||
if (entities) {
|
||
entities.delete(entity);
|
||
if (entities.size === 0) {
|
||
this._componentToEntities.delete(componentType);
|
||
ComponentSparseSet._entitySetPool.release(entities);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
ComponentSparseSet._entitySetPool = Pool.getPool(PoolableEntitySet, 50, 512);
|
||
class ComponentIndex {
|
||
constructor() {
|
||
this._queryCount = 0;
|
||
this._totalQueryTime = 0;
|
||
this._lastUpdated = Date.now();
|
||
this._sparseSet = new ComponentSparseSet();
|
||
}
|
||
addEntity(entity) {
|
||
this._sparseSet.addEntity(entity);
|
||
this._lastUpdated = Date.now();
|
||
}
|
||
removeEntity(entity) {
|
||
this._sparseSet.removeEntity(entity);
|
||
this._lastUpdated = Date.now();
|
||
}
|
||
query(componentType) {
|
||
const startTime = performance.now();
|
||
const result = this._sparseSet.queryByComponent(componentType);
|
||
this._queryCount++;
|
||
this._totalQueryTime += performance.now() - startTime;
|
||
return result;
|
||
}
|
||
queryMultiple(componentTypes, operation) {
|
||
const startTime = performance.now();
|
||
let result;
|
||
if (componentTypes.length === 0) {
|
||
result = /* @__PURE__ */ new Set();
|
||
} else if (componentTypes.length === 1) {
|
||
result = this.query(componentTypes[0]);
|
||
} else if (operation === "AND") {
|
||
result = this._sparseSet.queryMultipleAnd(componentTypes);
|
||
} else {
|
||
result = this._sparseSet.queryMultipleOr(componentTypes);
|
||
}
|
||
this._queryCount++;
|
||
this._totalQueryTime += performance.now() - startTime;
|
||
return result;
|
||
}
|
||
clear() {
|
||
this._sparseSet.clear();
|
||
this._lastUpdated = Date.now();
|
||
}
|
||
getStats() {
|
||
const memoryStats = this._sparseSet.getMemoryStats();
|
||
return {
|
||
size: this._sparseSet.size,
|
||
memoryUsage: memoryStats.totalMemory,
|
||
queryCount: this._queryCount,
|
||
avgQueryTime: this._queryCount > 0 ? this._totalQueryTime / this._queryCount : 0,
|
||
lastUpdated: this._lastUpdated
|
||
};
|
||
}
|
||
}
|
||
class ComponentIndexManager {
|
||
constructor() {
|
||
this._index = new ComponentIndex();
|
||
}
|
||
/**
|
||
* 添加实体到索引
|
||
*/
|
||
addEntity(entity) {
|
||
this._index.addEntity(entity);
|
||
}
|
||
/**
|
||
* 从索引中移除实体
|
||
*/
|
||
removeEntity(entity) {
|
||
this._index.removeEntity(entity);
|
||
}
|
||
/**
|
||
* 查询包含指定组件的实体
|
||
*/
|
||
query(componentType) {
|
||
return this._index.query(componentType);
|
||
}
|
||
/**
|
||
* 批量查询多个组件
|
||
*/
|
||
queryMultiple(componentTypes, operation) {
|
||
return this._index.queryMultiple(componentTypes, operation);
|
||
}
|
||
/**
|
||
* 获取索引统计信息
|
||
*/
|
||
getStats() {
|
||
return this._index.getStats();
|
||
}
|
||
/**
|
||
* 清空索引
|
||
*/
|
||
clear() {
|
||
this._index.clear();
|
||
}
|
||
}
|
||
class ArchetypeSystem {
|
||
constructor() {
|
||
this._archetypes = /* @__PURE__ */ new Map();
|
||
this._entityToArchetype = /* @__PURE__ */ new Map();
|
||
this._componentToArchetypes = /* @__PURE__ */ new Map();
|
||
this._queryCache = /* @__PURE__ */ new Map();
|
||
this._cacheTimeout = 5e3;
|
||
this._maxCacheSize = 100;
|
||
}
|
||
/**
|
||
* 添加实体到原型系统
|
||
*/
|
||
addEntity(entity) {
|
||
const componentTypes = this.getEntityComponentTypes(entity);
|
||
const archetypeId = this.generateArchetypeId(componentTypes);
|
||
let archetype = this._archetypes.get(archetypeId);
|
||
if (!archetype) {
|
||
archetype = this.createArchetype(componentTypes);
|
||
}
|
||
archetype.entities.push(entity);
|
||
archetype.updatedAt = Date.now();
|
||
this._entityToArchetype.set(entity, archetype);
|
||
this.updateComponentIndexes(archetype, componentTypes, true);
|
||
this.invalidateQueryCache();
|
||
}
|
||
/**
|
||
* 从原型系统中移除实体
|
||
*/
|
||
removeEntity(entity) {
|
||
const archetype = this._entityToArchetype.get(entity);
|
||
if (!archetype)
|
||
return;
|
||
const index = archetype.entities.indexOf(entity);
|
||
if (index !== -1) {
|
||
archetype.entities.splice(index, 1);
|
||
archetype.updatedAt = Date.now();
|
||
}
|
||
this._entityToArchetype.delete(entity);
|
||
this.invalidateQueryCache();
|
||
}
|
||
/**
|
||
* 更新实体的原型归属
|
||
*
|
||
* 当实体的组件组合发生变化时调用此方法,将实体从旧原型移动到新原型。
|
||
* 如果新的组件组合对应的原型不存在,将自动创建新原型。
|
||
*
|
||
* @param entity 要更新的实体
|
||
*/
|
||
updateEntity(entity) {
|
||
const currentArchetype = this._entityToArchetype.get(entity);
|
||
const newComponentTypes = this.getEntityComponentTypes(entity);
|
||
const newArchetypeId = this.generateArchetypeId(newComponentTypes);
|
||
if (currentArchetype && currentArchetype.id === newArchetypeId) {
|
||
return;
|
||
}
|
||
if (currentArchetype) {
|
||
const index = currentArchetype.entities.indexOf(entity);
|
||
if (index !== -1) {
|
||
currentArchetype.entities.splice(index, 1);
|
||
currentArchetype.updatedAt = Date.now();
|
||
}
|
||
}
|
||
let newArchetype = this._archetypes.get(newArchetypeId);
|
||
if (!newArchetype) {
|
||
newArchetype = this.createArchetype(newComponentTypes);
|
||
}
|
||
newArchetype.entities.push(entity);
|
||
newArchetype.updatedAt = Date.now();
|
||
this._entityToArchetype.set(entity, newArchetype);
|
||
if (currentArchetype) {
|
||
this.updateComponentIndexes(currentArchetype, currentArchetype.componentTypes, false);
|
||
}
|
||
this.updateComponentIndexes(newArchetype, newComponentTypes, true);
|
||
this.invalidateQueryCache();
|
||
}
|
||
/**
|
||
* 查询包含指定组件组合的原型
|
||
*/
|
||
queryArchetypes(componentTypes, operation = "AND") {
|
||
const startTime = performance.now();
|
||
const cacheKey = `${operation}:${componentTypes.map((t) => getComponentTypeName(t)).sort().join(",")}`;
|
||
const cached = this._queryCache.get(cacheKey);
|
||
if (cached && Date.now() - cached.timestamp < this._cacheTimeout) {
|
||
return {
|
||
...cached.result,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: true
|
||
};
|
||
}
|
||
const matchingArchetypes = [];
|
||
let totalEntities = 0;
|
||
if (operation === "AND") {
|
||
for (const archetype of this._archetypes.values()) {
|
||
if (this.archetypeContainsAllComponents(archetype, componentTypes)) {
|
||
matchingArchetypes.push(archetype);
|
||
totalEntities += archetype.entities.length;
|
||
}
|
||
}
|
||
} else {
|
||
const foundArchetypes = /* @__PURE__ */ new Set();
|
||
for (const componentType of componentTypes) {
|
||
const archetypes = this._componentToArchetypes.get(componentType);
|
||
if (archetypes) {
|
||
for (const archetype of archetypes) {
|
||
foundArchetypes.add(archetype);
|
||
}
|
||
}
|
||
}
|
||
for (const archetype of foundArchetypes) {
|
||
matchingArchetypes.push(archetype);
|
||
totalEntities += archetype.entities.length;
|
||
}
|
||
}
|
||
const result = {
|
||
archetypes: matchingArchetypes,
|
||
totalEntities,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: false
|
||
};
|
||
this._queryCache.set(cacheKey, {
|
||
result,
|
||
timestamp: Date.now()
|
||
});
|
||
return result;
|
||
}
|
||
/**
|
||
* 获取实体所属的原型
|
||
*/
|
||
getEntityArchetype(entity) {
|
||
return this._entityToArchetype.get(entity);
|
||
}
|
||
/**
|
||
* 获取所有原型
|
||
*/
|
||
getAllArchetypes() {
|
||
return Array.from(this._archetypes.values());
|
||
}
|
||
/**
|
||
* 清空所有数据
|
||
*/
|
||
clear() {
|
||
this._archetypes.clear();
|
||
this._entityToArchetype.clear();
|
||
this._componentToArchetypes.clear();
|
||
this._queryCache.clear();
|
||
}
|
||
/**
|
||
* 获取实体的组件类型列表
|
||
*/
|
||
getEntityComponentTypes(entity) {
|
||
return entity.components.map((component) => component.constructor);
|
||
}
|
||
/**
|
||
* 生成原型ID
|
||
*/
|
||
generateArchetypeId(componentTypes) {
|
||
return componentTypes.map((type) => getComponentTypeName(type)).sort().join("|");
|
||
}
|
||
/**
|
||
* 创建新原型
|
||
*/
|
||
createArchetype(componentTypes) {
|
||
const id = this.generateArchetypeId(componentTypes);
|
||
const archetype = {
|
||
id,
|
||
componentTypes: [...componentTypes],
|
||
entities: [],
|
||
createdAt: Date.now(),
|
||
updatedAt: Date.now()
|
||
};
|
||
this._archetypes.set(id, archetype);
|
||
return archetype;
|
||
}
|
||
/**
|
||
* 检查原型是否包含所有指定组件
|
||
*/
|
||
archetypeContainsAllComponents(archetype, componentTypes) {
|
||
for (const componentType of componentTypes) {
|
||
if (!archetype.componentTypes.includes(componentType)) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
/**
|
||
* 更新组件索引
|
||
*/
|
||
updateComponentIndexes(archetype, componentTypes, add) {
|
||
for (const componentType of componentTypes) {
|
||
let archetypes = this._componentToArchetypes.get(componentType);
|
||
if (!archetypes) {
|
||
archetypes = /* @__PURE__ */ new Set();
|
||
this._componentToArchetypes.set(componentType, archetypes);
|
||
}
|
||
if (add) {
|
||
archetypes.add(archetype);
|
||
} else {
|
||
archetypes.delete(archetype);
|
||
if (archetypes.size === 0) {
|
||
this._componentToArchetypes.delete(componentType);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 使查询缓存失效
|
||
*/
|
||
invalidateQueryCache() {
|
||
this._queryCache.clear();
|
||
}
|
||
}
|
||
var QueryConditionType;
|
||
(function(QueryConditionType2) {
|
||
QueryConditionType2["ALL"] = "all";
|
||
QueryConditionType2["ANY"] = "any";
|
||
QueryConditionType2["NONE"] = "none";
|
||
})(QueryConditionType || (QueryConditionType = {}));
|
||
class QuerySystem {
|
||
constructor() {
|
||
this._logger = createLogger("QuerySystem");
|
||
this.entities = [];
|
||
this._version = 0;
|
||
this.queryCache = /* @__PURE__ */ new Map();
|
||
this.cacheMaxSize = 1e3;
|
||
this.cacheTimeout = 5e3;
|
||
this.componentNameCache = /* @__PURE__ */ new WeakMap();
|
||
this.cacheKeyCache = /* @__PURE__ */ new Map();
|
||
this.componentMaskCache = /* @__PURE__ */ new Map();
|
||
this.queryStats = {
|
||
totalQueries: 0,
|
||
cacheHits: 0,
|
||
indexHits: 0,
|
||
linearScans: 0,
|
||
archetypeHits: 0,
|
||
dirtyChecks: 0
|
||
};
|
||
this.entityIndex = {
|
||
byMask: /* @__PURE__ */ new Map(),
|
||
byComponentType: /* @__PURE__ */ new Map(),
|
||
byTag: /* @__PURE__ */ new Map(),
|
||
byName: /* @__PURE__ */ new Map()
|
||
};
|
||
this.componentIndexManager = new ComponentIndexManager();
|
||
this.archetypeSystem = new ArchetypeSystem();
|
||
}
|
||
/**
|
||
* 设置实体列表并重建索引
|
||
*
|
||
* 当实体集合发生大规模变化时调用此方法。
|
||
* 系统将重新构建所有索引以确保查询性能。
|
||
*
|
||
* @param entities 新的实体列表
|
||
*/
|
||
setEntities(entities) {
|
||
this.entities = entities;
|
||
this.clearQueryCache();
|
||
this.rebuildIndexes();
|
||
}
|
||
/**
|
||
* 添加单个实体到查询系统
|
||
*
|
||
* 将新实体添加到查询系统中,并自动更新相关索引。
|
||
* 为了提高批量添加性能,可以延迟缓存清理。
|
||
*
|
||
* @param entity 要添加的实体
|
||
* @param deferCacheClear 是否延迟缓存清理(用于批量操作)
|
||
*/
|
||
addEntity(entity, deferCacheClear = false) {
|
||
if (!this.entities.includes(entity)) {
|
||
this.entities.push(entity);
|
||
this.addEntityToIndexes(entity);
|
||
this.componentIndexManager.addEntity(entity);
|
||
this.archetypeSystem.addEntity(entity);
|
||
if (!deferCacheClear) {
|
||
this.clearQueryCache();
|
||
}
|
||
this._version++;
|
||
}
|
||
}
|
||
/**
|
||
* 批量添加实体
|
||
*
|
||
* 高效地批量添加多个实体,减少缓存清理次数。
|
||
* 使用Set来避免O(n)的重复检查。
|
||
*
|
||
* @param entities 要添加的实体列表
|
||
*/
|
||
addEntities(entities) {
|
||
if (entities.length === 0)
|
||
return;
|
||
const existingIds = new Set(this.entities.map((e) => e.id));
|
||
let addedCount = 0;
|
||
for (const entity of entities) {
|
||
if (!existingIds.has(entity.id)) {
|
||
this.entities.push(entity);
|
||
this.addEntityToIndexes(entity);
|
||
this.componentIndexManager.addEntity(entity);
|
||
this.archetypeSystem.addEntity(entity);
|
||
existingIds.add(entity.id);
|
||
addedCount++;
|
||
}
|
||
}
|
||
if (addedCount > 0) {
|
||
this.clearQueryCache();
|
||
}
|
||
}
|
||
/**
|
||
* 批量添加实体(无重复检查版本)
|
||
*
|
||
* 假设所有实体都是新的,跳过重复检查以获得最大性能。
|
||
* 仅在确保没有重复实体时使用。
|
||
*
|
||
* @param entities 要添加的实体列表
|
||
*/
|
||
addEntitiesUnchecked(entities) {
|
||
if (entities.length === 0)
|
||
return;
|
||
for (const entity of entities) {
|
||
this.entities.push(entity);
|
||
}
|
||
for (const entity of entities) {
|
||
this.addEntityToIndexes(entity);
|
||
this.componentIndexManager.addEntity(entity);
|
||
this.archetypeSystem.addEntity(entity);
|
||
}
|
||
this.clearQueryCache();
|
||
}
|
||
/**
|
||
* 从查询系统移除实体
|
||
*
|
||
* 从查询系统中移除指定实体,并清理相关索引。
|
||
*
|
||
* @param entity 要移除的实体
|
||
*/
|
||
removeEntity(entity) {
|
||
const index = this.entities.indexOf(entity);
|
||
if (index !== -1) {
|
||
this.entities.splice(index, 1);
|
||
this.removeEntityFromIndexes(entity);
|
||
this.componentIndexManager.removeEntity(entity);
|
||
this.archetypeSystem.removeEntity(entity);
|
||
this.clearQueryCache();
|
||
this._version++;
|
||
}
|
||
}
|
||
/**
|
||
* 更新实体在查询系统中的索引
|
||
*
|
||
* 当实体的组件组合发生变化时调用此方法,高效地更新实体在查询系统中的索引。
|
||
*
|
||
* @param entity 要更新的实体
|
||
*/
|
||
updateEntity(entity) {
|
||
if (!this.entities.includes(entity)) {
|
||
this.addEntity(entity);
|
||
return;
|
||
}
|
||
this.removeEntityFromIndexes(entity);
|
||
this.archetypeSystem.updateEntity(entity);
|
||
this.componentIndexManager.removeEntity(entity);
|
||
this.componentIndexManager.addEntity(entity);
|
||
this.addEntityToIndexes(entity);
|
||
this.clearQueryCache();
|
||
this._version++;
|
||
}
|
||
/**
|
||
* 将实体添加到各种索引中
|
||
*/
|
||
addEntityToIndexes(entity) {
|
||
const mask = entity.componentMask;
|
||
const maskKey = mask.toString();
|
||
const maskSet = this.entityIndex.byMask.get(maskKey) || this.createAndSetMaskIndex(maskKey);
|
||
maskSet.add(entity);
|
||
const components = entity.components;
|
||
for (let i = 0; i < components.length; i++) {
|
||
const componentType = components[i].constructor;
|
||
const typeSet = this.entityIndex.byComponentType.get(componentType) || this.createAndSetComponentIndex(componentType);
|
||
typeSet.add(entity);
|
||
}
|
||
const tag = entity.tag;
|
||
if (tag !== void 0) {
|
||
const tagSet = this.entityIndex.byTag.get(tag) || this.createAndSetTagIndex(tag);
|
||
tagSet.add(entity);
|
||
}
|
||
const name = entity.name;
|
||
if (name) {
|
||
const nameSet = this.entityIndex.byName.get(name) || this.createAndSetNameIndex(name);
|
||
nameSet.add(entity);
|
||
}
|
||
}
|
||
createAndSetMaskIndex(maskKey) {
|
||
const set = /* @__PURE__ */ new Set();
|
||
this.entityIndex.byMask.set(maskKey, set);
|
||
return set;
|
||
}
|
||
createAndSetComponentIndex(componentType) {
|
||
const set = /* @__PURE__ */ new Set();
|
||
this.entityIndex.byComponentType.set(componentType, set);
|
||
return set;
|
||
}
|
||
createAndSetTagIndex(tag) {
|
||
const set = /* @__PURE__ */ new Set();
|
||
this.entityIndex.byTag.set(tag, set);
|
||
return set;
|
||
}
|
||
createAndSetNameIndex(name) {
|
||
const set = /* @__PURE__ */ new Set();
|
||
this.entityIndex.byName.set(name, set);
|
||
return set;
|
||
}
|
||
/**
|
||
* 从各种索引中移除实体
|
||
*/
|
||
removeEntityFromIndexes(entity) {
|
||
const mask = entity.componentMask;
|
||
const maskKey = mask.toString();
|
||
const maskSet = this.entityIndex.byMask.get(maskKey);
|
||
if (maskSet) {
|
||
maskSet.delete(entity);
|
||
if (maskSet.size === 0) {
|
||
this.entityIndex.byMask.delete(maskKey);
|
||
}
|
||
}
|
||
for (const component of entity.components) {
|
||
const componentType = component.constructor;
|
||
const typeSet = this.entityIndex.byComponentType.get(componentType);
|
||
if (typeSet) {
|
||
typeSet.delete(entity);
|
||
if (typeSet.size === 0) {
|
||
this.entityIndex.byComponentType.delete(componentType);
|
||
}
|
||
}
|
||
}
|
||
if (entity.tag !== void 0) {
|
||
const tagSet = this.entityIndex.byTag.get(entity.tag);
|
||
if (tagSet) {
|
||
tagSet.delete(entity);
|
||
if (tagSet.size === 0) {
|
||
this.entityIndex.byTag.delete(entity.tag);
|
||
}
|
||
}
|
||
}
|
||
if (entity.name) {
|
||
const nameSet = this.entityIndex.byName.get(entity.name);
|
||
if (nameSet) {
|
||
nameSet.delete(entity);
|
||
if (nameSet.size === 0) {
|
||
this.entityIndex.byName.delete(entity.name);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 重建所有索引
|
||
*
|
||
* 清空并重新构建所有查询索引。
|
||
* 通常在大量实体变更后调用以确保索引一致性。
|
||
*/
|
||
rebuildIndexes() {
|
||
this.entityIndex.byMask.clear();
|
||
this.entityIndex.byComponentType.clear();
|
||
this.entityIndex.byTag.clear();
|
||
this.entityIndex.byName.clear();
|
||
this.archetypeSystem.clear();
|
||
this.componentIndexManager.clear();
|
||
for (const entity of this.entities) {
|
||
this.addEntityToIndexes(entity);
|
||
this.componentIndexManager.addEntity(entity);
|
||
this.archetypeSystem.addEntity(entity);
|
||
}
|
||
}
|
||
/**
|
||
* 查询包含所有指定组件的实体
|
||
*
|
||
* 返回同时包含所有指定组件类型的实体列表。
|
||
* 系统会自动选择最高效的查询策略,包括索引查找和缓存机制。
|
||
*
|
||
* @param componentTypes 要查询的组件类型列表
|
||
* @returns 查询结果,包含匹配的实体和性能信息
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 查询同时具有位置和速度组件的实体
|
||
* const result = querySystem.queryAll(PositionComponent, VelocityComponent);
|
||
* logger.info(`找到 ${result.count} 个移动实体`);
|
||
* ```
|
||
*/
|
||
queryAll(...componentTypes) {
|
||
const startTime = performance.now();
|
||
this.queryStats.totalQueries++;
|
||
const cacheKey = this.generateCacheKey("all", componentTypes);
|
||
const cached = this.getFromCache(cacheKey);
|
||
if (cached) {
|
||
this.queryStats.cacheHits++;
|
||
return {
|
||
entities: cached,
|
||
count: cached.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: true
|
||
};
|
||
}
|
||
let entities = [];
|
||
const archetypeResult = this.archetypeSystem.queryArchetypes(componentTypes, "AND");
|
||
if (archetypeResult.archetypes.length > 0) {
|
||
this.queryStats.archetypeHits++;
|
||
for (const archetype of archetypeResult.archetypes) {
|
||
entities.push(...archetype.entities);
|
||
}
|
||
} else {
|
||
try {
|
||
if (componentTypes.length === 1) {
|
||
this.queryStats.indexHits++;
|
||
const indexResult = this.componentIndexManager.query(componentTypes[0]);
|
||
entities = Array.from(indexResult);
|
||
} else {
|
||
const indexResult = this.componentIndexManager.queryMultiple(componentTypes, "AND");
|
||
entities = Array.from(indexResult);
|
||
}
|
||
} catch (error) {
|
||
entities = [];
|
||
}
|
||
}
|
||
this.addToCache(cacheKey, entities);
|
||
return {
|
||
entities,
|
||
count: entities.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: false
|
||
};
|
||
}
|
||
/**
|
||
* 多组件查询算法
|
||
*
|
||
* 针对多组件查询场景的高效算法实现。
|
||
* 通过选择最小的组件集合作为起点,减少需要检查的实体数量。
|
||
*
|
||
* @param componentTypes 组件类型列表
|
||
* @returns 匹配的实体列表
|
||
*/
|
||
queryMultipleComponents(componentTypes) {
|
||
let smallestSet = null;
|
||
let smallestSize = Infinity;
|
||
for (const componentType of componentTypes) {
|
||
const set = this.entityIndex.byComponentType.get(componentType);
|
||
if (!set || set.size === 0) {
|
||
return [];
|
||
}
|
||
if (set.size < smallestSize) {
|
||
smallestSize = set.size;
|
||
smallestSet = set;
|
||
}
|
||
}
|
||
if (!smallestSet) {
|
||
return [];
|
||
}
|
||
const mask = this.createComponentMask(componentTypes);
|
||
const result = [];
|
||
for (const entity of smallestSet) {
|
||
if (BitMask64Utils.hasAll(entity.componentMask, mask)) {
|
||
result.push(entity);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 查询包含任意指定组件的实体
|
||
*
|
||
* 返回包含任意一个指定组件类型的实体列表。
|
||
* 使用集合合并算法确保高效的查询性能。
|
||
*
|
||
* @param componentTypes 要查询的组件类型列表
|
||
* @returns 查询结果,包含匹配的实体和性能信息
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 查询具有武器或护甲组件的实体
|
||
* const result = querySystem.queryAny(WeaponComponent, ArmorComponent);
|
||
* logger.info(`找到 ${result.count} 个装备实体`);
|
||
* ```
|
||
*/
|
||
queryAny(...componentTypes) {
|
||
const startTime = performance.now();
|
||
this.queryStats.totalQueries++;
|
||
const cacheKey = this.generateCacheKey("any", componentTypes);
|
||
const cached = this.getFromCache(cacheKey);
|
||
if (cached) {
|
||
this.queryStats.cacheHits++;
|
||
return {
|
||
entities: cached,
|
||
count: cached.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: true
|
||
};
|
||
}
|
||
const archetypeResult = this.archetypeSystem.queryArchetypes(componentTypes, "OR");
|
||
let entities;
|
||
if (archetypeResult.archetypes.length > 0) {
|
||
this.queryStats.archetypeHits++;
|
||
entities = [];
|
||
for (const archetype of archetypeResult.archetypes) {
|
||
entities.push(...archetype.entities);
|
||
}
|
||
} else {
|
||
const indexResult = this.componentIndexManager.queryMultiple(componentTypes, "OR");
|
||
entities = Array.from(indexResult);
|
||
}
|
||
this.addToCache(cacheKey, entities);
|
||
return {
|
||
entities,
|
||
count: entities.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: false
|
||
};
|
||
}
|
||
/**
|
||
* 查询不包含任何指定组件的实体
|
||
*
|
||
* 返回不包含任何指定组件类型的实体列表。
|
||
* 适用于排除特定类型实体的查询场景。
|
||
*
|
||
* @param componentTypes 要排除的组件类型列表
|
||
* @returns 查询结果,包含匹配的实体和性能信息
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 查询不具有AI和玩家控制组件的实体(如静态物体)
|
||
* const result = querySystem.queryNone(AIComponent, PlayerControlComponent);
|
||
* logger.info(`找到 ${result.count} 个静态实体`);
|
||
* ```
|
||
*/
|
||
queryNone(...componentTypes) {
|
||
const startTime = performance.now();
|
||
this.queryStats.totalQueries++;
|
||
const cacheKey = this.generateCacheKey("none", componentTypes);
|
||
const cached = this.getFromCache(cacheKey);
|
||
if (cached) {
|
||
this.queryStats.cacheHits++;
|
||
return {
|
||
entities: cached,
|
||
count: cached.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: true
|
||
};
|
||
}
|
||
const mask = this.createComponentMask(componentTypes);
|
||
const entities = this.entities.filter((entity) => BitMask64Utils.hasNone(entity.componentMask, mask));
|
||
this.addToCache(cacheKey, entities);
|
||
return {
|
||
entities,
|
||
count: entities.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: false
|
||
};
|
||
}
|
||
/**
|
||
* 按标签查询实体
|
||
*
|
||
* 返回具有指定标签的所有实体。
|
||
* 标签查询使用专用索引,具有很高的查询性能。
|
||
*
|
||
* @param tag 要查询的标签值
|
||
* @returns 查询结果,包含匹配的实体和性能信息
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 查询所有玩家实体
|
||
* const players = querySystem.queryByTag(PLAYER_TAG);
|
||
* ```
|
||
*/
|
||
queryByTag(tag) {
|
||
const startTime = performance.now();
|
||
this.queryStats.totalQueries++;
|
||
const cacheKey = `tag:${tag}`;
|
||
const cached = this.getFromCache(cacheKey);
|
||
if (cached) {
|
||
this.queryStats.cacheHits++;
|
||
return {
|
||
entities: cached,
|
||
count: cached.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: true
|
||
};
|
||
}
|
||
this.queryStats.indexHits++;
|
||
const entities = Array.from(this.entityIndex.byTag.get(tag) || []);
|
||
this.addToCache(cacheKey, entities);
|
||
return {
|
||
entities,
|
||
count: entities.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: false
|
||
};
|
||
}
|
||
/**
|
||
* 按名称查询实体
|
||
*
|
||
* 返回具有指定名称的所有实体。
|
||
* 名称查询使用专用索引,适用于查找特定的命名实体。
|
||
*
|
||
* @param name 要查询的实体名称
|
||
* @returns 查询结果,包含匹配的实体和性能信息
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 查找名为"Player"的实体
|
||
* const player = querySystem.queryByName("Player");
|
||
* ```
|
||
*/
|
||
queryByName(name) {
|
||
const startTime = performance.now();
|
||
this.queryStats.totalQueries++;
|
||
const cacheKey = `name:${name}`;
|
||
const cached = this.getFromCache(cacheKey);
|
||
if (cached) {
|
||
this.queryStats.cacheHits++;
|
||
return {
|
||
entities: cached,
|
||
count: cached.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: true
|
||
};
|
||
}
|
||
this.queryStats.indexHits++;
|
||
const entities = Array.from(this.entityIndex.byName.get(name) || []);
|
||
this.addToCache(cacheKey, entities);
|
||
return {
|
||
entities,
|
||
count: entities.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: false
|
||
};
|
||
}
|
||
/**
|
||
* 按单个组件类型查询实体
|
||
*
|
||
* 返回包含指定组件类型的所有实体。
|
||
* 这是最基础的查询方法,具有最高的查询性能。
|
||
*
|
||
* @param componentType 要查询的组件类型
|
||
* @returns 查询结果,包含匹配的实体和性能信息
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 查询所有具有位置组件的实体
|
||
* const entitiesWithPosition = querySystem.queryByComponent(PositionComponent);
|
||
* ```
|
||
*/
|
||
queryByComponent(componentType) {
|
||
const startTime = performance.now();
|
||
this.queryStats.totalQueries++;
|
||
const cacheKey = this.generateCacheKey("component", [componentType]);
|
||
const cached = this.getFromCache(cacheKey);
|
||
if (cached) {
|
||
this.queryStats.cacheHits++;
|
||
return {
|
||
entities: cached,
|
||
count: cached.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: true
|
||
};
|
||
}
|
||
this.queryStats.indexHits++;
|
||
const entities = Array.from(this.entityIndex.byComponentType.get(componentType) || []);
|
||
this.addToCache(cacheKey, entities);
|
||
return {
|
||
entities,
|
||
count: entities.length,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: false
|
||
};
|
||
}
|
||
/**
|
||
* 从缓存获取查询结果
|
||
*/
|
||
getFromCache(cacheKey) {
|
||
const entry = this.queryCache.get(cacheKey);
|
||
if (!entry)
|
||
return null;
|
||
if (Date.now() - entry.timestamp > this.cacheTimeout || entry.version !== this._version) {
|
||
this.queryCache.delete(cacheKey);
|
||
return null;
|
||
}
|
||
entry.hitCount++;
|
||
return entry.entities;
|
||
}
|
||
/**
|
||
* 添加查询结果到缓存
|
||
*/
|
||
addToCache(cacheKey, entities) {
|
||
if (this.queryCache.size >= this.cacheMaxSize) {
|
||
this.cleanupCache();
|
||
}
|
||
this.queryCache.set(cacheKey, {
|
||
entities,
|
||
// 直接使用引用,通过版本号控制失效
|
||
timestamp: Date.now(),
|
||
hitCount: 0,
|
||
version: this._version
|
||
});
|
||
}
|
||
/**
|
||
* 清理缓存
|
||
*/
|
||
cleanupCache() {
|
||
const now = Date.now();
|
||
for (const [key, entry] of this.queryCache.entries()) {
|
||
if (now - entry.timestamp > this.cacheTimeout) {
|
||
this.queryCache.delete(key);
|
||
}
|
||
}
|
||
if (this.queryCache.size >= this.cacheMaxSize) {
|
||
let minHitCount = Infinity;
|
||
let oldestKey = "";
|
||
let oldestTimestamp = Infinity;
|
||
for (const [key, entry] of this.queryCache.entries()) {
|
||
if (entry.hitCount < minHitCount || entry.hitCount === minHitCount && entry.timestamp < oldestTimestamp) {
|
||
minHitCount = entry.hitCount;
|
||
oldestKey = key;
|
||
oldestTimestamp = entry.timestamp;
|
||
}
|
||
}
|
||
if (oldestKey) {
|
||
this.queryCache.delete(oldestKey);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 清除所有查询缓存
|
||
*/
|
||
clearQueryCache() {
|
||
this.queryCache.clear();
|
||
this.cacheKeyCache.clear();
|
||
this.componentMaskCache.clear();
|
||
}
|
||
/**
|
||
* 高效的缓存键生成
|
||
*/
|
||
generateCacheKey(prefix, componentTypes) {
|
||
if (componentTypes.length === 1) {
|
||
let name = this.componentNameCache.get(componentTypes[0]);
|
||
if (!name) {
|
||
name = getComponentTypeName(componentTypes[0]);
|
||
this.componentNameCache.set(componentTypes[0], name);
|
||
}
|
||
return `${prefix}:${name}`;
|
||
}
|
||
const sortKey = componentTypes.map((t) => {
|
||
let name = this.componentNameCache.get(t);
|
||
if (!name) {
|
||
name = getComponentTypeName(t);
|
||
this.componentNameCache.set(t, name);
|
||
}
|
||
return name;
|
||
}).sort().join(",");
|
||
const fullKey = `${prefix}:${sortKey}`;
|
||
let cachedKey = this.cacheKeyCache.get(fullKey);
|
||
if (!cachedKey) {
|
||
cachedKey = fullKey;
|
||
this.cacheKeyCache.set(fullKey, cachedKey);
|
||
}
|
||
return cachedKey;
|
||
}
|
||
/**
|
||
* 清理查询缓存
|
||
*
|
||
* 用于外部调用清理缓存,通常在批量操作后使用。
|
||
*/
|
||
clearCache() {
|
||
this.clearQueryCache();
|
||
}
|
||
/**
|
||
* 创建组件掩码
|
||
*
|
||
* 根据组件类型列表生成对应的位掩码。
|
||
* 使用缓存避免重复计算。
|
||
*
|
||
* @param componentTypes 组件类型列表
|
||
* @returns 生成的位掩码
|
||
*/
|
||
createComponentMask(componentTypes) {
|
||
const cacheKey = componentTypes.map((t) => {
|
||
let name = this.componentNameCache.get(t);
|
||
if (!name) {
|
||
name = getComponentTypeName(t);
|
||
this.componentNameCache.set(t, name);
|
||
}
|
||
return name;
|
||
}).sort().join(",");
|
||
const cached = this.componentMaskCache.get(cacheKey);
|
||
if (cached) {
|
||
return cached;
|
||
}
|
||
let mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||
let hasValidComponents = false;
|
||
for (const type of componentTypes) {
|
||
try {
|
||
const bitMask = ComponentRegistry.getBitMask(type);
|
||
BitMask64Utils.orInPlace(mask, bitMask);
|
||
hasValidComponents = true;
|
||
} catch (error) {
|
||
this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`);
|
||
}
|
||
}
|
||
if (!hasValidComponents) {
|
||
mask = { lo: 4294967295, hi: 4294967295 };
|
||
}
|
||
this.componentMaskCache.set(cacheKey, mask);
|
||
return mask;
|
||
}
|
||
/**
|
||
* 获取当前版本号(用于缓存失效)
|
||
*/
|
||
get version() {
|
||
return this._version;
|
||
}
|
||
/**
|
||
* 获取所有实体
|
||
*/
|
||
getAllEntities() {
|
||
return this.entities;
|
||
}
|
||
/**
|
||
* 获取系统统计信息
|
||
*
|
||
* 返回查询系统的详细统计信息,包括实体数量、索引状态、
|
||
* 查询性能统计等,用于性能监控和调试。
|
||
*
|
||
* @returns 系统统计信息对象
|
||
*/
|
||
getStats() {
|
||
return {
|
||
entityCount: this.entities.length,
|
||
indexStats: {
|
||
maskIndexSize: this.entityIndex.byMask.size,
|
||
componentIndexSize: this.entityIndex.byComponentType.size,
|
||
tagIndexSize: this.entityIndex.byTag.size,
|
||
nameIndexSize: this.entityIndex.byName.size
|
||
},
|
||
queryStats: {
|
||
...this.queryStats,
|
||
cacheHitRate: this.queryStats.totalQueries > 0 ? (this.queryStats.cacheHits / this.queryStats.totalQueries * 100).toFixed(2) + "%" : "0%"
|
||
},
|
||
optimizationStats: {
|
||
componentIndex: this.componentIndexManager.getStats(),
|
||
archetypeSystem: this.archetypeSystem.getAllArchetypes().map((a) => ({
|
||
id: a.id,
|
||
componentTypes: a.componentTypes.map((t) => getComponentTypeName(t)),
|
||
entityCount: a.entities.length
|
||
}))
|
||
},
|
||
cacheStats: {
|
||
size: this.queryCache.size,
|
||
hitRate: this.queryStats.totalQueries > 0 ? (this.queryStats.cacheHits / this.queryStats.totalQueries * 100).toFixed(2) + "%" : "0%"
|
||
}
|
||
};
|
||
}
|
||
/**
|
||
* 获取实体所属的原型信息
|
||
*
|
||
* @param entity 要查询的实体
|
||
*/
|
||
getEntityArchetype(entity) {
|
||
return this.archetypeSystem.getEntityArchetype(entity);
|
||
}
|
||
}
|
||
class QueryBuilder {
|
||
constructor(querySystem) {
|
||
this._logger = createLogger("QueryBuilder");
|
||
this.conditions = [];
|
||
this.querySystem = querySystem;
|
||
}
|
||
/**
|
||
* 添加"必须包含所有组件"条件
|
||
*
|
||
* @param componentTypes 必须包含的组件类型
|
||
* @returns 查询构建器实例,支持链式调用
|
||
*/
|
||
withAll(...componentTypes) {
|
||
this.conditions.push({
|
||
type: QueryConditionType.ALL,
|
||
componentTypes,
|
||
mask: this.createComponentMask(componentTypes)
|
||
});
|
||
return this;
|
||
}
|
||
/**
|
||
* 添加"必须包含任意组件"条件
|
||
*
|
||
* @param componentTypes 必须包含其中任意一个的组件类型
|
||
* @returns 查询构建器实例,支持链式调用
|
||
*/
|
||
withAny(...componentTypes) {
|
||
this.conditions.push({
|
||
type: QueryConditionType.ANY,
|
||
componentTypes,
|
||
mask: this.createComponentMask(componentTypes)
|
||
});
|
||
return this;
|
||
}
|
||
/**
|
||
* 添加"不能包含任何组件"条件
|
||
*
|
||
* @param componentTypes 不能包含的组件类型
|
||
* @returns 查询构建器实例,支持链式调用
|
||
*/
|
||
without(...componentTypes) {
|
||
this.conditions.push({
|
||
type: QueryConditionType.NONE,
|
||
componentTypes,
|
||
mask: this.createComponentMask(componentTypes)
|
||
});
|
||
return this;
|
||
}
|
||
/**
|
||
* 执行查询并返回结果
|
||
*
|
||
* 根据已添加的查询条件执行实体查询。
|
||
*
|
||
* @returns 查询结果,包含匹配的实体和性能信息
|
||
*/
|
||
execute() {
|
||
const startTime = performance.now();
|
||
if (this.conditions.length === 1) {
|
||
const condition = this.conditions[0];
|
||
switch (condition.type) {
|
||
case QueryConditionType.ALL:
|
||
return this.querySystem.queryAll(...condition.componentTypes);
|
||
case QueryConditionType.ANY:
|
||
return this.querySystem.queryAny(...condition.componentTypes);
|
||
case QueryConditionType.NONE:
|
||
return this.querySystem.queryNone(...condition.componentTypes);
|
||
}
|
||
}
|
||
return {
|
||
entities: [],
|
||
count: 0,
|
||
executionTime: performance.now() - startTime,
|
||
fromCache: false
|
||
};
|
||
}
|
||
/**
|
||
* 创建组件掩码
|
||
*/
|
||
createComponentMask(componentTypes) {
|
||
let mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||
for (const type of componentTypes) {
|
||
try {
|
||
const bitMask = ComponentRegistry.getBitMask(type);
|
||
BitMask64Utils.orInPlace(mask, bitMask);
|
||
} catch (error) {
|
||
this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`);
|
||
}
|
||
}
|
||
return mask;
|
||
}
|
||
/**
|
||
* 重置查询构建器
|
||
*
|
||
* 清除所有已添加的查询条件,重新开始构建查询。
|
||
*
|
||
* @returns 查询构建器实例,支持链式调用
|
||
*/
|
||
reset() {
|
||
this.conditions = [];
|
||
return this;
|
||
}
|
||
}
|
||
class TypeSafeEventSystem {
|
||
constructor() {
|
||
this.listeners = /* @__PURE__ */ new Map();
|
||
this.stats = /* @__PURE__ */ new Map();
|
||
this.batchQueue = /* @__PURE__ */ new Map();
|
||
this.batchTimers = /* @__PURE__ */ new Map();
|
||
this.batchConfigs = /* @__PURE__ */ new Map();
|
||
this.nextListenerId = 0;
|
||
this.isEnabled = true;
|
||
this.maxListeners = 100;
|
||
}
|
||
/**
|
||
* 添加事件监听器
|
||
* @param eventType 事件类型
|
||
* @param handler 事件处理器
|
||
* @param config 监听器配置
|
||
* @returns 监听器ID(用于移除)
|
||
*/
|
||
on(eventType, handler, config = {}) {
|
||
return this.addListener(eventType, handler, config);
|
||
}
|
||
/**
|
||
* 添加一次性事件监听器
|
||
* @param eventType 事件类型
|
||
* @param handler 事件处理器
|
||
* @param config 监听器配置
|
||
* @returns 监听器ID
|
||
*/
|
||
once(eventType, handler, config = {}) {
|
||
return this.addListener(eventType, handler, { ...config, once: true });
|
||
}
|
||
/**
|
||
* 添加异步事件监听器
|
||
* @param eventType 事件类型
|
||
* @param handler 异步事件处理器
|
||
* @param config 监听器配置
|
||
* @returns 监听器ID
|
||
*/
|
||
onAsync(eventType, handler, config = {}) {
|
||
return this.addListener(eventType, handler, { ...config, async: true });
|
||
}
|
||
/**
|
||
* 移除事件监听器
|
||
* @param eventType 事件类型
|
||
* @param listenerId 监听器ID
|
||
* @returns 是否成功移除
|
||
*/
|
||
off(eventType, listenerId) {
|
||
const listeners = this.listeners.get(eventType);
|
||
if (!listeners)
|
||
return false;
|
||
const index = listeners.findIndex((l) => l.id === listenerId);
|
||
if (index === -1)
|
||
return false;
|
||
listeners.splice(index, 1);
|
||
if (listeners.length === 0) {
|
||
this.listeners.delete(eventType);
|
||
this.stats.delete(eventType);
|
||
}
|
||
return true;
|
||
}
|
||
/**
|
||
* 移除指定事件类型的所有监听器
|
||
* @param eventType 事件类型
|
||
*/
|
||
offAll(eventType) {
|
||
this.listeners.delete(eventType);
|
||
this.stats.delete(eventType);
|
||
this.clearBatch(eventType);
|
||
}
|
||
/**
|
||
* 触发事件
|
||
* @param eventType 事件类型
|
||
* @param event 事件数据
|
||
* @returns Promise(如果有异步监听器)
|
||
*/
|
||
async emit(eventType, event) {
|
||
if (!this.isEnabled)
|
||
return;
|
||
const batchConfig = this.batchConfigs.get(eventType);
|
||
if (batchConfig?.enabled) {
|
||
this.addToBatch(eventType, event);
|
||
return;
|
||
}
|
||
await this.executeEvent(eventType, event);
|
||
}
|
||
/**
|
||
* 同步触发事件(忽略异步监听器)
|
||
* @param eventType 事件类型
|
||
* @param event 事件数据
|
||
*/
|
||
emitSync(eventType, event) {
|
||
if (!this.isEnabled)
|
||
return;
|
||
const listeners = this.listeners.get(eventType);
|
||
if (!listeners || listeners.length === 0)
|
||
return;
|
||
const startTime = performance.now();
|
||
const toRemove = [];
|
||
const sortedListeners = this.sortListenersByPriority(listeners);
|
||
for (const listener of sortedListeners) {
|
||
if (listener.config.async)
|
||
continue;
|
||
try {
|
||
if (listener.config.context) {
|
||
listener.handler.call(listener.config.context, event);
|
||
} else {
|
||
listener.handler(event);
|
||
}
|
||
if (listener.config.once) {
|
||
toRemove.push(listener.id);
|
||
}
|
||
} catch (error) {
|
||
TypeSafeEventSystem._logger.error(`事件处理器执行错误 ${eventType}:`, error);
|
||
}
|
||
}
|
||
this.removeListeners(eventType, toRemove);
|
||
this.updateStats(eventType, performance.now() - startTime);
|
||
}
|
||
/**
|
||
* 设置事件批处理配置
|
||
* @param eventType 事件类型
|
||
* @param config 批处理配置
|
||
*/
|
||
setBatchConfig(eventType, config) {
|
||
this.batchConfigs.set(eventType, config);
|
||
}
|
||
/**
|
||
* 立即处理指定事件类型的批处理队列
|
||
* @param eventType 事件类型
|
||
*/
|
||
flushBatch(eventType) {
|
||
const batch = this.batchQueue.get(eventType);
|
||
if (!batch || batch.length === 0)
|
||
return;
|
||
const timer = this.batchTimers.get(eventType);
|
||
if (timer) {
|
||
clearTimeout(timer);
|
||
this.batchTimers.delete(eventType);
|
||
}
|
||
this.processBatch(eventType, batch);
|
||
this.batchQueue.delete(eventType);
|
||
}
|
||
/**
|
||
* 获取事件统计信息
|
||
* @param eventType 事件类型(可选)
|
||
* @returns 统计信息
|
||
*/
|
||
getStats(eventType) {
|
||
if (eventType) {
|
||
return this.stats.get(eventType) || this.createEmptyStats(eventType);
|
||
}
|
||
return new Map(this.stats);
|
||
}
|
||
/**
|
||
* 重置统计信息
|
||
* @param eventType 事件类型(可选,不指定则重置所有)
|
||
*/
|
||
resetStats(eventType) {
|
||
if (eventType) {
|
||
this.stats.delete(eventType);
|
||
} else {
|
||
this.stats.clear();
|
||
}
|
||
}
|
||
/**
|
||
* 启用/禁用事件系统
|
||
* @param enabled 是否启用
|
||
*/
|
||
setEnabled(enabled) {
|
||
this.isEnabled = enabled;
|
||
}
|
||
/**
|
||
* 检查是否有指定事件类型的监听器
|
||
* @param eventType 事件类型
|
||
* @returns 是否有监听器
|
||
*/
|
||
hasListeners(eventType) {
|
||
const listeners = this.listeners.get(eventType);
|
||
return listeners ? listeners.length > 0 : false;
|
||
}
|
||
/**
|
||
* 获取指定事件类型的监听器数量
|
||
* @param eventType 事件类型
|
||
* @returns 监听器数量
|
||
*/
|
||
getListenerCount(eventType) {
|
||
const listeners = this.listeners.get(eventType);
|
||
return listeners ? listeners.length : 0;
|
||
}
|
||
/**
|
||
* 清空所有事件监听器和数据
|
||
*/
|
||
clear() {
|
||
this.listeners.clear();
|
||
this.stats.clear();
|
||
this.clearAllBatches();
|
||
}
|
||
/**
|
||
* 设置每个事件类型的最大监听器数量
|
||
* @param max 最大数量
|
||
*/
|
||
setMaxListeners(max) {
|
||
this.maxListeners = max;
|
||
}
|
||
/**
|
||
* 添加监听器的内部实现
|
||
* @param eventType 事件类型
|
||
* @param handler 事件处理器
|
||
* @param config 配置
|
||
* @returns 监听器ID
|
||
*/
|
||
addListener(eventType, handler, config) {
|
||
let listeners = this.listeners.get(eventType);
|
||
if (!listeners) {
|
||
listeners = [];
|
||
this.listeners.set(eventType, listeners);
|
||
}
|
||
if (listeners.length >= this.maxListeners) {
|
||
TypeSafeEventSystem._logger.warn(`事件类型 ${eventType} 的监听器数量超过最大限制 (${this.maxListeners})`);
|
||
return "";
|
||
}
|
||
const listenerId = `listener_${this.nextListenerId++}`;
|
||
const listener = {
|
||
handler,
|
||
config: {
|
||
priority: 0,
|
||
...config
|
||
},
|
||
id: listenerId
|
||
};
|
||
listeners.push(listener);
|
||
if (!this.stats.has(eventType)) {
|
||
this.stats.set(eventType, this.createEmptyStats(eventType));
|
||
}
|
||
return listenerId;
|
||
}
|
||
/**
|
||
* 执行事件的内部实现
|
||
* @param eventType 事件类型
|
||
* @param event 事件数据
|
||
*/
|
||
async executeEvent(eventType, event) {
|
||
const listeners = this.listeners.get(eventType);
|
||
if (!listeners || listeners.length === 0)
|
||
return;
|
||
const startTime = performance.now();
|
||
const toRemove = [];
|
||
const sortedListeners = this.sortListenersByPriority(listeners);
|
||
const syncListeners = sortedListeners.filter((l) => !l.config.async);
|
||
const asyncListeners = sortedListeners.filter((l) => l.config.async);
|
||
for (const listener of syncListeners) {
|
||
try {
|
||
if (listener.config.context) {
|
||
listener.handler.call(listener.config.context, event);
|
||
} else {
|
||
listener.handler(event);
|
||
}
|
||
if (listener.config.once) {
|
||
toRemove.push(listener.id);
|
||
}
|
||
} catch (error) {
|
||
TypeSafeEventSystem._logger.error(`同步事件处理器执行错误 ${eventType}:`, error);
|
||
}
|
||
}
|
||
const asyncPromises = asyncListeners.map(async (listener) => {
|
||
try {
|
||
if (listener.config.context) {
|
||
await listener.handler.call(listener.config.context, event);
|
||
} else {
|
||
await listener.handler(event);
|
||
}
|
||
if (listener.config.once) {
|
||
toRemove.push(listener.id);
|
||
}
|
||
} catch (error) {
|
||
TypeSafeEventSystem._logger.error(`异步事件处理器执行错误 ${eventType}:`, error);
|
||
}
|
||
});
|
||
await Promise.all(asyncPromises);
|
||
this.removeListeners(eventType, toRemove);
|
||
this.updateStats(eventType, performance.now() - startTime);
|
||
}
|
||
/**
|
||
* 按优先级排序监听器
|
||
* @param listeners 监听器数组
|
||
* @returns 排序后的监听器数组
|
||
*/
|
||
sortListenersByPriority(listeners) {
|
||
return listeners.slice().sort((a, b) => (b.config.priority || 0) - (a.config.priority || 0));
|
||
}
|
||
/**
|
||
* 移除指定的监听器
|
||
* @param eventType 事件类型
|
||
* @param listenerIds 要移除的监听器ID数组
|
||
*/
|
||
removeListeners(eventType, listenerIds) {
|
||
if (listenerIds.length === 0)
|
||
return;
|
||
const listeners = this.listeners.get(eventType);
|
||
if (!listeners)
|
||
return;
|
||
for (const id of listenerIds) {
|
||
const index = listeners.findIndex((l) => l.id === id);
|
||
if (index !== -1) {
|
||
listeners.splice(index, 1);
|
||
}
|
||
}
|
||
if (listeners.length === 0) {
|
||
this.listeners.delete(eventType);
|
||
this.stats.delete(eventType);
|
||
}
|
||
}
|
||
/**
|
||
* 添加事件到批处理队列
|
||
* @param eventType 事件类型
|
||
* @param event 事件数据
|
||
*/
|
||
addToBatch(eventType, event) {
|
||
let batch = this.batchQueue.get(eventType);
|
||
if (!batch) {
|
||
batch = [];
|
||
this.batchQueue.set(eventType, batch);
|
||
}
|
||
batch.push(event);
|
||
const config = this.batchConfigs.get(eventType);
|
||
if (batch.length >= config.batchSize) {
|
||
this.flushBatch(eventType);
|
||
return;
|
||
}
|
||
if (!this.batchTimers.has(eventType)) {
|
||
const timer = setTimeout(() => {
|
||
this.flushBatch(eventType);
|
||
}, config.delay);
|
||
this.batchTimers.set(eventType, timer);
|
||
}
|
||
}
|
||
/**
|
||
* 处理批处理事件
|
||
* @param eventType 事件类型
|
||
* @param batch 批处理事件数组
|
||
*/
|
||
async processBatch(eventType, batch) {
|
||
const batchEvent = {
|
||
type: eventType,
|
||
events: batch,
|
||
count: batch.length,
|
||
timestamp: Date.now()
|
||
};
|
||
await this.executeEvent(`${eventType}:batch`, batchEvent);
|
||
}
|
||
/**
|
||
* 清除指定事件类型的批处理
|
||
* @param eventType 事件类型
|
||
*/
|
||
clearBatch(eventType) {
|
||
this.batchQueue.delete(eventType);
|
||
const timer = this.batchTimers.get(eventType);
|
||
if (timer) {
|
||
clearTimeout(timer);
|
||
this.batchTimers.delete(eventType);
|
||
}
|
||
}
|
||
/**
|
||
* 清除所有批处理
|
||
*/
|
||
clearAllBatches() {
|
||
this.batchQueue.clear();
|
||
for (const timer of this.batchTimers.values()) {
|
||
clearTimeout(timer);
|
||
}
|
||
this.batchTimers.clear();
|
||
this.batchConfigs.clear();
|
||
}
|
||
/**
|
||
* 更新事件统计信息
|
||
* @param eventType 事件类型
|
||
* @param executionTime 执行时间
|
||
*/
|
||
updateStats(eventType, executionTime) {
|
||
let stats = this.stats.get(eventType);
|
||
if (!stats) {
|
||
stats = this.createEmptyStats(eventType);
|
||
this.stats.set(eventType, stats);
|
||
}
|
||
stats.triggerCount++;
|
||
stats.totalExecutionTime += executionTime;
|
||
stats.averageExecutionTime = stats.totalExecutionTime / stats.triggerCount;
|
||
stats.lastTriggerTime = Date.now();
|
||
stats.listenerCount = this.getListenerCount(eventType);
|
||
}
|
||
/**
|
||
* 创建空的统计信息
|
||
* @param eventType 事件类型
|
||
* @returns 空的统计信息
|
||
*/
|
||
createEmptyStats(eventType) {
|
||
return {
|
||
eventType,
|
||
listenerCount: 0,
|
||
triggerCount: 0,
|
||
totalExecutionTime: 0,
|
||
averageExecutionTime: 0,
|
||
lastTriggerTime: 0
|
||
};
|
||
}
|
||
}
|
||
TypeSafeEventSystem._logger = createLogger("EventSystem");
|
||
var ECSEventType;
|
||
(function(ECSEventType2) {
|
||
ECSEventType2["ENTITY_CREATED"] = "entity:created";
|
||
ECSEventType2["ENTITY_DESTROYED"] = "entity:destroyed";
|
||
ECSEventType2["ENTITY_ENABLED"] = "entity:enabled";
|
||
ECSEventType2["ENTITY_DISABLED"] = "entity:disabled";
|
||
ECSEventType2["ENTITY_TAG_ADDED"] = "entity:tag:added";
|
||
ECSEventType2["ENTITY_TAG_REMOVED"] = "entity:tag:removed";
|
||
ECSEventType2["ENTITY_NAME_CHANGED"] = "entity:name:changed";
|
||
ECSEventType2["COMPONENT_ADDED"] = "component:added";
|
||
ECSEventType2["COMPONENT_REMOVED"] = "component:removed";
|
||
ECSEventType2["COMPONENT_MODIFIED"] = "component:modified";
|
||
ECSEventType2["COMPONENT_ENABLED"] = "component:enabled";
|
||
ECSEventType2["COMPONENT_DISABLED"] = "component:disabled";
|
||
ECSEventType2["SYSTEM_ADDED"] = "system:added";
|
||
ECSEventType2["SYSTEM_REMOVED"] = "system:removed";
|
||
ECSEventType2["SYSTEM_ENABLED"] = "system:enabled";
|
||
ECSEventType2["SYSTEM_DISABLED"] = "system:disabled";
|
||
ECSEventType2["SYSTEM_PROCESSING_START"] = "system:processing:start";
|
||
ECSEventType2["SYSTEM_PROCESSING_END"] = "system:processing:end";
|
||
ECSEventType2["SYSTEM_ERROR"] = "system:error";
|
||
ECSEventType2["SCENE_CREATED"] = "scene:created";
|
||
ECSEventType2["SCENE_DESTROYED"] = "scene:destroyed";
|
||
ECSEventType2["SCENE_ACTIVATED"] = "scene:activated";
|
||
ECSEventType2["SCENE_DEACTIVATED"] = "scene:deactivated";
|
||
ECSEventType2["SCENE_PAUSED"] = "scene:paused";
|
||
ECSEventType2["SCENE_RESUMED"] = "scene:resumed";
|
||
ECSEventType2["QUERY_EXECUTED"] = "query:executed";
|
||
ECSEventType2["QUERY_CACHE_HIT"] = "query:cache:hit";
|
||
ECSEventType2["QUERY_CACHE_MISS"] = "query:cache:miss";
|
||
ECSEventType2["QUERY_OPTIMIZED"] = "query:optimized";
|
||
ECSEventType2["PERFORMANCE_WARNING"] = "performance:warning";
|
||
ECSEventType2["PERFORMANCE_CRITICAL"] = "performance:critical";
|
||
ECSEventType2["MEMORY_USAGE_HIGH"] = "memory:usage:high";
|
||
ECSEventType2["FRAME_RATE_DROP"] = "frame:rate:drop";
|
||
ECSEventType2["INDEX_CREATED"] = "index:created";
|
||
ECSEventType2["INDEX_UPDATED"] = "index:updated";
|
||
ECSEventType2["INDEX_OPTIMIZED"] = "index:optimized";
|
||
ECSEventType2["ARCHETYPE_CREATED"] = "archetype:created";
|
||
ECSEventType2["ARCHETYPE_ENTITY_ADDED"] = "archetype:entity:added";
|
||
ECSEventType2["ARCHETYPE_ENTITY_REMOVED"] = "archetype:entity:removed";
|
||
ECSEventType2["DIRTY_MARK_ADDED"] = "dirty:mark:added";
|
||
ECSEventType2["DIRTY_BATCH_PROCESSED"] = "dirty:batch:processed";
|
||
ECSEventType2["ERROR_OCCURRED"] = "error:occurred";
|
||
ECSEventType2["WARNING_ISSUED"] = "warning:issued";
|
||
ECSEventType2["FRAMEWORK_INITIALIZED"] = "framework:initialized";
|
||
ECSEventType2["FRAMEWORK_SHUTDOWN"] = "framework:shutdown";
|
||
ECSEventType2["DEBUG_INFO"] = "debug:info";
|
||
ECSEventType2["DEBUG_STATS_UPDATED"] = "debug:stats:updated";
|
||
})(ECSEventType || (ECSEventType = {}));
|
||
var EventPriority;
|
||
(function(EventPriority2) {
|
||
EventPriority2[EventPriority2["LOWEST"] = 0] = "LOWEST";
|
||
EventPriority2[EventPriority2["LOW"] = 25] = "LOW";
|
||
EventPriority2[EventPriority2["NORMAL"] = 50] = "NORMAL";
|
||
EventPriority2[EventPriority2["HIGH"] = 75] = "HIGH";
|
||
EventPriority2[EventPriority2["HIGHEST"] = 100] = "HIGHEST";
|
||
EventPriority2[EventPriority2["CRITICAL"] = 200] = "CRITICAL";
|
||
})(EventPriority || (EventPriority = {}));
|
||
const EVENT_TYPES = {
|
||
// 实体事件
|
||
ENTITY: {
|
||
CREATED: ECSEventType.ENTITY_CREATED,
|
||
DESTROYED: ECSEventType.ENTITY_DESTROYED,
|
||
ENABLED: ECSEventType.ENTITY_ENABLED,
|
||
DISABLED: ECSEventType.ENTITY_DISABLED,
|
||
TAG_ADDED: ECSEventType.ENTITY_TAG_ADDED,
|
||
TAG_REMOVED: ECSEventType.ENTITY_TAG_REMOVED,
|
||
NAME_CHANGED: ECSEventType.ENTITY_NAME_CHANGED
|
||
},
|
||
// 组件事件
|
||
COMPONENT: {
|
||
ADDED: ECSEventType.COMPONENT_ADDED,
|
||
REMOVED: ECSEventType.COMPONENT_REMOVED,
|
||
MODIFIED: ECSEventType.COMPONENT_MODIFIED,
|
||
ENABLED: ECSEventType.COMPONENT_ENABLED,
|
||
DISABLED: ECSEventType.COMPONENT_DISABLED
|
||
},
|
||
// 系统事件
|
||
SYSTEM: {
|
||
ADDED: ECSEventType.SYSTEM_ADDED,
|
||
REMOVED: ECSEventType.SYSTEM_REMOVED,
|
||
ENABLED: ECSEventType.SYSTEM_ENABLED,
|
||
DISABLED: ECSEventType.SYSTEM_DISABLED,
|
||
PROCESSING_START: ECSEventType.SYSTEM_PROCESSING_START,
|
||
PROCESSING_END: ECSEventType.SYSTEM_PROCESSING_END,
|
||
ERROR: ECSEventType.SYSTEM_ERROR
|
||
},
|
||
// 性能事件
|
||
PERFORMANCE: {
|
||
WARNING: ECSEventType.PERFORMANCE_WARNING,
|
||
CRITICAL: ECSEventType.PERFORMANCE_CRITICAL,
|
||
MEMORY_HIGH: ECSEventType.MEMORY_USAGE_HIGH,
|
||
FRAME_DROP: ECSEventType.FRAME_RATE_DROP
|
||
}
|
||
};
|
||
class EventTypeValidator {
|
||
/**
|
||
* 验证事件类型是否有效
|
||
* @param eventType 事件类型
|
||
* @returns 是否有效
|
||
*/
|
||
static isValid(eventType) {
|
||
return this.validTypes.has(eventType);
|
||
}
|
||
/**
|
||
* 获取所有有效的事件类型
|
||
* @returns 事件类型数组
|
||
*/
|
||
static getAllValidTypes() {
|
||
return Array.from(this.validTypes);
|
||
}
|
||
/**
|
||
* 添加自定义事件类型
|
||
* @param eventType 事件类型
|
||
*/
|
||
static addCustomType(eventType) {
|
||
this.validTypes.add(eventType);
|
||
}
|
||
/**
|
||
* 移除自定义事件类型
|
||
* @param eventType 事件类型
|
||
*/
|
||
static removeCustomType(eventType) {
|
||
this.validTypes.delete(eventType);
|
||
}
|
||
}
|
||
EventTypeValidator.validTypes = /* @__PURE__ */ new Set([
|
||
...Object.values(ECSEventType),
|
||
...Object.values(EVENT_TYPES.ENTITY),
|
||
...Object.values(EVENT_TYPES.COMPONENT),
|
||
...Object.values(EVENT_TYPES.SYSTEM),
|
||
...Object.values(EVENT_TYPES.PERFORMANCE)
|
||
]);
|
||
class EventBus {
|
||
constructor(debugMode = false) {
|
||
this.eventIdCounter = 0;
|
||
this.isDebugMode = false;
|
||
this.eventSystem = new TypeSafeEventSystem();
|
||
this.isDebugMode = debugMode;
|
||
}
|
||
/**
|
||
* 发射事件
|
||
* @param eventType 事件类型
|
||
* @param data 事件数据
|
||
* @param enhance 是否增强事件数据(添加timestamp、eventId等),默认false提升性能
|
||
*/
|
||
emit(eventType, data, enhance = false) {
|
||
this.validateEventType(eventType);
|
||
const finalData = enhance ? this.enhanceEventData(eventType, data) : data;
|
||
if (this.isDebugMode) {
|
||
EventBus._logger.info(`发射事件: ${eventType}`, finalData);
|
||
}
|
||
this.eventSystem.emitSync(eventType, finalData);
|
||
}
|
||
/**
|
||
* 异步发射事件
|
||
* @param eventType 事件类型
|
||
* @param data 事件数据
|
||
* @param enhance 是否增强事件数据(添加timestamp、eventId等),默认false提升性能
|
||
*/
|
||
async emitAsync(eventType, data, enhance = false) {
|
||
this.validateEventType(eventType);
|
||
const finalData = enhance ? this.enhanceEventData(eventType, data) : data;
|
||
if (this.isDebugMode) {
|
||
EventBus._logger.info(`发射异步事件: ${eventType}`, finalData);
|
||
}
|
||
await this.eventSystem.emit(eventType, finalData);
|
||
}
|
||
/**
|
||
* 监听事件
|
||
* @param eventType 事件类型
|
||
* @param handler 事件处理器
|
||
* @param config 监听器配置
|
||
* @returns 监听器ID
|
||
*/
|
||
on(eventType, handler, config = {}) {
|
||
this.validateEventType(eventType);
|
||
const eventConfig = {
|
||
once: config.once || false,
|
||
priority: config.priority || EventPriority.NORMAL,
|
||
async: config.async || false,
|
||
context: config.context
|
||
};
|
||
if (this.isDebugMode) {
|
||
EventBus._logger.info(`添加监听器: ${eventType}`, eventConfig);
|
||
}
|
||
return this.eventSystem.on(eventType, handler, eventConfig);
|
||
}
|
||
/**
|
||
* 监听事件(一次性)
|
||
* @param eventType 事件类型
|
||
* @param handler 事件处理器
|
||
* @param config 监听器配置
|
||
* @returns 监听器ID
|
||
*/
|
||
once(eventType, handler, config = {}) {
|
||
return this.on(eventType, handler, { ...config, once: true });
|
||
}
|
||
/**
|
||
* 异步监听事件
|
||
* @param eventType 事件类型
|
||
* @param handler 异步事件处理器
|
||
* @param config 监听器配置
|
||
* @returns 监听器ID
|
||
*/
|
||
onAsync(eventType, handler, config = {}) {
|
||
return this.on(eventType, handler, { ...config, async: true });
|
||
}
|
||
/**
|
||
* 移除事件监听器
|
||
* @param eventType 事件类型
|
||
* @param listenerId 监听器ID
|
||
*/
|
||
off(eventType, listenerId) {
|
||
if (this.isDebugMode) {
|
||
EventBus._logger.info(`移除监听器: ${listenerId} 事件: ${eventType}`);
|
||
}
|
||
return this.eventSystem.off(eventType, listenerId);
|
||
}
|
||
/**
|
||
* 移除指定事件类型的所有监听器
|
||
* @param eventType 事件类型
|
||
*/
|
||
offAll(eventType) {
|
||
if (this.isDebugMode) {
|
||
EventBus._logger.info(`移除所有监听器: ${eventType}`);
|
||
}
|
||
this.eventSystem.offAll(eventType);
|
||
}
|
||
/**
|
||
* 检查是否有指定事件的监听器
|
||
* @param eventType 事件类型
|
||
*/
|
||
hasListeners(eventType) {
|
||
return this.eventSystem.hasListeners(eventType);
|
||
}
|
||
/**
|
||
* 获取事件统计信息
|
||
* @param eventType 事件类型(可选)
|
||
*/
|
||
getStats(eventType) {
|
||
const stats = this.eventSystem.getStats(eventType);
|
||
if (stats instanceof Map) {
|
||
const result = /* @__PURE__ */ new Map();
|
||
stats.forEach((stat, key) => {
|
||
result.set(key, this.convertEventStats(stat));
|
||
});
|
||
return result;
|
||
} else {
|
||
return this.convertEventStats(stats);
|
||
}
|
||
}
|
||
/**
|
||
* 清空所有监听器
|
||
*/
|
||
clear() {
|
||
if (this.isDebugMode) {
|
||
EventBus._logger.info("清空所有监听器");
|
||
}
|
||
this.eventSystem.clear();
|
||
}
|
||
/**
|
||
* 启用或禁用事件系统
|
||
* @param enabled 是否启用
|
||
*/
|
||
setEnabled(enabled) {
|
||
this.eventSystem.setEnabled(enabled);
|
||
}
|
||
/**
|
||
* 设置调试模式
|
||
* @param debug 是否启用调试
|
||
*/
|
||
setDebugMode(debug) {
|
||
this.isDebugMode = debug;
|
||
}
|
||
/**
|
||
* 设置最大监听器数量
|
||
* @param max 最大数量
|
||
*/
|
||
setMaxListeners(max) {
|
||
this.eventSystem.setMaxListeners(max);
|
||
}
|
||
/**
|
||
* 获取监听器数量
|
||
* @param eventType 事件类型
|
||
*/
|
||
getListenerCount(eventType) {
|
||
return this.eventSystem.getListenerCount(eventType);
|
||
}
|
||
/**
|
||
* 设置事件批处理配置
|
||
* @param eventType 事件类型
|
||
* @param batchSize 批处理大小
|
||
* @param delay 延迟时间(毫秒)
|
||
*/
|
||
setBatchConfig(eventType, batchSize, delay) {
|
||
this.eventSystem.setBatchConfig(eventType, {
|
||
batchSize,
|
||
delay,
|
||
enabled: true
|
||
});
|
||
}
|
||
/**
|
||
* 刷新指定事件的批处理队列
|
||
* @param eventType 事件类型
|
||
*/
|
||
flushBatch(eventType) {
|
||
this.eventSystem.flushBatch(eventType);
|
||
}
|
||
/**
|
||
* 重置事件统计
|
||
* @param eventType 事件类型(可选)
|
||
*/
|
||
resetStats(eventType) {
|
||
this.eventSystem.resetStats(eventType);
|
||
}
|
||
// 便捷方法:发射预定义的ECS事件
|
||
/**
|
||
* 发射实体创建事件
|
||
* @param entityData 实体事件数据
|
||
*/
|
||
emitEntityCreated(entityData) {
|
||
this.emit(ECSEventType.ENTITY_CREATED, entityData);
|
||
}
|
||
/**
|
||
* 发射实体销毁事件
|
||
* @param entityData 实体事件数据
|
||
*/
|
||
emitEntityDestroyed(entityData) {
|
||
this.emit(ECSEventType.ENTITY_DESTROYED, entityData);
|
||
}
|
||
/**
|
||
* 发射组件添加事件
|
||
* @param componentData 组件事件数据
|
||
*/
|
||
emitComponentAdded(componentData) {
|
||
this.emit(ECSEventType.COMPONENT_ADDED, componentData);
|
||
}
|
||
/**
|
||
* 发射组件移除事件
|
||
* @param componentData 组件事件数据
|
||
*/
|
||
emitComponentRemoved(componentData) {
|
||
this.emit(ECSEventType.COMPONENT_REMOVED, componentData);
|
||
}
|
||
/**
|
||
* 发射系统添加事件
|
||
* @param systemData 系统事件数据
|
||
*/
|
||
emitSystemAdded(systemData) {
|
||
this.emit(ECSEventType.SYSTEM_ADDED, systemData);
|
||
}
|
||
/**
|
||
* 发射系统移除事件
|
||
* @param systemData 系统事件数据
|
||
*/
|
||
emitSystemRemoved(systemData) {
|
||
this.emit(ECSEventType.SYSTEM_REMOVED, systemData);
|
||
}
|
||
/**
|
||
* 发射场景变化事件
|
||
* @param sceneData 场景事件数据
|
||
*/
|
||
emitSceneChanged(sceneData) {
|
||
this.emit(ECSEventType.SCENE_ACTIVATED, sceneData);
|
||
}
|
||
/**
|
||
* 发射性能警告事件
|
||
* @param performanceData 性能事件数据
|
||
*/
|
||
emitPerformanceWarning(performanceData) {
|
||
this.emit(ECSEventType.PERFORMANCE_WARNING, performanceData);
|
||
}
|
||
// 便捷方法:监听预定义的ECS事件
|
||
/**
|
||
* 监听实体创建事件
|
||
* @param handler 事件处理器
|
||
* @param config 监听器配置
|
||
*/
|
||
onEntityCreated(handler, config) {
|
||
return this.on(ECSEventType.ENTITY_CREATED, handler, config);
|
||
}
|
||
/**
|
||
* 监听组件添加事件
|
||
* @param handler 事件处理器
|
||
* @param config 监听器配置
|
||
*/
|
||
onComponentAdded(handler, config) {
|
||
return this.on(ECSEventType.COMPONENT_ADDED, handler, config);
|
||
}
|
||
/**
|
||
* 监听系统错误事件
|
||
* @param handler 事件处理器
|
||
* @param config 监听器配置
|
||
*/
|
||
onSystemError(handler, config) {
|
||
return this.on(ECSEventType.SYSTEM_ERROR, handler, config);
|
||
}
|
||
/**
|
||
* 监听性能警告事件
|
||
* @param handler 事件处理器
|
||
* @param config 监听器配置
|
||
*/
|
||
onPerformanceWarning(handler, config) {
|
||
return this.on(ECSEventType.PERFORMANCE_WARNING, handler, config);
|
||
}
|
||
// 私有方法
|
||
/**
|
||
* 验证事件类型(仅在debug模式下执行,提升性能)
|
||
* @param eventType 事件类型
|
||
*/
|
||
validateEventType(eventType) {
|
||
if (this.isDebugMode) {
|
||
if (!EventTypeValidator.isValid(eventType)) {
|
||
EventBus._logger.warn(`未知事件类型: ${eventType}`);
|
||
EventTypeValidator.addCustomType(eventType);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 增强事件数据
|
||
* @param eventType 事件类型
|
||
* @param data 原始数据
|
||
*/
|
||
enhanceEventData(eventType, data) {
|
||
if (data === null || data === void 0) {
|
||
return {
|
||
timestamp: Date.now(),
|
||
eventId: `${eventType}_${++this.eventIdCounter}`,
|
||
source: "EventBus"
|
||
};
|
||
}
|
||
const enhanced = data;
|
||
if (!enhanced.timestamp) {
|
||
enhanced.timestamp = Date.now();
|
||
}
|
||
if (!enhanced.eventId) {
|
||
enhanced.eventId = `${eventType}_${++this.eventIdCounter}`;
|
||
}
|
||
if (!enhanced.source) {
|
||
enhanced.source = "EventBus";
|
||
}
|
||
return enhanced;
|
||
}
|
||
/**
|
||
* 转换EventStats为IEventStats
|
||
* @param stats EventStats实例
|
||
*/
|
||
convertEventStats(stats) {
|
||
return {
|
||
eventType: stats.eventType,
|
||
listenerCount: stats.listenerCount,
|
||
triggerCount: stats.triggerCount,
|
||
totalExecutionTime: stats.totalExecutionTime,
|
||
averageExecutionTime: stats.averageExecutionTime,
|
||
lastTriggerTime: stats.lastTriggerTime
|
||
};
|
||
}
|
||
}
|
||
EventBus._logger = createLogger("EventBus");
|
||
class Scene {
|
||
/**
|
||
* 获取系统列表(兼容性属性)
|
||
*/
|
||
get systems() {
|
||
return this.entityProcessors.processors;
|
||
}
|
||
/**
|
||
* 创建场景实例
|
||
*/
|
||
constructor(config) {
|
||
this.name = "";
|
||
this._didSceneBegin = false;
|
||
this.entities = new EntityList(this);
|
||
this.entityProcessors = new EntityProcessorList();
|
||
this.identifierPool = new IdentifierPool();
|
||
this.componentStorageManager = new ComponentStorageManager();
|
||
this.querySystem = new QuerySystem();
|
||
this.eventSystem = new TypeSafeEventSystem();
|
||
if (config?.name) {
|
||
this.name = config.name;
|
||
}
|
||
if (config?.enableEntityDirectUpdate !== void 0) {
|
||
this.entities.setEnableEntityDirectUpdate(config.enableEntityDirectUpdate);
|
||
}
|
||
if (!Entity.eventBus) {
|
||
Entity.eventBus = new EventBus(false);
|
||
}
|
||
if (Entity.eventBus) {
|
||
Entity.eventBus.onComponentAdded((data) => {
|
||
this.eventSystem.emitSync("component:added", data);
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 初始化场景
|
||
*
|
||
* 在场景创建时调用,子类可以重写此方法来设置初始实体和组件。
|
||
*/
|
||
initialize() {
|
||
}
|
||
/**
|
||
* 场景开始运行时的回调
|
||
*
|
||
* 在场景开始运行时调用,可以在此方法中执行场景启动逻辑。
|
||
*/
|
||
onStart() {
|
||
}
|
||
/**
|
||
* 场景卸载时的回调
|
||
*
|
||
* 在场景被销毁时调用,可以在此方法中执行清理工作。
|
||
*/
|
||
unload() {
|
||
}
|
||
/**
|
||
* 开始场景,启动实体处理器等
|
||
*
|
||
* 这个方法会启动场景。它将启动实体处理器等,并调用onStart方法。
|
||
*/
|
||
begin() {
|
||
if (this.entityProcessors != null)
|
||
this.entityProcessors.begin();
|
||
this._didSceneBegin = true;
|
||
this.onStart();
|
||
}
|
||
/**
|
||
* 结束场景,清除实体、实体处理器等
|
||
*
|
||
* 这个方法会结束场景。它将移除所有实体,结束实体处理器等,并调用unload方法。
|
||
*/
|
||
end() {
|
||
this._didSceneBegin = false;
|
||
this.entities.removeAllEntities();
|
||
this.querySystem.setEntities([]);
|
||
this.componentStorageManager.clear();
|
||
if (this.entityProcessors)
|
||
this.entityProcessors.end();
|
||
this.unload();
|
||
}
|
||
/**
|
||
* 更新场景
|
||
*/
|
||
update() {
|
||
this.entities.updateLists();
|
||
if (this.entityProcessors != null)
|
||
this.entityProcessors.update();
|
||
this.entities.update();
|
||
if (this.entityProcessors != null)
|
||
this.entityProcessors.lateUpdate();
|
||
}
|
||
/**
|
||
* 将实体添加到此场景,并返回它
|
||
* @param name 实体名称
|
||
*/
|
||
createEntity(name) {
|
||
let entity = new Entity(name, this.identifierPool.checkOut());
|
||
this.eventSystem.emitSync("entity:created", { entityName: name, entity, scene: this });
|
||
return this.addEntity(entity);
|
||
}
|
||
/**
|
||
* 清除所有EntitySystem的实体缓存
|
||
* 当实体或组件发生变化时调用
|
||
*/
|
||
clearSystemEntityCaches() {
|
||
for (const system of this.entityProcessors.processors) {
|
||
system.clearEntityCache();
|
||
}
|
||
}
|
||
/**
|
||
* 在场景的实体列表中添加一个实体
|
||
* @param entity 要添加的实体
|
||
* @param deferCacheClear 是否延迟缓存清理(用于批量操作)
|
||
*/
|
||
addEntity(entity, deferCacheClear = false) {
|
||
this.entities.add(entity);
|
||
entity.scene = this;
|
||
this.querySystem.addEntity(entity, deferCacheClear);
|
||
if (!deferCacheClear) {
|
||
this.clearSystemEntityCaches();
|
||
}
|
||
this.eventSystem.emitSync("entity:added", { entity, scene: this });
|
||
return entity;
|
||
}
|
||
/**
|
||
* 批量创建实体(高性能版本)
|
||
* @param count 要创建的实体数量
|
||
* @param namePrefix 实体名称前缀
|
||
* @returns 创建的实体列表
|
||
*/
|
||
createEntities(count, namePrefix = "Entity") {
|
||
const entities = [];
|
||
for (let i = 0; i < count; i++) {
|
||
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
|
||
entity.scene = this;
|
||
entities.push(entity);
|
||
}
|
||
for (const entity of entities) {
|
||
this.entities.add(entity);
|
||
}
|
||
this.querySystem.addEntitiesUnchecked(entities);
|
||
this.eventSystem.emitSync("entities:batch_added", { entities, scene: this, count });
|
||
return entities;
|
||
}
|
||
/**
|
||
* 从场景中删除所有实体
|
||
*/
|
||
destroyAllEntities() {
|
||
this.entities.removeAllEntities();
|
||
this.querySystem.setEntities([]);
|
||
}
|
||
/**
|
||
* 搜索并返回第一个具有名称的实体
|
||
* @param name 实体名称
|
||
*/
|
||
findEntity(name) {
|
||
return this.entities.findEntity(name);
|
||
}
|
||
/**
|
||
* 根据ID查找实体
|
||
* @param id 实体ID
|
||
*/
|
||
findEntityById(id) {
|
||
return this.entities.findEntityById(id);
|
||
}
|
||
/**
|
||
* 根据标签查找实体
|
||
* @param tag 实体标签
|
||
*/
|
||
findEntitiesByTag(tag) {
|
||
const result = [];
|
||
for (const entity of this.entities.buffer) {
|
||
if (entity.tag === tag) {
|
||
result.push(entity);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 根据名称查找实体(别名方法)
|
||
* @param name 实体名称
|
||
*/
|
||
getEntityByName(name) {
|
||
return this.findEntity(name);
|
||
}
|
||
/**
|
||
* 根据标签查找实体(别名方法)
|
||
* @param tag 实体标签
|
||
*/
|
||
getEntitiesByTag(tag) {
|
||
return this.findEntitiesByTag(tag);
|
||
}
|
||
/**
|
||
* 在场景中添加一个EntitySystem处理器
|
||
* @param processor 处理器
|
||
*/
|
||
addEntityProcessor(processor) {
|
||
if (this.entityProcessors.processors.includes(processor)) {
|
||
return processor;
|
||
}
|
||
processor.scene = this;
|
||
this.entityProcessors.add(processor);
|
||
processor.initialize();
|
||
processor.setUpdateOrder(this.entityProcessors.count - 1);
|
||
return processor;
|
||
}
|
||
/**
|
||
* 添加系统到场景(addEntityProcessor的别名)
|
||
* @param system 系统
|
||
*/
|
||
addSystem(system) {
|
||
return this.addEntityProcessor(system);
|
||
}
|
||
/**
|
||
* 从场景中删除EntitySystem处理器
|
||
* @param processor 要删除的处理器
|
||
*/
|
||
removeEntityProcessor(processor) {
|
||
this.entityProcessors.remove(processor);
|
||
processor.reset();
|
||
}
|
||
/**
|
||
* 从场景中删除系统(removeEntityProcessor的别名)
|
||
* @param system 系统
|
||
*/
|
||
removeSystem(system) {
|
||
this.removeEntityProcessor(system);
|
||
}
|
||
/**
|
||
* 获取指定类型的EntitySystem处理器
|
||
* @param type 处理器类型
|
||
*/
|
||
getEntityProcessor(type) {
|
||
return this.entityProcessors.getProcessor(type);
|
||
}
|
||
/**
|
||
* 获取场景统计信息
|
||
*/
|
||
getStats() {
|
||
return {
|
||
entityCount: this.entities.count,
|
||
processorCount: this.entityProcessors.count,
|
||
componentStorageStats: this.componentStorageManager.getAllStats()
|
||
};
|
||
}
|
||
/**
|
||
* 获取场景的调试信息
|
||
*/
|
||
getDebugInfo() {
|
||
return {
|
||
name: this.name || this.constructor.name,
|
||
entityCount: this.entities.count,
|
||
processorCount: this.entityProcessors.count,
|
||
isRunning: this._didSceneBegin,
|
||
entities: this.entities.buffer.map((entity) => ({
|
||
name: entity.name,
|
||
id: entity.id,
|
||
componentCount: entity.components.length,
|
||
componentTypes: entity.components.map((c) => getComponentInstanceTypeName(c))
|
||
})),
|
||
processors: this.entityProcessors.processors.map((processor) => ({
|
||
name: getSystemInstanceTypeName(processor),
|
||
updateOrder: processor.updateOrder,
|
||
entityCount: processor._entities?.length || 0
|
||
})),
|
||
componentStats: this.componentStorageManager.getAllStats()
|
||
};
|
||
}
|
||
}
|
||
class SceneBuilder {
|
||
constructor() {
|
||
this.scene = new Scene();
|
||
}
|
||
/**
|
||
* 设置场景名称
|
||
* @param name 场景名称
|
||
* @returns 场景构建器
|
||
*/
|
||
named(name) {
|
||
this.scene.name = name;
|
||
return this;
|
||
}
|
||
/**
|
||
* 添加实体
|
||
* @param entity 实体
|
||
* @returns 场景构建器
|
||
*/
|
||
withEntity(entity) {
|
||
this.scene.addEntity(entity);
|
||
return this;
|
||
}
|
||
/**
|
||
* 使用实体构建器添加实体
|
||
* @param builderFn 实体构建器函数
|
||
* @returns 场景构建器
|
||
*/
|
||
withEntityBuilder(builderFn) {
|
||
const builder = new EntityBuilder(this.scene, this.scene.componentStorageManager);
|
||
const configuredBuilder = builderFn(builder);
|
||
const entity = configuredBuilder.build();
|
||
this.scene.addEntity(entity);
|
||
return this;
|
||
}
|
||
/**
|
||
* 批量添加实体
|
||
* @param entities 实体数组
|
||
* @returns 场景构建器
|
||
*/
|
||
withEntities(...entities) {
|
||
for (const entity of entities) {
|
||
this.scene.addEntity(entity);
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 添加系统
|
||
* @param system 系统实例
|
||
* @returns 场景构建器
|
||
*/
|
||
withSystem(system) {
|
||
this.scene.addSystem(system);
|
||
return this;
|
||
}
|
||
/**
|
||
* 批量添加系统
|
||
* @param systems 系统数组
|
||
* @returns 场景构建器
|
||
*/
|
||
withSystems(...systems) {
|
||
for (const system of systems) {
|
||
this.scene.addSystem(system);
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 构建并返回场景
|
||
* @returns 构建的场景
|
||
*/
|
||
build() {
|
||
return this.scene;
|
||
}
|
||
}
|
||
class ComponentBuilder {
|
||
constructor(componentClass, ...args) {
|
||
this.component = new componentClass(...args);
|
||
}
|
||
/**
|
||
* 设置组件属性
|
||
* @param property 属性名
|
||
* @param value 属性值
|
||
* @returns 组件构建器
|
||
*/
|
||
set(property, value) {
|
||
this.component[property] = value;
|
||
return this;
|
||
}
|
||
/**
|
||
* 使用配置函数设置组件
|
||
* @param configurator 配置函数
|
||
* @returns 组件构建器
|
||
*/
|
||
configure(configurator) {
|
||
configurator(this.component);
|
||
return this;
|
||
}
|
||
/**
|
||
* 条件性设置属性
|
||
* @param condition 条件
|
||
* @param property 属性名
|
||
* @param value 属性值
|
||
* @returns 组件构建器
|
||
*/
|
||
setIf(condition, property, value) {
|
||
if (condition) {
|
||
this.component[property] = value;
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 构建并返回组件
|
||
* @returns 构建的组件
|
||
*/
|
||
build() {
|
||
return this.component;
|
||
}
|
||
}
|
||
class EntityBatchOperator {
|
||
constructor(entities) {
|
||
this.entities = entities;
|
||
}
|
||
/**
|
||
* 批量添加组件
|
||
* @param component 组件实例
|
||
* @returns 批量操作器
|
||
*/
|
||
addComponent(component) {
|
||
for (const entity of this.entities) {
|
||
entity.addComponent(component);
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 批量移除组件
|
||
* @param componentType 组件类型
|
||
* @returns 批量操作器
|
||
*/
|
||
removeComponent(componentType) {
|
||
for (const entity of this.entities) {
|
||
entity.removeComponentByType(componentType);
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 批量设置活跃状态
|
||
* @param active 是否活跃
|
||
* @returns 批量操作器
|
||
*/
|
||
setActive(active) {
|
||
for (const entity of this.entities) {
|
||
entity.active = active;
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 批量设置标签
|
||
* @param tag 标签
|
||
* @returns 批量操作器
|
||
*/
|
||
setTag(tag) {
|
||
for (const entity of this.entities) {
|
||
entity.tag = tag;
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 批量执行操作
|
||
* @param operation 操作函数
|
||
* @returns 批量操作器
|
||
*/
|
||
forEach(operation) {
|
||
this.entities.forEach(operation);
|
||
return this;
|
||
}
|
||
/**
|
||
* 过滤实体
|
||
* @param predicate 过滤条件
|
||
* @returns 新的批量操作器
|
||
*/
|
||
filter(predicate) {
|
||
return new EntityBatchOperator(this.entities.filter(predicate));
|
||
}
|
||
/**
|
||
* 获取实体数组
|
||
* @returns 实体数组
|
||
*/
|
||
toArray() {
|
||
return this.entities.slice();
|
||
}
|
||
/**
|
||
* 获取实体数量
|
||
* @returns 实体数量
|
||
*/
|
||
count() {
|
||
return this.entities.length;
|
||
}
|
||
}
|
||
class ECSFluentAPI {
|
||
constructor(scene, querySystem, eventSystem) {
|
||
this.scene = scene;
|
||
this.querySystem = querySystem;
|
||
this.eventSystem = eventSystem;
|
||
}
|
||
/**
|
||
* 创建实体构建器
|
||
* @returns 实体构建器
|
||
*/
|
||
createEntity() {
|
||
return new EntityBuilder(this.scene, this.scene.componentStorageManager);
|
||
}
|
||
/**
|
||
* 创建场景构建器
|
||
* @returns 场景构建器
|
||
*/
|
||
createScene() {
|
||
return new SceneBuilder();
|
||
}
|
||
/**
|
||
* 创建组件构建器
|
||
* @param componentClass 组件类
|
||
* @param args 构造参数
|
||
* @returns 组件构建器
|
||
*/
|
||
createComponent(componentClass, ...args) {
|
||
return new ComponentBuilder(componentClass, ...args);
|
||
}
|
||
/**
|
||
* 创建查询构建器
|
||
* @returns 查询构建器
|
||
*/
|
||
query() {
|
||
return new QueryBuilder(this.querySystem);
|
||
}
|
||
/**
|
||
* 查找实体
|
||
* @param componentTypes 组件类型
|
||
* @returns 实体数组
|
||
*/
|
||
find(...componentTypes) {
|
||
return this.querySystem.queryAll(...componentTypes).entities;
|
||
}
|
||
/**
|
||
* 查找第一个匹配的实体
|
||
* @param componentTypes 组件类型
|
||
* @returns 实体或null
|
||
*/
|
||
findFirst(...componentTypes) {
|
||
const result = this.querySystem.queryAll(...componentTypes);
|
||
return result.entities.length > 0 ? result.entities[0] : null;
|
||
}
|
||
/**
|
||
* 按名称查找实体
|
||
* @param name 实体名称
|
||
* @returns 实体或null
|
||
*/
|
||
findByName(name) {
|
||
return this.scene.findEntity(name);
|
||
}
|
||
/**
|
||
* 按标签查找实体
|
||
* @param tag 标签
|
||
* @returns 实体数组
|
||
*/
|
||
findByTag(tag) {
|
||
return this.scene.findEntitiesByTag(tag);
|
||
}
|
||
/**
|
||
* 触发事件
|
||
* @param eventType 事件类型
|
||
* @param event 事件数据
|
||
*/
|
||
emit(eventType, event) {
|
||
this.eventSystem.emitSync(eventType, event);
|
||
}
|
||
/**
|
||
* 异步触发事件
|
||
* @param eventType 事件类型
|
||
* @param event 事件数据
|
||
*/
|
||
async emitAsync(eventType, event) {
|
||
await this.eventSystem.emit(eventType, event);
|
||
}
|
||
/**
|
||
* 监听事件
|
||
* @param eventType 事件类型
|
||
* @param handler 事件处理器
|
||
* @returns 监听器ID
|
||
*/
|
||
on(eventType, handler) {
|
||
return this.eventSystem.on(eventType, handler);
|
||
}
|
||
/**
|
||
* 一次性监听事件
|
||
* @param eventType 事件类型
|
||
* @param handler 事件处理器
|
||
* @returns 监听器ID
|
||
*/
|
||
once(eventType, handler) {
|
||
return this.eventSystem.once(eventType, handler);
|
||
}
|
||
/**
|
||
* 移除事件监听器
|
||
* @param eventType 事件类型
|
||
* @param listenerId 监听器ID
|
||
*/
|
||
off(eventType, listenerId) {
|
||
this.eventSystem.off(eventType, listenerId);
|
||
}
|
||
/**
|
||
* 批量操作实体
|
||
* @param entities 实体数组
|
||
* @returns 批量操作器
|
||
*/
|
||
batch(entities) {
|
||
return new EntityBatchOperator(entities);
|
||
}
|
||
/**
|
||
* 获取场景统计信息
|
||
* @returns 统计信息
|
||
*/
|
||
getStats() {
|
||
return {
|
||
entityCount: this.scene.entities.count,
|
||
systemCount: this.scene.systems.length,
|
||
componentStats: this.scene.componentStorageManager.getAllStats(),
|
||
queryStats: this.querySystem.getStats(),
|
||
eventStats: this.eventSystem.getStats()
|
||
};
|
||
}
|
||
}
|
||
function createECSAPI(scene, querySystem, eventSystem) {
|
||
return new ECSFluentAPI(scene, querySystem, eventSystem);
|
||
}
|
||
const logger$1 = createLogger("World");
|
||
class World {
|
||
constructor(config = {}) {
|
||
this._scenes = /* @__PURE__ */ new Map();
|
||
this._activeScenes = /* @__PURE__ */ new Set();
|
||
this._globalSystems = [];
|
||
this._isActive = false;
|
||
this._config = {
|
||
name: "World",
|
||
debug: false,
|
||
maxScenes: 10,
|
||
autoCleanup: true,
|
||
...config
|
||
};
|
||
this.name = this._config.name;
|
||
this._createdAt = Date.now();
|
||
logger$1.info(`创建World: ${this.name}`);
|
||
}
|
||
// ===== Scene管理 =====
|
||
/**
|
||
* 创建并添加Scene到World
|
||
*/
|
||
createScene(sceneId, sceneInstance) {
|
||
if (this._scenes.has(sceneId)) {
|
||
throw new Error(`Scene ID '${sceneId}' 已存在于World '${this.name}' 中`);
|
||
}
|
||
if (this._scenes.size >= this._config.maxScenes) {
|
||
throw new Error(`World '${this.name}' 已达到最大Scene数量限制: ${this._config.maxScenes}`);
|
||
}
|
||
const scene = sceneInstance || new Scene();
|
||
if ("id" in scene) {
|
||
scene.id = sceneId;
|
||
}
|
||
if ("name" in scene && !scene.name) {
|
||
scene.name = sceneId;
|
||
}
|
||
this._scenes.set(sceneId, scene);
|
||
scene.initialize();
|
||
logger$1.info(`在World '${this.name}' 中创建Scene: ${sceneId}`);
|
||
return scene;
|
||
}
|
||
/**
|
||
* 移除Scene
|
||
*/
|
||
removeScene(sceneId) {
|
||
const scene = this._scenes.get(sceneId);
|
||
if (!scene) {
|
||
return false;
|
||
}
|
||
if (this._activeScenes.has(sceneId)) {
|
||
this.setSceneActive(sceneId, false);
|
||
}
|
||
scene.end();
|
||
this._scenes.delete(sceneId);
|
||
logger$1.info(`从World '${this.name}' 中移除Scene: ${sceneId}`);
|
||
return true;
|
||
}
|
||
/**
|
||
* 获取Scene
|
||
*/
|
||
getScene(sceneId) {
|
||
return this._scenes.get(sceneId) || null;
|
||
}
|
||
/**
|
||
* 获取所有Scene ID
|
||
*/
|
||
getSceneIds() {
|
||
return Array.from(this._scenes.keys());
|
||
}
|
||
/**
|
||
* 获取所有Scene
|
||
*/
|
||
getAllScenes() {
|
||
return Array.from(this._scenes.values());
|
||
}
|
||
/**
|
||
* 设置Scene激活状态
|
||
*/
|
||
setSceneActive(sceneId, active) {
|
||
const scene = this._scenes.get(sceneId);
|
||
if (!scene) {
|
||
logger$1.warn(`Scene '${sceneId}' 不存在于World '${this.name}' 中`);
|
||
return;
|
||
}
|
||
if (active) {
|
||
this._activeScenes.add(sceneId);
|
||
if (scene.begin) {
|
||
scene.begin();
|
||
}
|
||
logger$1.debug(`在World '${this.name}' 中激活Scene: ${sceneId}`);
|
||
} else {
|
||
this._activeScenes.delete(sceneId);
|
||
logger$1.debug(`在World '${this.name}' 中停用Scene: ${sceneId}`);
|
||
}
|
||
}
|
||
/**
|
||
* 检查Scene是否激活
|
||
*/
|
||
isSceneActive(sceneId) {
|
||
return this._activeScenes.has(sceneId);
|
||
}
|
||
/**
|
||
* 获取活跃Scene数量
|
||
*/
|
||
getActiveSceneCount() {
|
||
return this._activeScenes.size;
|
||
}
|
||
// ===== 全局System管理 =====
|
||
/**
|
||
* 添加全局System
|
||
* 全局System会在所有激活Scene之前更新
|
||
*/
|
||
addGlobalSystem(system) {
|
||
if (this._globalSystems.includes(system)) {
|
||
return system;
|
||
}
|
||
this._globalSystems.push(system);
|
||
if (system.initialize) {
|
||
system.initialize();
|
||
}
|
||
logger$1.debug(`在World '${this.name}' 中添加全局System: ${system.name}`);
|
||
return system;
|
||
}
|
||
/**
|
||
* 移除全局System
|
||
*/
|
||
removeGlobalSystem(system) {
|
||
const index = this._globalSystems.indexOf(system);
|
||
if (index === -1) {
|
||
return false;
|
||
}
|
||
this._globalSystems.splice(index, 1);
|
||
if (system.reset) {
|
||
system.reset();
|
||
}
|
||
logger$1.debug(`从World '${this.name}' 中移除全局System: ${system.name}`);
|
||
return true;
|
||
}
|
||
/**
|
||
* 获取全局System
|
||
*/
|
||
getGlobalSystem(type) {
|
||
for (const system of this._globalSystems) {
|
||
if (system instanceof type) {
|
||
return system;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
// ===== World生命周期 =====
|
||
/**
|
||
* 启动World
|
||
*/
|
||
start() {
|
||
if (this._isActive) {
|
||
return;
|
||
}
|
||
this._isActive = true;
|
||
for (const system of this._globalSystems) {
|
||
if (system.initialize) {
|
||
system.initialize();
|
||
}
|
||
}
|
||
logger$1.info(`启动World: ${this.name}`);
|
||
}
|
||
/**
|
||
* 停止World
|
||
*/
|
||
stop() {
|
||
if (!this._isActive) {
|
||
return;
|
||
}
|
||
for (const sceneId of this._activeScenes) {
|
||
this.setSceneActive(sceneId, false);
|
||
}
|
||
for (const system of this._globalSystems) {
|
||
if (system.reset) {
|
||
system.reset();
|
||
}
|
||
}
|
||
this._isActive = false;
|
||
logger$1.info(`停止World: ${this.name}`);
|
||
}
|
||
/**
|
||
* 更新World中的全局System
|
||
* 注意:此方法由Core.update()调用,不应直接调用
|
||
*/
|
||
updateGlobalSystems() {
|
||
if (!this._isActive) {
|
||
return;
|
||
}
|
||
for (const system of this._globalSystems) {
|
||
if (system.update) {
|
||
system.update();
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 更新World中的所有激活Scene
|
||
* 注意:此方法由Core.update()调用,不应直接调用
|
||
*/
|
||
updateScenes() {
|
||
if (!this._isActive) {
|
||
return;
|
||
}
|
||
for (const sceneId of this._activeScenes) {
|
||
const scene = this._scenes.get(sceneId);
|
||
if (scene && scene.update) {
|
||
scene.update();
|
||
}
|
||
}
|
||
if (this._config.autoCleanup && this.shouldAutoCleanup()) {
|
||
this.cleanup();
|
||
}
|
||
}
|
||
/**
|
||
* 销毁World
|
||
*/
|
||
destroy() {
|
||
logger$1.info(`销毁World: ${this.name}`);
|
||
this.stop();
|
||
const sceneIds = Array.from(this._scenes.keys());
|
||
for (const sceneId of sceneIds) {
|
||
this.removeScene(sceneId);
|
||
}
|
||
for (const system of this._globalSystems) {
|
||
if (system.destroy) {
|
||
system.destroy();
|
||
} else if (system.reset) {
|
||
system.reset();
|
||
}
|
||
}
|
||
this._globalSystems.length = 0;
|
||
this._scenes.clear();
|
||
this._activeScenes.clear();
|
||
}
|
||
// ===== 状态信息 =====
|
||
/**
|
||
* 获取World状态
|
||
*/
|
||
getStatus() {
|
||
return {
|
||
name: this.name,
|
||
isActive: this._isActive,
|
||
sceneCount: this._scenes.size,
|
||
activeSceneCount: this._activeScenes.size,
|
||
globalSystemCount: this._globalSystems.length,
|
||
createdAt: this._createdAt,
|
||
config: { ...this._config },
|
||
scenes: Array.from(this._scenes.keys()).map((sceneId) => ({
|
||
id: sceneId,
|
||
isActive: this._activeScenes.has(sceneId),
|
||
name: this._scenes.get(sceneId)?.name || sceneId
|
||
}))
|
||
};
|
||
}
|
||
/**
|
||
* 获取World统计信息
|
||
*/
|
||
getStats() {
|
||
const stats = {
|
||
totalEntities: 0,
|
||
totalSystems: this._globalSystems.length,
|
||
memoryUsage: 0,
|
||
performance: {
|
||
averageUpdateTime: 0,
|
||
maxUpdateTime: 0
|
||
}
|
||
};
|
||
for (const scene of this._scenes.values()) {
|
||
if (scene.entities) {
|
||
stats.totalEntities += scene.entities.count;
|
||
}
|
||
if (scene.systems) {
|
||
stats.totalSystems += scene.systems.length;
|
||
}
|
||
}
|
||
return stats;
|
||
}
|
||
// ===== 私有方法 =====
|
||
/**
|
||
* 检查是否应该执行自动清理
|
||
*/
|
||
shouldAutoCleanup() {
|
||
const currentTime = Date.now();
|
||
const cleanupThreshold = 5 * 60 * 1e3;
|
||
for (const [sceneId, scene] of this._scenes) {
|
||
if (!this._activeScenes.has(sceneId) && scene.entities && scene.entities.count === 0 && currentTime - this._createdAt > cleanupThreshold) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
/**
|
||
* 执行清理操作
|
||
*/
|
||
cleanup() {
|
||
const sceneIds = Array.from(this._scenes.keys());
|
||
const currentTime = Date.now();
|
||
const cleanupThreshold = 5 * 60 * 1e3;
|
||
for (const sceneId of sceneIds) {
|
||
const scene = this._scenes.get(sceneId);
|
||
if (scene && !this._activeScenes.has(sceneId) && scene.entities && scene.entities.count === 0 && currentTime - this._createdAt > cleanupThreshold) {
|
||
this.removeScene(sceneId);
|
||
logger$1.debug(`自动清理空Scene: ${sceneId} from World ${this.name}`);
|
||
}
|
||
}
|
||
}
|
||
// ===== 访问器 =====
|
||
/**
|
||
* 检查World是否激活
|
||
*/
|
||
get isActive() {
|
||
return this._isActive;
|
||
}
|
||
/**
|
||
* 获取Scene数量
|
||
*/
|
||
get sceneCount() {
|
||
return this._scenes.size;
|
||
}
|
||
/**
|
||
* 获取创建时间
|
||
*/
|
||
get createdAt() {
|
||
return this._createdAt;
|
||
}
|
||
}
|
||
const logger = createLogger("WorldManager");
|
||
class WorldManager {
|
||
constructor(config = {}) {
|
||
this._worlds = /* @__PURE__ */ new Map();
|
||
this._activeWorlds = /* @__PURE__ */ new Set();
|
||
this._cleanupTimer = null;
|
||
this._isRunning = false;
|
||
this._config = {
|
||
maxWorlds: 50,
|
||
autoCleanup: true,
|
||
cleanupInterval: 3e4,
|
||
// 30秒
|
||
debug: false,
|
||
...config
|
||
};
|
||
logger.info("WorldManager已初始化", {
|
||
maxWorlds: this._config.maxWorlds,
|
||
autoCleanup: this._config.autoCleanup,
|
||
cleanupInterval: this._config.cleanupInterval
|
||
});
|
||
this.startCleanupTimer();
|
||
}
|
||
/**
|
||
* 获取WorldManager单例实例
|
||
*/
|
||
static getInstance(config) {
|
||
if (!this._instance) {
|
||
this._instance = new WorldManager(config);
|
||
}
|
||
return this._instance;
|
||
}
|
||
/**
|
||
* 重置WorldManager实例(主要用于测试)
|
||
*/
|
||
static reset() {
|
||
if (this._instance) {
|
||
this._instance.destroy();
|
||
this._instance = null;
|
||
}
|
||
}
|
||
// ===== World管理 =====
|
||
/**
|
||
* 创建新World
|
||
*/
|
||
createWorld(worldId, config) {
|
||
if (!worldId || typeof worldId !== "string" || worldId.trim() === "") {
|
||
throw new Error("World ID不能为空");
|
||
}
|
||
if (this._worlds.has(worldId)) {
|
||
throw new Error(`World ID '${worldId}' 已存在`);
|
||
}
|
||
if (this._worlds.size >= this._config.maxWorlds) {
|
||
throw new Error(`已达到最大World数量限制: ${this._config.maxWorlds}`);
|
||
}
|
||
const worldConfig = {
|
||
name: worldId,
|
||
debug: this._config.debug,
|
||
...config
|
||
};
|
||
const world = new World(worldConfig);
|
||
this._worlds.set(worldId, world);
|
||
logger.info(`创建World: ${worldId}`, { config: worldConfig });
|
||
return world;
|
||
}
|
||
/**
|
||
* 移除World
|
||
*/
|
||
removeWorld(worldId) {
|
||
const world = this._worlds.get(worldId);
|
||
if (!world) {
|
||
return false;
|
||
}
|
||
if (this._activeWorlds.has(worldId)) {
|
||
this.setWorldActive(worldId, false);
|
||
}
|
||
world.destroy();
|
||
this._worlds.delete(worldId);
|
||
logger.info(`移除World: ${worldId}`);
|
||
return true;
|
||
}
|
||
/**
|
||
* 获取World
|
||
*/
|
||
getWorld(worldId) {
|
||
return this._worlds.get(worldId) || null;
|
||
}
|
||
/**
|
||
* 获取所有World ID
|
||
*/
|
||
getWorldIds() {
|
||
return Array.from(this._worlds.keys());
|
||
}
|
||
/**
|
||
* 获取所有World
|
||
*/
|
||
getAllWorlds() {
|
||
return Array.from(this._worlds.values());
|
||
}
|
||
/**
|
||
* 设置World激活状态
|
||
*/
|
||
setWorldActive(worldId, active) {
|
||
const world = this._worlds.get(worldId);
|
||
if (!world) {
|
||
logger.warn(`World '${worldId}' 不存在`);
|
||
return;
|
||
}
|
||
if (active) {
|
||
this._activeWorlds.add(worldId);
|
||
world.start();
|
||
logger.debug(`激活World: ${worldId}`);
|
||
} else {
|
||
this._activeWorlds.delete(worldId);
|
||
world.stop();
|
||
logger.debug(`停用World: ${worldId}`);
|
||
}
|
||
}
|
||
/**
|
||
* 检查World是否激活
|
||
*/
|
||
isWorldActive(worldId) {
|
||
return this._activeWorlds.has(worldId);
|
||
}
|
||
// ===== 批量操作 =====
|
||
/**
|
||
* 获取所有激活的World
|
||
* 注意:此方法供Core.update()使用
|
||
*/
|
||
getActiveWorlds() {
|
||
const activeWorlds = [];
|
||
for (const worldId of this._activeWorlds) {
|
||
const world = this._worlds.get(worldId);
|
||
if (world) {
|
||
activeWorlds.push(world);
|
||
}
|
||
}
|
||
return activeWorlds;
|
||
}
|
||
/**
|
||
* 启动所有World
|
||
*/
|
||
startAll() {
|
||
this._isRunning = true;
|
||
for (const worldId of this._worlds.keys()) {
|
||
this.setWorldActive(worldId, true);
|
||
}
|
||
logger.info("启动所有World");
|
||
}
|
||
/**
|
||
* 停止所有World
|
||
*/
|
||
stopAll() {
|
||
this._isRunning = false;
|
||
for (const worldId of this._activeWorlds) {
|
||
this.setWorldActive(worldId, false);
|
||
}
|
||
logger.info("停止所有World");
|
||
}
|
||
/**
|
||
* 查找满足条件的World
|
||
*/
|
||
findWorlds(predicate) {
|
||
const results = [];
|
||
for (const world of this._worlds.values()) {
|
||
if (predicate(world)) {
|
||
results.push(world);
|
||
}
|
||
}
|
||
return results;
|
||
}
|
||
/**
|
||
* 根据名称查找World
|
||
*/
|
||
findWorldByName(name) {
|
||
for (const world of this._worlds.values()) {
|
||
if (world.name === name) {
|
||
return world;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
// ===== 统计和监控 =====
|
||
/**
|
||
* 获取WorldManager统计信息
|
||
*/
|
||
getStats() {
|
||
const stats = {
|
||
totalWorlds: this._worlds.size,
|
||
activeWorlds: this._activeWorlds.size,
|
||
totalScenes: 0,
|
||
totalEntities: 0,
|
||
totalSystems: 0,
|
||
memoryUsage: 0,
|
||
isRunning: this._isRunning,
|
||
config: { ...this._config },
|
||
worlds: []
|
||
};
|
||
for (const [worldId, world] of this._worlds) {
|
||
const worldStats = world.getStats();
|
||
stats.totalScenes += worldStats.totalSystems;
|
||
stats.totalEntities += worldStats.totalEntities;
|
||
stats.totalSystems += worldStats.totalSystems;
|
||
stats.worlds.push({
|
||
id: worldId,
|
||
name: world.name,
|
||
isActive: this._activeWorlds.has(worldId),
|
||
sceneCount: world.sceneCount,
|
||
...worldStats
|
||
});
|
||
}
|
||
return stats;
|
||
}
|
||
/**
|
||
* 获取详细状态信息
|
||
*/
|
||
getDetailedStatus() {
|
||
return {
|
||
...this.getStats(),
|
||
worlds: Array.from(this._worlds.entries()).map(([worldId, world]) => ({
|
||
id: worldId,
|
||
isActive: this._activeWorlds.has(worldId),
|
||
status: world.getStatus()
|
||
}))
|
||
};
|
||
}
|
||
// ===== 生命周期管理 =====
|
||
/**
|
||
* 清理空World
|
||
*/
|
||
cleanup() {
|
||
const worldsToRemove = [];
|
||
for (const [worldId, world] of this._worlds) {
|
||
if (this.shouldCleanupWorld(world)) {
|
||
worldsToRemove.push(worldId);
|
||
}
|
||
}
|
||
for (const worldId of worldsToRemove) {
|
||
this.removeWorld(worldId);
|
||
}
|
||
if (worldsToRemove.length > 0) {
|
||
logger.debug(`清理了 ${worldsToRemove.length} 个World`);
|
||
}
|
||
return worldsToRemove.length;
|
||
}
|
||
/**
|
||
* 销毁WorldManager
|
||
*/
|
||
destroy() {
|
||
logger.info("正在销毁WorldManager...");
|
||
this.stopCleanupTimer();
|
||
this.stopAll();
|
||
const worldIds = Array.from(this._worlds.keys());
|
||
for (const worldId of worldIds) {
|
||
this.removeWorld(worldId);
|
||
}
|
||
this._worlds.clear();
|
||
this._activeWorlds.clear();
|
||
this._isRunning = false;
|
||
logger.info("WorldManager已销毁");
|
||
}
|
||
// ===== 私有方法 =====
|
||
/**
|
||
* 启动清理定时器
|
||
*/
|
||
startCleanupTimer() {
|
||
if (!this._config.autoCleanup || this._cleanupTimer) {
|
||
return;
|
||
}
|
||
this._cleanupTimer = setInterval(() => {
|
||
this.cleanup();
|
||
}, this._config.cleanupInterval);
|
||
logger.debug(`启动World清理定时器,间隔: ${this._config.cleanupInterval}ms`);
|
||
}
|
||
/**
|
||
* 停止清理定时器
|
||
*/
|
||
stopCleanupTimer() {
|
||
if (this._cleanupTimer) {
|
||
clearInterval(this._cleanupTimer);
|
||
this._cleanupTimer = null;
|
||
logger.debug("停止World清理定时器");
|
||
}
|
||
}
|
||
/**
|
||
* 判断World是否应该被清理
|
||
*/
|
||
shouldCleanupWorld(world) {
|
||
if (world.isActive) {
|
||
return false;
|
||
}
|
||
if (world.sceneCount === 0) {
|
||
const age = Date.now() - world.createdAt;
|
||
return age > 10 * 60 * 1e3;
|
||
}
|
||
const allScenes = world.getAllScenes();
|
||
const hasEntities = allScenes.some((scene) => scene.entities && scene.entities.count > 0);
|
||
if (!hasEntities) {
|
||
const age = Date.now() - world.createdAt;
|
||
return age > 10 * 60 * 1e3;
|
||
}
|
||
return false;
|
||
}
|
||
// ===== 访问器 =====
|
||
/**
|
||
* 获取World总数
|
||
*/
|
||
get worldCount() {
|
||
return this._worlds.size;
|
||
}
|
||
/**
|
||
* 获取激活World数量
|
||
*/
|
||
get activeWorldCount() {
|
||
return this._activeWorlds.size;
|
||
}
|
||
/**
|
||
* 检查是否正在运行
|
||
*/
|
||
get isRunning() {
|
||
return this._isRunning;
|
||
}
|
||
/**
|
||
* 获取配置
|
||
*/
|
||
get config() {
|
||
return { ...this._config };
|
||
}
|
||
}
|
||
WorldManager._instance = null;
|
||
class Bits {
|
||
/**
|
||
* 构造函数,创建位集合
|
||
* @param initialValue 初始值,可以是BitMask64Data对象、数字或字符串
|
||
*/
|
||
constructor(initialValue) {
|
||
if (initialValue && typeof initialValue === "object") {
|
||
this._value = BitMask64Utils.clone(initialValue);
|
||
} else if (typeof initialValue === "number") {
|
||
this._value = BitMask64Utils.fromNumber(initialValue);
|
||
} else if (typeof initialValue === "string") {
|
||
const num = parseInt(initialValue, 10);
|
||
this._value = BitMask64Utils.fromNumber(num);
|
||
} else {
|
||
this._value = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||
}
|
||
}
|
||
/**
|
||
* 设置指定位为1
|
||
* @param index 位索引,范围 [0, 63]
|
||
* @throws 当位索引为负数或超过64位限制时抛出错误
|
||
*/
|
||
set(index) {
|
||
if (index < 0) {
|
||
throw new Error("Bit index cannot be negative");
|
||
}
|
||
if (index >= 64) {
|
||
throw new Error("Bit index exceeds 64-bit limit. ECS framework supports max 64 component types.");
|
||
}
|
||
BitMask64Utils.setBit(this._value, index);
|
||
}
|
||
/**
|
||
* 清除指定位为0
|
||
* @param index 位索引,范围 [0, 63]
|
||
* @throws 当位索引为负数时抛出错误
|
||
*/
|
||
clear(index) {
|
||
if (index < 0) {
|
||
throw new Error("Bit index cannot be negative");
|
||
}
|
||
if (index >= 64) {
|
||
return;
|
||
}
|
||
BitMask64Utils.clearBit(this._value, index);
|
||
}
|
||
/**
|
||
* 获取指定位的值
|
||
* @param index 位索引
|
||
* @returns 如果位被设置为1则返回true,否则返回false
|
||
*/
|
||
get(index) {
|
||
if (index < 0 || index >= 64) {
|
||
return false;
|
||
}
|
||
const mask = BitMask64Utils.create(index);
|
||
return BitMask64Utils.hasAny(this._value, mask);
|
||
}
|
||
/**
|
||
* 检查是否包含另一个位集合的所有位
|
||
* @param other 另一个位集合
|
||
* @returns 如果包含other的所有设置位则返回true
|
||
*/
|
||
containsAll(other) {
|
||
return BitMask64Utils.hasAll(this._value, other._value);
|
||
}
|
||
/**
|
||
* 检查是否与另一个位集合有交集
|
||
* @param other 另一个位集合
|
||
* @returns 如果有共同的设置位则返回true
|
||
*/
|
||
intersects(other) {
|
||
return BitMask64Utils.hasAny(this._value, other._value);
|
||
}
|
||
/**
|
||
* 检查是否与另一个位集合没有交集
|
||
* @param other 另一个位集合
|
||
* @returns 如果没有共同的设置位则返回true
|
||
*/
|
||
excludes(other) {
|
||
return BitMask64Utils.hasNone(this._value, other._value);
|
||
}
|
||
/**
|
||
* 清除所有位为0
|
||
*/
|
||
clearAll() {
|
||
BitMask64Utils.clear(this._value);
|
||
}
|
||
/**
|
||
* 检查位集合是否为空
|
||
* @returns 如果所有位都为0则返回true
|
||
*/
|
||
isEmpty() {
|
||
return BitMask64Utils.isZero(this._value);
|
||
}
|
||
/**
|
||
* 计算设置为1的位数
|
||
* @returns 设置位的总数
|
||
*/
|
||
cardinality() {
|
||
return BitMask64Utils.popCount(this._value);
|
||
}
|
||
/**
|
||
* 与另一个位集合执行按位与操作
|
||
* @param other 另一个位集合
|
||
* @returns 新的位集合,包含按位与的结果
|
||
*/
|
||
and(other) {
|
||
const result = new Bits();
|
||
BitMask64Utils.copy(this._value, result._value);
|
||
BitMask64Utils.andInPlace(result._value, other._value);
|
||
return result;
|
||
}
|
||
/**
|
||
* 与另一个位集合执行按位或操作
|
||
* @param other 另一个位集合
|
||
* @returns 新的位集合,包含按位或的结果
|
||
*/
|
||
or(other) {
|
||
const result = new Bits();
|
||
BitMask64Utils.copy(this._value, result._value);
|
||
BitMask64Utils.orInPlace(result._value, other._value);
|
||
return result;
|
||
}
|
||
/**
|
||
* 与另一个位集合执行按位异或操作
|
||
* @param other 另一个位集合
|
||
* @returns 新的位集合,包含按位异或的结果
|
||
*/
|
||
xor(other) {
|
||
const result = new Bits();
|
||
BitMask64Utils.copy(this._value, result._value);
|
||
BitMask64Utils.xorInPlace(result._value, other._value);
|
||
return result;
|
||
}
|
||
/**
|
||
* 执行按位取反操作
|
||
* @param maxBits 最大位数,默认为64
|
||
* @returns 新的位集合,包含按位取反的结果
|
||
*/
|
||
not(maxBits = 64) {
|
||
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.lo = ~result._value.lo & mask;
|
||
result._value.hi = 0;
|
||
} else {
|
||
result._value.lo = ~result._value.lo;
|
||
if (maxBits < 64) {
|
||
const remainingBits = maxBits - 32;
|
||
const mask = (1 << remainingBits) - 1;
|
||
result._value.hi = ~result._value.hi & mask;
|
||
} else {
|
||
result._value.hi = ~result._value.hi;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 从另一个位集合复制值
|
||
* @param other 源位集合
|
||
*/
|
||
copyFrom(other) {
|
||
BitMask64Utils.copy(other._value, this._value);
|
||
}
|
||
/**
|
||
* 创建当前位集合的深拷贝
|
||
* @returns 新的位集合,内容与当前位集合相同
|
||
*/
|
||
clone() {
|
||
return new Bits(this._value);
|
||
}
|
||
/**
|
||
* 获取内部的64位掩码数据
|
||
* @returns 内部存储的BitMask64Data对象
|
||
*/
|
||
getValue() {
|
||
return this._value;
|
||
}
|
||
/**
|
||
* 设置位集合的值
|
||
* @param value 新值,可以是BitMask64Data对象、数字或字符串
|
||
*/
|
||
setValue(value) {
|
||
if (typeof value === "object") {
|
||
BitMask64Utils.copy(value, this._value);
|
||
} else if (typeof value === "number") {
|
||
this._value = BitMask64Utils.fromNumber(value);
|
||
} else {
|
||
const num = parseInt(value, 10);
|
||
this._value = BitMask64Utils.fromNumber(num);
|
||
}
|
||
}
|
||
/**
|
||
* 将位集合转换为可读字符串
|
||
* @returns 格式为"Bits[index1, index2, ...]"的字符串
|
||
*/
|
||
toString() {
|
||
const bits = [];
|
||
for (let i = 0; i < 64; i++) {
|
||
if (this.get(i)) {
|
||
bits.push(i.toString());
|
||
}
|
||
}
|
||
return `Bits[${bits.join(", ")}]`;
|
||
}
|
||
/**
|
||
* 将位集合转换为二进制字符串
|
||
* @param maxBits 最大位数,默认为64
|
||
* @returns 二进制字符串表示,每8位用空格分隔
|
||
*/
|
||
toBinaryString(maxBits = 64) {
|
||
if (maxBits > 64)
|
||
maxBits = 64;
|
||
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 十六进制字符串表示,带0x前缀
|
||
*/
|
||
toHexString() {
|
||
return BitMask64Utils.toString(this._value, 16);
|
||
}
|
||
/**
|
||
* 从二进制字符串创建位集合
|
||
* @param binaryString 二进制字符串,可以包含空格
|
||
* @returns 新的位集合对象
|
||
*/
|
||
static fromBinaryString(binaryString) {
|
||
const cleanString = binaryString.replace(/\s/g, "");
|
||
let data;
|
||
if (cleanString.length <= 32) {
|
||
const num = parseInt(cleanString, 2);
|
||
data = { lo: num >>> 0, hi: 0 };
|
||
} else {
|
||
const loBits = cleanString.substring(cleanString.length - 32);
|
||
const hiBits = cleanString.substring(0, cleanString.length - 32);
|
||
const lo = parseInt(loBits, 2);
|
||
const hi = parseInt(hiBits, 2);
|
||
data = { lo: lo >>> 0, hi: hi >>> 0 };
|
||
}
|
||
return new Bits(data);
|
||
}
|
||
/**
|
||
* 从十六进制字符串创建位集合
|
||
* @param hexString 十六进制字符串,可以带或不带0x前缀
|
||
* @returns 新的位集合对象
|
||
*/
|
||
static fromHexString(hexString) {
|
||
const cleanString = hexString.replace(/^0x/i, "");
|
||
let data;
|
||
if (cleanString.length <= 8) {
|
||
const num = parseInt(cleanString, 16);
|
||
data = { lo: num >>> 0, hi: 0 };
|
||
} else {
|
||
const loBits = cleanString.substring(cleanString.length - 8);
|
||
const hiBits = cleanString.substring(0, cleanString.length - 8);
|
||
const lo = parseInt(loBits, 16);
|
||
const hi = parseInt(hiBits, 16);
|
||
data = { lo: lo >>> 0, hi: hi >>> 0 };
|
||
}
|
||
return new Bits(data);
|
||
}
|
||
/**
|
||
* 检查是否与另一个位集合相等
|
||
* @param other 另一个位集合
|
||
* @returns 如果两个位集合完全相同则返回true
|
||
*/
|
||
equals(other) {
|
||
return BitMask64Utils.equals(this._value, other._value);
|
||
}
|
||
/**
|
||
* 获取最高位设置位的索引
|
||
* @returns 最高位设置位的索引,如果位集合为空则返回-1
|
||
*/
|
||
getHighestBitIndex() {
|
||
if (BitMask64Utils.isZero(this._value)) {
|
||
return -1;
|
||
}
|
||
if (this._value.hi !== 0) {
|
||
for (let i = 31; i >= 0; i--) {
|
||
if ((this._value.hi & 1 << i) !== 0) {
|
||
return i + 32;
|
||
}
|
||
}
|
||
}
|
||
for (let i = 31; i >= 0; i--) {
|
||
if ((this._value.lo & 1 << i) !== 0) {
|
||
return i;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
/**
|
||
* 获取最低位设置位的索引
|
||
* @returns 最低位设置位的索引,如果位集合为空则返回-1
|
||
*/
|
||
getLowestBitIndex() {
|
||
if (BitMask64Utils.isZero(this._value)) {
|
||
return -1;
|
||
}
|
||
for (let i = 0; i < 32; i++) {
|
||
if ((this._value.lo & 1 << i) !== 0) {
|
||
return i;
|
||
}
|
||
}
|
||
for (let i = 0; i < 32; i++) {
|
||
if ((this._value.hi & 1 << i) !== 0) {
|
||
return i + 32;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
}
|
||
class ComponentTypeManager {
|
||
/**
|
||
* 获取单例实例
|
||
*/
|
||
static get instance() {
|
||
if (!ComponentTypeManager._instance) {
|
||
ComponentTypeManager._instance = new ComponentTypeManager();
|
||
}
|
||
return ComponentTypeManager._instance;
|
||
}
|
||
constructor() {
|
||
this._componentTypes = /* @__PURE__ */ new Map();
|
||
this._typeNames = /* @__PURE__ */ new Map();
|
||
this._nextTypeId = 0;
|
||
}
|
||
/**
|
||
* 获取组件类型的ID
|
||
* @param componentType 组件类型构造函数
|
||
* @returns 组件类型ID
|
||
*/
|
||
getTypeId(componentType) {
|
||
let typeId = this._componentTypes.get(componentType);
|
||
if (typeId === void 0) {
|
||
typeId = this._nextTypeId++;
|
||
this._componentTypes.set(componentType, typeId);
|
||
this._typeNames.set(typeId, getComponentTypeName(componentType));
|
||
}
|
||
return typeId;
|
||
}
|
||
/**
|
||
* 获取组件类型名称
|
||
* @param typeId 组件类型ID
|
||
* @returns 组件类型名称
|
||
*/
|
||
getTypeName(typeId) {
|
||
return this._typeNames.get(typeId) || "Unknown";
|
||
}
|
||
/**
|
||
* 创建包含指定组件类型的Bits对象
|
||
* @param componentTypes 组件类型构造函数数组
|
||
* @returns Bits对象
|
||
*/
|
||
createBits(...componentTypes) {
|
||
const bits = new Bits();
|
||
for (const componentType of componentTypes) {
|
||
const typeId = this.getTypeId(componentType);
|
||
bits.set(typeId);
|
||
}
|
||
return bits;
|
||
}
|
||
/**
|
||
* 获取实体的组件位掩码
|
||
* @param components 组件数组
|
||
* @returns Bits对象
|
||
*/
|
||
getEntityBits(components) {
|
||
const bits = new Bits();
|
||
for (const component of components) {
|
||
const typeId = this.getTypeId(component.constructor);
|
||
bits.set(typeId);
|
||
}
|
||
return bits;
|
||
}
|
||
/**
|
||
* 重置管理器(主要用于测试)
|
||
*/
|
||
reset() {
|
||
this._componentTypes.clear();
|
||
this._typeNames.clear();
|
||
this._nextTypeId = 0;
|
||
}
|
||
/**
|
||
* 获取已注册的组件类型数量
|
||
*/
|
||
get registeredTypeCount() {
|
||
return this._componentTypes.size;
|
||
}
|
||
}
|
||
class EntityDataCollector {
|
||
/**
|
||
* 收集实体数据
|
||
* @param scene 场景实例
|
||
*/
|
||
collectEntityData(scene) {
|
||
if (!scene) {
|
||
return this.getEmptyEntityDebugData();
|
||
}
|
||
const entityList = scene.entities;
|
||
if (!entityList) {
|
||
return this.getEmptyEntityDebugData();
|
||
}
|
||
let stats;
|
||
try {
|
||
stats = entityList.getStats ? entityList.getStats() : this.calculateFallbackEntityStats(entityList);
|
||
} catch (error) {
|
||
return {
|
||
totalEntities: 0,
|
||
activeEntities: 0,
|
||
pendingAdd: 0,
|
||
pendingRemove: 0,
|
||
entitiesPerArchetype: [],
|
||
topEntitiesByComponents: [],
|
||
entityHierarchy: [],
|
||
entityDetailsMap: {}
|
||
};
|
||
}
|
||
const archetypeData = this.collectArchetypeData(scene);
|
||
return {
|
||
totalEntities: stats.totalEntities,
|
||
activeEntities: stats.activeEntities,
|
||
pendingAdd: stats.pendingAdd || 0,
|
||
pendingRemove: stats.pendingRemove || 0,
|
||
entitiesPerArchetype: archetypeData.distribution,
|
||
topEntitiesByComponents: archetypeData.topEntities,
|
||
entityHierarchy: [],
|
||
entityDetailsMap: {}
|
||
};
|
||
}
|
||
/**
|
||
* 获取原始实体列表
|
||
* @param scene 场景实例
|
||
*/
|
||
getRawEntityList(scene) {
|
||
if (!scene)
|
||
return [];
|
||
const entityList = scene.entities;
|
||
if (!entityList?.buffer)
|
||
return [];
|
||
return entityList.buffer.map((entity) => ({
|
||
id: entity.id,
|
||
name: entity.name || `Entity_${entity.id}`,
|
||
active: entity.active !== false,
|
||
enabled: entity.enabled !== false,
|
||
activeInHierarchy: entity.activeInHierarchy !== false,
|
||
componentCount: entity.components.length,
|
||
componentTypes: entity.components.map((component) => getComponentInstanceTypeName(component)),
|
||
parentId: entity.parent?.id || null,
|
||
childIds: entity.children?.map((child) => child.id) || [],
|
||
depth: entity.getDepth ? entity.getDepth() : 0,
|
||
tag: entity.tag || 0,
|
||
updateOrder: entity.updateOrder || 0
|
||
}));
|
||
}
|
||
/**
|
||
* 获取实体详细信息
|
||
* @param entityId 实体ID
|
||
* @param scene 场景实例
|
||
*/
|
||
getEntityDetails(entityId, scene) {
|
||
try {
|
||
if (!scene)
|
||
return null;
|
||
const entityList = scene.entities;
|
||
if (!entityList?.buffer)
|
||
return null;
|
||
const entity = entityList.buffer.find((e) => e.id === entityId);
|
||
if (!entity)
|
||
return null;
|
||
const baseDebugInfo = entity.getDebugInfo ? entity.getDebugInfo() : this.buildFallbackEntityInfo(entity, scene);
|
||
const componentDetails = this.extractComponentDetails(entity.components);
|
||
const sceneInfo = this.getSceneInfo(scene);
|
||
return {
|
||
...baseDebugInfo,
|
||
scene: sceneInfo.name,
|
||
sceneName: sceneInfo.name,
|
||
sceneType: sceneInfo.type,
|
||
parentName: entity.parent?.name || null,
|
||
components: componentDetails || [],
|
||
componentCount: entity.components?.length || 0,
|
||
componentTypes: entity.components?.map((comp) => getComponentInstanceTypeName(comp)) || []
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
error: `获取实体详情失败: ${error instanceof Error ? error.message : String(error)}`,
|
||
scene: "获取失败",
|
||
components: [],
|
||
componentCount: 0,
|
||
componentTypes: []
|
||
};
|
||
}
|
||
}
|
||
getSceneInfo(scene) {
|
||
let sceneName = "当前场景";
|
||
let sceneType = "Scene";
|
||
try {
|
||
if (scene.name && typeof scene.name === "string" && scene.name.trim()) {
|
||
sceneName = scene.name.trim();
|
||
} else if (scene.constructor && scene.constructor.name) {
|
||
sceneName = scene.constructor.name;
|
||
sceneType = scene.constructor.name;
|
||
} else if (scene._name && typeof scene._name === "string" && scene._name.trim()) {
|
||
sceneName = scene._name.trim();
|
||
} else {
|
||
const sceneClassName = Object.getPrototypeOf(scene)?.constructor?.name;
|
||
if (sceneClassName && sceneClassName !== "Object") {
|
||
sceneName = sceneClassName;
|
||
sceneType = sceneClassName;
|
||
}
|
||
}
|
||
} catch (error) {
|
||
sceneName = "场景名获取失败";
|
||
}
|
||
return { name: sceneName, type: sceneType };
|
||
}
|
||
/**
|
||
* 收集实体数据(包含内存信息)
|
||
* @param scene 场景实例
|
||
*/
|
||
collectEntityDataWithMemory(scene) {
|
||
if (!scene) {
|
||
return this.getEmptyEntityDebugData();
|
||
}
|
||
const entityList = scene.entities;
|
||
if (!entityList) {
|
||
return this.getEmptyEntityDebugData();
|
||
}
|
||
let stats;
|
||
try {
|
||
stats = entityList.getStats ? entityList.getStats() : this.calculateFallbackEntityStats(entityList);
|
||
} catch (error) {
|
||
return {
|
||
totalEntities: 0,
|
||
activeEntities: 0,
|
||
pendingAdd: 0,
|
||
pendingRemove: 0,
|
||
entitiesPerArchetype: [],
|
||
topEntitiesByComponents: [],
|
||
entityHierarchy: [],
|
||
entityDetailsMap: {}
|
||
};
|
||
}
|
||
const archetypeData = this.collectArchetypeDataWithMemory(scene);
|
||
return {
|
||
totalEntities: stats.totalEntities,
|
||
activeEntities: stats.activeEntities,
|
||
pendingAdd: stats.pendingAdd || 0,
|
||
pendingRemove: stats.pendingRemove || 0,
|
||
entitiesPerArchetype: archetypeData.distribution,
|
||
topEntitiesByComponents: archetypeData.topEntities,
|
||
entityHierarchy: this.buildEntityHierarchyTree(entityList),
|
||
entityDetailsMap: this.buildEntityDetailsMap(entityList, scene)
|
||
};
|
||
}
|
||
collectArchetypeData(scene) {
|
||
if (scene && scene.archetypeSystem && typeof scene.archetypeSystem.getAllArchetypes === "function") {
|
||
return this.extractArchetypeStatistics(scene.archetypeSystem);
|
||
}
|
||
const entityContainer = { entities: scene.entities?.buffer || [] };
|
||
return {
|
||
distribution: this.getArchetypeDistributionFast(entityContainer),
|
||
topEntities: this.getTopEntitiesByComponentsFast(entityContainer)
|
||
};
|
||
}
|
||
getArchetypeDistributionFast(entityContainer) {
|
||
const distribution = /* @__PURE__ */ new Map();
|
||
if (entityContainer && entityContainer.entities) {
|
||
entityContainer.entities.forEach((entity) => {
|
||
const componentTypes = entity.components?.map((comp) => getComponentInstanceTypeName(comp)) || [];
|
||
const signature = componentTypes.length > 0 ? componentTypes.sort().join(", ") : "无组件";
|
||
const existing = distribution.get(signature);
|
||
if (existing) {
|
||
existing.count++;
|
||
} else {
|
||
distribution.set(signature, { count: 1, componentTypes });
|
||
}
|
||
});
|
||
}
|
||
return Array.from(distribution.entries()).map(([signature, data]) => ({
|
||
signature,
|
||
count: data.count,
|
||
memory: 0
|
||
})).sort((a, b) => b.count - a.count).slice(0, 20);
|
||
}
|
||
getTopEntitiesByComponentsFast(entityContainer) {
|
||
if (!entityContainer || !entityContainer.entities) {
|
||
return [];
|
||
}
|
||
return entityContainer.entities.map((entity) => ({
|
||
id: entity.id.toString(),
|
||
name: entity.name || `Entity_${entity.id}`,
|
||
componentCount: entity.components?.length || 0,
|
||
memory: 0
|
||
})).sort((a, b) => b.componentCount - a.componentCount).slice(0, 10);
|
||
}
|
||
collectArchetypeDataWithMemory(scene) {
|
||
if (scene && scene.archetypeSystem && typeof scene.archetypeSystem.getAllArchetypes === "function") {
|
||
return this.extractArchetypeStatisticsWithMemory(scene.archetypeSystem);
|
||
}
|
||
const entityContainer = { entities: scene.entities?.buffer || [] };
|
||
return {
|
||
distribution: this.getArchetypeDistributionWithMemory(entityContainer),
|
||
topEntities: this.getTopEntitiesByComponentsWithMemory(entityContainer)
|
||
};
|
||
}
|
||
extractArchetypeStatistics(archetypeSystem) {
|
||
const archetypes = archetypeSystem.getAllArchetypes();
|
||
const distribution = [];
|
||
const topEntities = [];
|
||
archetypes.forEach((archetype) => {
|
||
const signature = archetype.componentTypes?.map((type) => type.name).join(",") || "Unknown";
|
||
const entityCount = archetype.entities?.length || 0;
|
||
distribution.push({
|
||
signature,
|
||
count: entityCount,
|
||
memory: 0
|
||
});
|
||
if (archetype.entities) {
|
||
archetype.entities.slice(0, 5).forEach((entity) => {
|
||
topEntities.push({
|
||
id: entity.id.toString(),
|
||
name: entity.name || `Entity_${entity.id}`,
|
||
componentCount: entity.components?.length || 0,
|
||
memory: 0
|
||
});
|
||
});
|
||
}
|
||
});
|
||
distribution.sort((a, b) => b.count - a.count);
|
||
topEntities.sort((a, b) => b.componentCount - a.componentCount);
|
||
return { distribution, topEntities };
|
||
}
|
||
extractArchetypeStatisticsWithMemory(archetypeSystem) {
|
||
const archetypes = archetypeSystem.getAllArchetypes();
|
||
const distribution = [];
|
||
const topEntities = [];
|
||
archetypes.forEach((archetype) => {
|
||
const signature = archetype.componentTypes?.map((type) => type.name).join(",") || "Unknown";
|
||
const entityCount = archetype.entities?.length || 0;
|
||
let actualMemory = 0;
|
||
if (archetype.entities && archetype.entities.length > 0) {
|
||
const sampleSize = Math.min(5, archetype.entities.length);
|
||
let sampleMemory = 0;
|
||
for (let i = 0; i < sampleSize; i++) {
|
||
sampleMemory += this.estimateEntityMemoryUsage(archetype.entities[i]);
|
||
}
|
||
actualMemory = sampleMemory / sampleSize * entityCount;
|
||
}
|
||
distribution.push({
|
||
signature,
|
||
count: entityCount,
|
||
memory: actualMemory
|
||
});
|
||
if (archetype.entities) {
|
||
archetype.entities.slice(0, 5).forEach((entity) => {
|
||
topEntities.push({
|
||
id: entity.id.toString(),
|
||
name: entity.name || `Entity_${entity.id}`,
|
||
componentCount: entity.components?.length || 0,
|
||
memory: this.estimateEntityMemoryUsage(entity)
|
||
});
|
||
});
|
||
}
|
||
});
|
||
distribution.sort((a, b) => b.count - a.count);
|
||
topEntities.sort((a, b) => b.componentCount - a.componentCount);
|
||
return { distribution, topEntities };
|
||
}
|
||
getArchetypeDistribution(entityContainer) {
|
||
const distribution = /* @__PURE__ */ new Map();
|
||
if (entityContainer && entityContainer.entities) {
|
||
entityContainer.entities.forEach((entity) => {
|
||
const signature = entity.componentMask?.toString() || "0";
|
||
const existing = distribution.get(signature);
|
||
distribution.set(signature, (existing || 0) + 1);
|
||
});
|
||
}
|
||
return Array.from(distribution.entries()).map(([signature, count]) => ({ signature, count, memory: 0 })).sort((a, b) => b.count - a.count);
|
||
}
|
||
getArchetypeDistributionWithMemory(entityContainer) {
|
||
const distribution = /* @__PURE__ */ new Map();
|
||
if (entityContainer && entityContainer.entities) {
|
||
entityContainer.entities.forEach((entity) => {
|
||
const componentTypes = entity.components?.map((comp) => getComponentInstanceTypeName(comp)) || [];
|
||
const signature = componentTypes.length > 0 ? componentTypes.sort().join(", ") : "无组件";
|
||
const existing = distribution.get(signature);
|
||
let memory = this.estimateEntityMemoryUsage(entity);
|
||
if (isNaN(memory) || memory < 0) {
|
||
memory = 0;
|
||
}
|
||
if (existing) {
|
||
existing.count++;
|
||
existing.memory += memory;
|
||
} else {
|
||
distribution.set(signature, { count: 1, memory, componentTypes });
|
||
}
|
||
});
|
||
}
|
||
return Array.from(distribution.entries()).map(([signature, data]) => ({
|
||
signature,
|
||
count: data.count,
|
||
memory: isNaN(data.memory) ? 0 : data.memory
|
||
})).sort((a, b) => b.count - a.count);
|
||
}
|
||
getTopEntitiesByComponents(entityContainer) {
|
||
if (!entityContainer || !entityContainer.entities) {
|
||
return [];
|
||
}
|
||
return entityContainer.entities.map((entity) => ({
|
||
id: entity.id.toString(),
|
||
name: entity.name || `Entity_${entity.id}`,
|
||
componentCount: entity.components?.length || 0,
|
||
memory: 0
|
||
})).sort((a, b) => b.componentCount - a.componentCount);
|
||
}
|
||
getTopEntitiesByComponentsWithMemory(entityContainer) {
|
||
if (!entityContainer || !entityContainer.entities) {
|
||
return [];
|
||
}
|
||
return entityContainer.entities.map((entity) => ({
|
||
id: entity.id.toString(),
|
||
name: entity.name || `Entity_${entity.id}`,
|
||
componentCount: entity.components?.length || 0,
|
||
memory: this.estimateEntityMemoryUsage(entity)
|
||
})).sort((a, b) => b.componentCount - a.componentCount);
|
||
}
|
||
getEmptyEntityDebugData() {
|
||
return {
|
||
totalEntities: 0,
|
||
activeEntities: 0,
|
||
pendingAdd: 0,
|
||
pendingRemove: 0,
|
||
entitiesPerArchetype: [],
|
||
topEntitiesByComponents: [],
|
||
entityHierarchy: [],
|
||
entityDetailsMap: {}
|
||
};
|
||
}
|
||
calculateFallbackEntityStats(entityList) {
|
||
const allEntities = entityList.buffer || [];
|
||
const activeEntities = allEntities.filter((entity) => entity.enabled && !entity._isDestroyed);
|
||
return {
|
||
totalEntities: allEntities.length,
|
||
activeEntities: activeEntities.length,
|
||
pendingAdd: 0,
|
||
pendingRemove: 0,
|
||
averageComponentsPerEntity: activeEntities.length > 0 ? allEntities.reduce((sum, e) => sum + (e.components?.length || 0), 0) / activeEntities.length : 0
|
||
};
|
||
}
|
||
estimateEntityMemoryUsage(entity) {
|
||
try {
|
||
let totalSize = 0;
|
||
const entitySize = this.calculateObjectSize(entity, ["components", "children", "parent"]);
|
||
if (!isNaN(entitySize) && entitySize > 0) {
|
||
totalSize += entitySize;
|
||
}
|
||
if (entity.components && Array.isArray(entity.components)) {
|
||
entity.components.forEach((component) => {
|
||
const componentSize = this.calculateObjectSize(component, ["entity"]);
|
||
if (!isNaN(componentSize) && componentSize > 0) {
|
||
totalSize += componentSize;
|
||
}
|
||
});
|
||
}
|
||
return isNaN(totalSize) || totalSize < 0 ? 0 : totalSize;
|
||
} catch (error) {
|
||
return 0;
|
||
}
|
||
}
|
||
calculateObjectSize(obj, excludeKeys = []) {
|
||
if (!obj || typeof obj !== "object")
|
||
return 0;
|
||
const visited = /* @__PURE__ */ new WeakSet();
|
||
const maxDepth = 2;
|
||
const calculate = (item, depth = 0) => {
|
||
if (!item || typeof item !== "object" || depth >= maxDepth) {
|
||
return 0;
|
||
}
|
||
if (visited.has(item))
|
||
return 0;
|
||
visited.add(item);
|
||
let itemSize = 32;
|
||
try {
|
||
const keys = Object.keys(item);
|
||
const maxKeys = Math.min(keys.length, 20);
|
||
for (let i = 0; i < maxKeys; i++) {
|
||
const key = keys[i];
|
||
if (excludeKeys.includes(key) || key === "constructor" || key === "__proto__" || key.startsWith("_cc_") || key.startsWith("__")) {
|
||
continue;
|
||
}
|
||
const value = item[key];
|
||
itemSize += key.length * 2;
|
||
if (typeof value === "string") {
|
||
itemSize += Math.min(value.length * 2, 200);
|
||
} else if (typeof value === "number") {
|
||
itemSize += 8;
|
||
} else if (typeof value === "boolean") {
|
||
itemSize += 4;
|
||
} else if (Array.isArray(value)) {
|
||
itemSize += 40 + Math.min(value.length * 8, 160);
|
||
} else if (typeof value === "object" && value !== null) {
|
||
itemSize += calculate(value, depth + 1);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
return 64;
|
||
}
|
||
return itemSize;
|
||
};
|
||
try {
|
||
const size = calculate(obj);
|
||
return Math.max(size, 32);
|
||
} catch (error) {
|
||
return 64;
|
||
}
|
||
}
|
||
buildEntityHierarchyTree(entityList) {
|
||
if (!entityList?.buffer)
|
||
return [];
|
||
const rootEntities = [];
|
||
entityList.buffer.forEach((entity) => {
|
||
if (!entity.parent) {
|
||
const hierarchyNode = this.buildEntityHierarchyNode(entity);
|
||
rootEntities.push(hierarchyNode);
|
||
}
|
||
});
|
||
rootEntities.sort((nodeA, nodeB) => {
|
||
if (nodeA.name < nodeB.name)
|
||
return -1;
|
||
if (nodeA.name > nodeB.name)
|
||
return 1;
|
||
return nodeA.id - nodeB.id;
|
||
});
|
||
return rootEntities;
|
||
}
|
||
/**
|
||
* 构建实体层次结构节点
|
||
*/
|
||
buildEntityHierarchyNode(entity) {
|
||
let node = {
|
||
id: entity.id,
|
||
name: entity.name || `Entity_${entity.id}`,
|
||
active: entity.active !== false,
|
||
enabled: entity.enabled !== false,
|
||
activeInHierarchy: entity.activeInHierarchy !== false,
|
||
componentCount: entity.components.length,
|
||
componentTypes: entity.components.map((component) => getComponentInstanceTypeName(component)),
|
||
parentId: entity.parent?.id || null,
|
||
children: [],
|
||
depth: entity.getDepth ? entity.getDepth() : 0,
|
||
tag: entity.tag || 0,
|
||
updateOrder: entity.updateOrder || 0
|
||
};
|
||
if (entity.children && entity.children.length > 0) {
|
||
node.children = entity.children.map((child) => this.buildEntityHierarchyNode(child));
|
||
}
|
||
if (typeof entity.getDebugInfo === "function") {
|
||
const debugInfo = entity.getDebugInfo();
|
||
node = {
|
||
...node,
|
||
...debugInfo
|
||
};
|
||
}
|
||
if (entity.components && entity.components.length > 0) {
|
||
node.componentDetails = this.extractComponentDetails(entity.components);
|
||
}
|
||
return node;
|
||
}
|
||
/**
|
||
* 构建实体详情映射
|
||
*/
|
||
buildEntityDetailsMap(entityList, scene) {
|
||
if (!entityList?.buffer)
|
||
return {};
|
||
const entityDetailsMap = {};
|
||
const entities = entityList.buffer;
|
||
const batchSize = 100;
|
||
for (let i = 0; i < entities.length; i += batchSize) {
|
||
const batch = entities.slice(i, i + batchSize);
|
||
batch.forEach((entity) => {
|
||
const baseDebugInfo = entity.getDebugInfo ? entity.getDebugInfo() : this.buildFallbackEntityInfo(entity, scene);
|
||
const componentCacheStats = entity.getComponentCacheStats ? entity.getComponentCacheStats() : null;
|
||
const componentDetails = this.extractComponentDetails(entity.components);
|
||
entityDetailsMap[entity.id] = {
|
||
...baseDebugInfo,
|
||
parentName: entity.parent?.name || null,
|
||
components: componentDetails,
|
||
componentTypes: baseDebugInfo.componentTypes || componentDetails.map((comp) => comp.typeName),
|
||
cachePerformance: componentCacheStats ? {
|
||
hitRate: componentCacheStats.cacheStats.hitRate,
|
||
size: componentCacheStats.cacheStats.size,
|
||
maxSize: componentCacheStats.cacheStats.maxSize
|
||
} : null
|
||
};
|
||
});
|
||
}
|
||
return entityDetailsMap;
|
||
}
|
||
/**
|
||
* 构建实体基础信息
|
||
*/
|
||
buildFallbackEntityInfo(entity, scene) {
|
||
const sceneInfo = this.getSceneInfo(scene);
|
||
return {
|
||
name: entity.name || `Entity_${entity.id}`,
|
||
id: entity.id,
|
||
enabled: entity.enabled !== false,
|
||
active: entity.active !== false,
|
||
activeInHierarchy: entity.activeInHierarchy !== false,
|
||
destroyed: entity.isDestroyed || false,
|
||
scene: sceneInfo.name,
|
||
sceneName: sceneInfo.name,
|
||
sceneType: sceneInfo.type,
|
||
componentCount: entity.components.length,
|
||
componentTypes: entity.components.map((component) => getComponentInstanceTypeName(component)),
|
||
componentMask: entity.componentMask?.toString() || "0",
|
||
parentId: entity.parent?.id || null,
|
||
childCount: entity.children?.length || 0,
|
||
childIds: entity.children.map((child) => child.id) || [],
|
||
depth: entity.getDepth ? entity.getDepth() : 0,
|
||
tag: entity.tag || 0,
|
||
updateOrder: entity.updateOrder || 0
|
||
};
|
||
}
|
||
/**
|
||
* 提取组件详细信息
|
||
*/
|
||
extractComponentDetails(components) {
|
||
return components.map((component) => {
|
||
let typeName = getComponentInstanceTypeName(component);
|
||
if (!typeName || typeName === "Object" || typeName === "Function") {
|
||
try {
|
||
const typeManager = ComponentTypeManager.instance;
|
||
const componentType = component.constructor;
|
||
const typeId = typeManager.getTypeId(componentType);
|
||
typeName = typeManager.getTypeName(typeId);
|
||
} catch (error) {
|
||
typeName = "UnknownComponent";
|
||
}
|
||
}
|
||
const properties = {};
|
||
try {
|
||
const propertyKeys = Object.keys(component);
|
||
propertyKeys.forEach((propertyKey) => {
|
||
if (!propertyKey.startsWith("_") && propertyKey !== "entity" && propertyKey !== "constructor") {
|
||
const propertyValue = component[propertyKey];
|
||
if (propertyValue !== void 0 && propertyValue !== null) {
|
||
properties[propertyKey] = this.formatPropertyValue(propertyValue);
|
||
}
|
||
}
|
||
});
|
||
if (Object.keys(properties).length === 0) {
|
||
properties._info = "该组件没有公开属性";
|
||
properties._componentId = getComponentInstanceTypeName(component);
|
||
}
|
||
} catch (error) {
|
||
properties._error = "属性提取失败";
|
||
properties._componentId = getComponentInstanceTypeName(component);
|
||
}
|
||
return {
|
||
typeName,
|
||
properties
|
||
};
|
||
});
|
||
}
|
||
/**
|
||
* 获取组件的完整属性信息(仅在需要时调用)
|
||
* @param entityId 实体ID
|
||
* @param componentIndex 组件索引
|
||
* @param scene 场景实例
|
||
*/
|
||
getComponentProperties(entityId, componentIndex, scene) {
|
||
try {
|
||
if (!scene)
|
||
return {};
|
||
const entityList = scene.entities;
|
||
if (!entityList?.buffer)
|
||
return {};
|
||
const entity = entityList.buffer.find((e) => e.id === entityId);
|
||
if (!entity || componentIndex >= entity.components.length)
|
||
return {};
|
||
const component = entity.components[componentIndex];
|
||
const properties = {};
|
||
const propertyKeys = Object.keys(component);
|
||
propertyKeys.forEach((propertyKey) => {
|
||
if (!propertyKey.startsWith("_") && propertyKey !== "entity") {
|
||
const propertyValue = component[propertyKey];
|
||
if (propertyValue !== void 0 && propertyValue !== null) {
|
||
properties[propertyKey] = this.formatPropertyValue(propertyValue);
|
||
}
|
||
}
|
||
});
|
||
return properties;
|
||
} catch (error) {
|
||
return { _error: "属性提取失败" };
|
||
}
|
||
}
|
||
/**
|
||
* 格式化属性值
|
||
*/
|
||
formatPropertyValue(value, depth = 0) {
|
||
if (value === null || value === void 0) {
|
||
return value;
|
||
}
|
||
if (typeof value !== "object") {
|
||
if (typeof value === "string" && value.length > 200) {
|
||
return `[长字符串: ${value.length}字符] ${value.substring(0, 100)}...`;
|
||
}
|
||
return value;
|
||
}
|
||
if (depth === 0) {
|
||
return this.formatObjectFirstLevel(value);
|
||
} else {
|
||
return this.createLazyLoadPlaceholder(value);
|
||
}
|
||
}
|
||
/**
|
||
* 格式化对象第一层
|
||
*/
|
||
formatObjectFirstLevel(obj) {
|
||
try {
|
||
if (Array.isArray(obj)) {
|
||
if (obj.length === 0)
|
||
return [];
|
||
if (obj.length > 10) {
|
||
const sample = obj.slice(0, 3).map((item) => this.formatPropertyValue(item, 1));
|
||
return {
|
||
_isLazyArray: true,
|
||
_arrayLength: obj.length,
|
||
_sample: sample,
|
||
_summary: `数组[${obj.length}个元素]`
|
||
};
|
||
}
|
||
return obj.map((item) => this.formatPropertyValue(item, 1));
|
||
}
|
||
const keys = Object.keys(obj);
|
||
if (keys.length === 0)
|
||
return {};
|
||
const result = {};
|
||
let processedCount = 0;
|
||
const maxProperties = 15;
|
||
for (const key of keys) {
|
||
if (processedCount >= maxProperties) {
|
||
result._hasMoreProperties = true;
|
||
result._totalProperties = keys.length;
|
||
result._hiddenCount = keys.length - processedCount;
|
||
break;
|
||
}
|
||
if (key.startsWith("_") || key.startsWith("$") || typeof obj[key] === "function") {
|
||
continue;
|
||
}
|
||
try {
|
||
const value = obj[key];
|
||
if (value !== null && value !== void 0) {
|
||
result[key] = this.formatPropertyValue(value, 1);
|
||
processedCount++;
|
||
}
|
||
} catch (error) {
|
||
result[key] = `[访问失败: ${error instanceof Error ? error.message : String(error)}]`;
|
||
processedCount++;
|
||
}
|
||
}
|
||
return result;
|
||
} catch (error) {
|
||
return `[对象解析失败: ${error instanceof Error ? error.message : String(error)}]`;
|
||
}
|
||
}
|
||
/**
|
||
* 创建懒加载占位符
|
||
*/
|
||
createLazyLoadPlaceholder(obj) {
|
||
try {
|
||
const typeName = obj.constructor?.name || "Object";
|
||
const summary = this.getObjectSummary(obj, typeName);
|
||
return {
|
||
_isLazyObject: true,
|
||
_typeName: typeName,
|
||
_summary: summary,
|
||
_objectId: this.generateObjectId(obj)
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
_isLazyObject: true,
|
||
_typeName: "Unknown",
|
||
_summary: `无法分析的对象: ${error instanceof Error ? error.message : String(error)}`,
|
||
_objectId: Math.random().toString(36).substr(2, 9)
|
||
};
|
||
}
|
||
}
|
||
/**
|
||
* 获取对象摘要信息
|
||
*/
|
||
getObjectSummary(obj, typeName) {
|
||
try {
|
||
if (typeName.toLowerCase().includes("vec") || typeName.toLowerCase().includes("vector")) {
|
||
if (obj.x !== void 0 && obj.y !== void 0) {
|
||
const z = obj.z !== void 0 ? obj.z : "";
|
||
return `${typeName}(${obj.x}, ${obj.y}${z ? ", " + z : ""})`;
|
||
}
|
||
}
|
||
if (typeName.toLowerCase().includes("color")) {
|
||
if (obj.r !== void 0 && obj.g !== void 0 && obj.b !== void 0) {
|
||
const a = obj.a !== void 0 ? obj.a : 1;
|
||
return `${typeName}(${obj.r}, ${obj.g}, ${obj.b}, ${a})`;
|
||
}
|
||
}
|
||
if (typeName.toLowerCase().includes("node")) {
|
||
const name = obj.name || obj._name || "未命名";
|
||
return `${typeName}: ${name}`;
|
||
}
|
||
if (typeName.toLowerCase().includes("component")) {
|
||
const nodeName = obj.node?.name || obj.node?._name || "";
|
||
return `${typeName}${nodeName ? ` on ${nodeName}` : ""}`;
|
||
}
|
||
const keys = Object.keys(obj);
|
||
if (keys.length === 0) {
|
||
return `${typeName} (空对象)`;
|
||
}
|
||
return `${typeName} (${keys.length}个属性)`;
|
||
} catch (error) {
|
||
return `${typeName} (无法分析)`;
|
||
}
|
||
}
|
||
/**
|
||
* 生成对象ID
|
||
*/
|
||
generateObjectId(obj) {
|
||
try {
|
||
if (obj.id !== void 0)
|
||
return `obj_${obj.id}`;
|
||
if (obj._id !== void 0)
|
||
return `obj_${obj._id}`;
|
||
if (obj.uuid !== void 0)
|
||
return `obj_${obj.uuid}`;
|
||
if (obj._uuid !== void 0)
|
||
return `obj_${obj._uuid}`;
|
||
return `obj_${Math.random().toString(36).substr(2, 9)}`;
|
||
} catch {
|
||
return `obj_${Math.random().toString(36).substr(2, 9)}`;
|
||
}
|
||
}
|
||
/**
|
||
* 展开懒加载对象(供调试面板调用)
|
||
* @param entityId 实体ID
|
||
* @param componentIndex 组件索引
|
||
* @param propertyPath 属性路径
|
||
* @param scene 场景实例
|
||
*/
|
||
expandLazyObject(entityId, componentIndex, propertyPath, scene) {
|
||
try {
|
||
if (!scene)
|
||
return null;
|
||
const entityList = scene.entities;
|
||
if (!entityList?.buffer)
|
||
return null;
|
||
const entity = entityList.buffer.find((e) => e.id === entityId);
|
||
if (!entity)
|
||
return null;
|
||
if (componentIndex >= entity.components.length)
|
||
return null;
|
||
const component = entity.components[componentIndex];
|
||
const targetObject = this.getObjectByPath(component, propertyPath);
|
||
if (!targetObject)
|
||
return null;
|
||
return this.formatObjectFirstLevel(targetObject);
|
||
} catch (error) {
|
||
return {
|
||
error: `展开失败: ${error instanceof Error ? error.message : String(error)}`
|
||
};
|
||
}
|
||
}
|
||
/**
|
||
* 根据路径获取对象
|
||
*/
|
||
getObjectByPath(root, path) {
|
||
if (!path)
|
||
return root;
|
||
const parts = path.split(".");
|
||
let current = root;
|
||
for (const part of parts) {
|
||
if (current === null || current === void 0)
|
||
return null;
|
||
if (part.includes("[") && part.includes("]")) {
|
||
const arrayName = part.substring(0, part.indexOf("["));
|
||
const index = parseInt(part.substring(part.indexOf("[") + 1, part.indexOf("]")));
|
||
if (arrayName) {
|
||
current = current[arrayName];
|
||
}
|
||
if (Array.isArray(current) && index >= 0 && index < current.length) {
|
||
current = current[index];
|
||
} else {
|
||
return null;
|
||
}
|
||
} else {
|
||
current = current[part];
|
||
}
|
||
}
|
||
return current;
|
||
}
|
||
}
|
||
class SystemDataCollector {
|
||
/**
|
||
* 收集系统数据
|
||
* @param performanceMonitor 性能监视器实例
|
||
* @param scene 场景实例
|
||
*/
|
||
collectSystemData(performanceMonitor, scene) {
|
||
if (!scene) {
|
||
return {
|
||
totalSystems: 0,
|
||
systemsInfo: []
|
||
};
|
||
}
|
||
const entityProcessors = scene.entityProcessors;
|
||
if (!entityProcessors) {
|
||
return {
|
||
totalSystems: 0,
|
||
systemsInfo: []
|
||
};
|
||
}
|
||
const systems = entityProcessors.processors || [];
|
||
let systemStats = /* @__PURE__ */ new Map();
|
||
let systemData = /* @__PURE__ */ new Map();
|
||
if (performanceMonitor) {
|
||
try {
|
||
systemStats = performanceMonitor.getAllSystemStats();
|
||
systemData = performanceMonitor.getAllSystemData();
|
||
} catch (error) {
|
||
}
|
||
}
|
||
return {
|
||
totalSystems: systems.length,
|
||
systemsInfo: systems.map((system) => {
|
||
const systemName = system.systemName || getSystemInstanceTypeName(system);
|
||
const stats = systemStats.get(systemName);
|
||
const data = systemData.get(systemName);
|
||
return {
|
||
name: systemName,
|
||
type: getSystemInstanceTypeName(system),
|
||
entityCount: system.entities?.length || 0,
|
||
executionTime: stats?.averageTime || data?.executionTime || 0,
|
||
minExecutionTime: stats?.minTime === Number.MAX_VALUE ? 0 : stats?.minTime || 0,
|
||
maxExecutionTime: stats?.maxTime || 0,
|
||
executionTimeHistory: stats?.recentTimes || [],
|
||
updateOrder: system.updateOrder || 0,
|
||
enabled: system.enabled !== false,
|
||
lastUpdateTime: data?.lastUpdateTime || 0
|
||
};
|
||
})
|
||
};
|
||
}
|
||
}
|
||
class PerformanceDataCollector {
|
||
constructor() {
|
||
this.frameTimeHistory = [];
|
||
this.maxHistoryLength = 60;
|
||
this.lastGCCount = 0;
|
||
this.gcCollections = 0;
|
||
this.lastMemoryCheck = 0;
|
||
}
|
||
/**
|
||
* 收集性能数据
|
||
*/
|
||
collectPerformanceData(performanceMonitor) {
|
||
const frameTimeSeconds = Time.deltaTime;
|
||
const engineFrameTimeMs = frameTimeSeconds * 1e3;
|
||
const currentFps = frameTimeSeconds > 0 ? Math.round(1 / frameTimeSeconds) : 0;
|
||
const ecsPerformanceData = this.getECSPerformanceData(performanceMonitor);
|
||
const ecsExecutionTimeMs = ecsPerformanceData.totalExecutionTime;
|
||
const ecsPercentage = engineFrameTimeMs > 0 ? ecsExecutionTimeMs / engineFrameTimeMs * 100 : 0;
|
||
let memoryUsage = 0;
|
||
if (performance.memory) {
|
||
memoryUsage = performance.memory.usedJSHeapSize / 1024 / 1024;
|
||
}
|
||
this.frameTimeHistory.push(ecsExecutionTimeMs);
|
||
if (this.frameTimeHistory.length > this.maxHistoryLength) {
|
||
this.frameTimeHistory.shift();
|
||
}
|
||
const history = this.frameTimeHistory.filter((t) => t >= 0);
|
||
const averageECSTime = history.length > 0 ? history.reduce((a, b) => a + b, 0) / history.length : ecsExecutionTimeMs;
|
||
const minECSTime = history.length > 0 ? Math.min(...history) : ecsExecutionTimeMs;
|
||
const maxECSTime = history.length > 0 ? Math.max(...history) : ecsExecutionTimeMs;
|
||
return {
|
||
frameTime: ecsExecutionTimeMs,
|
||
engineFrameTime: engineFrameTimeMs,
|
||
ecsPercentage,
|
||
memoryUsage,
|
||
fps: currentFps,
|
||
averageFrameTime: averageECSTime,
|
||
minFrameTime: minECSTime,
|
||
maxFrameTime: maxECSTime,
|
||
frameTimeHistory: [...this.frameTimeHistory],
|
||
systemPerformance: this.getSystemPerformance(performanceMonitor),
|
||
systemBreakdown: ecsPerformanceData.systemBreakdown,
|
||
memoryDetails: this.getMemoryDetails()
|
||
};
|
||
}
|
||
/**
|
||
* 获取ECS框架整体性能数据
|
||
*/
|
||
getECSPerformanceData(performanceMonitor) {
|
||
if (!performanceMonitor) {
|
||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||
}
|
||
if (!performanceMonitor.enabled) {
|
||
try {
|
||
performanceMonitor.enabled = true;
|
||
} catch (error) {
|
||
}
|
||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||
}
|
||
try {
|
||
let totalTime = 0;
|
||
const systemBreakdown = [];
|
||
const stats = performanceMonitor.getAllSystemStats();
|
||
if (stats.size === 0) {
|
||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||
}
|
||
for (const [systemName, stat] of stats.entries()) {
|
||
const systemTime = stat.recentTimes && stat.recentTimes.length > 0 ? stat.recentTimes[stat.recentTimes.length - 1] : stat.averageTime || 0;
|
||
totalTime += systemTime;
|
||
systemBreakdown.push({
|
||
systemName,
|
||
executionTime: systemTime,
|
||
percentage: 0
|
||
// 后面计算
|
||
});
|
||
}
|
||
systemBreakdown.forEach((system) => {
|
||
system.percentage = totalTime > 0 ? system.executionTime / totalTime * 100 : 0;
|
||
});
|
||
systemBreakdown.sort((a, b) => b.executionTime - a.executionTime);
|
||
return {
|
||
totalExecutionTime: totalTime,
|
||
systemBreakdown
|
||
};
|
||
} catch (error) {
|
||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||
}
|
||
}
|
||
/**
|
||
* 获取系统性能数据
|
||
*/
|
||
getSystemPerformance(performanceMonitor) {
|
||
if (!performanceMonitor) {
|
||
return [];
|
||
}
|
||
try {
|
||
const stats = performanceMonitor.getAllSystemStats();
|
||
const systemData = performanceMonitor.getAllSystemData();
|
||
return Array.from(stats.entries()).map(([systemName, stat]) => {
|
||
const data = systemData.get(systemName);
|
||
return {
|
||
systemName,
|
||
averageTime: stat.averageTime || 0,
|
||
maxTime: stat.maxTime || 0,
|
||
minTime: stat.minTime === Number.MAX_VALUE ? 0 : stat.minTime || 0,
|
||
samples: stat.executionCount || 0,
|
||
percentage: 0,
|
||
entityCount: data?.entityCount || 0,
|
||
lastExecutionTime: data?.executionTime || 0
|
||
};
|
||
});
|
||
} catch (error) {
|
||
return [];
|
||
}
|
||
}
|
||
/**
|
||
* 获取内存详情
|
||
*/
|
||
getMemoryDetails() {
|
||
const memoryInfo = {
|
||
entities: 0,
|
||
components: 0,
|
||
systems: 0,
|
||
pooled: 0,
|
||
totalMemory: 0,
|
||
usedMemory: 0,
|
||
freeMemory: 0,
|
||
gcCollections: this.updateGCCount()
|
||
};
|
||
try {
|
||
if (performance.memory) {
|
||
const perfMemory = performance.memory;
|
||
memoryInfo.totalMemory = perfMemory.jsHeapSizeLimit || 512 * 1024 * 1024;
|
||
memoryInfo.usedMemory = perfMemory.usedJSHeapSize || 0;
|
||
memoryInfo.freeMemory = memoryInfo.totalMemory - memoryInfo.usedMemory;
|
||
if (this.lastMemoryCheck > 0) {
|
||
const memoryDrop = this.lastMemoryCheck - memoryInfo.usedMemory;
|
||
if (memoryDrop > 1024 * 1024) {
|
||
this.gcCollections++;
|
||
}
|
||
}
|
||
this.lastMemoryCheck = memoryInfo.usedMemory;
|
||
} else {
|
||
memoryInfo.totalMemory = 512 * 1024 * 1024;
|
||
memoryInfo.freeMemory = 512 * 1024 * 1024;
|
||
}
|
||
} catch (error) {
|
||
return {
|
||
totalMemory: 0,
|
||
usedMemory: 0,
|
||
freeMemory: 0,
|
||
entityMemory: 0,
|
||
componentMemory: 0,
|
||
systemMemory: 0,
|
||
pooledMemory: 0,
|
||
gcCollections: this.gcCollections
|
||
};
|
||
}
|
||
return memoryInfo;
|
||
}
|
||
/**
|
||
* 更新GC计数
|
||
*/
|
||
updateGCCount() {
|
||
try {
|
||
if (typeof PerformanceObserver !== "undefined") {
|
||
return this.gcCollections;
|
||
}
|
||
if (performance.measureUserAgentSpecificMemory) {
|
||
return this.gcCollections;
|
||
}
|
||
return this.gcCollections;
|
||
} catch (error) {
|
||
return this.gcCollections;
|
||
}
|
||
}
|
||
}
|
||
class ComponentPool {
|
||
constructor(createFn, resetFn, maxSize = 1e3) {
|
||
this.pool = [];
|
||
this.createFn = createFn;
|
||
this.resetFn = resetFn;
|
||
this.maxSize = maxSize;
|
||
}
|
||
/**
|
||
* 获取一个组件实例
|
||
*/
|
||
acquire() {
|
||
if (this.pool.length > 0) {
|
||
return this.pool.pop();
|
||
}
|
||
return this.createFn();
|
||
}
|
||
/**
|
||
* 释放一个组件实例回池中
|
||
*/
|
||
release(component) {
|
||
if (this.pool.length < this.maxSize) {
|
||
if (this.resetFn) {
|
||
this.resetFn(component);
|
||
}
|
||
this.pool.push(component);
|
||
}
|
||
}
|
||
/**
|
||
* 预填充对象池
|
||
*/
|
||
prewarm(count) {
|
||
for (let i = 0; i < count && this.pool.length < this.maxSize; i++) {
|
||
this.pool.push(this.createFn());
|
||
}
|
||
}
|
||
/**
|
||
* 清空对象池
|
||
*/
|
||
clear() {
|
||
this.pool.length = 0;
|
||
}
|
||
/**
|
||
* 获取池中可用对象数量
|
||
*/
|
||
getAvailableCount() {
|
||
return this.pool.length;
|
||
}
|
||
/**
|
||
* 获取池的最大容量
|
||
*/
|
||
getMaxSize() {
|
||
return this.maxSize;
|
||
}
|
||
}
|
||
class ComponentPoolManager {
|
||
constructor() {
|
||
this.pools = /* @__PURE__ */ new Map();
|
||
}
|
||
static getInstance() {
|
||
if (!ComponentPoolManager.instance) {
|
||
ComponentPoolManager.instance = new ComponentPoolManager();
|
||
}
|
||
return ComponentPoolManager.instance;
|
||
}
|
||
/**
|
||
* 注册组件池
|
||
*/
|
||
registerPool(componentName, createFn, resetFn, maxSize) {
|
||
this.pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize));
|
||
}
|
||
/**
|
||
* 获取组件实例
|
||
*/
|
||
acquireComponent(componentName) {
|
||
const pool = this.pools.get(componentName);
|
||
return pool ? pool.acquire() : null;
|
||
}
|
||
/**
|
||
* 释放组件实例
|
||
*/
|
||
releaseComponent(componentName, component) {
|
||
const pool = this.pools.get(componentName);
|
||
if (pool) {
|
||
pool.release(component);
|
||
}
|
||
}
|
||
/**
|
||
* 预热所有池
|
||
*/
|
||
prewarmAll(count = 100) {
|
||
for (const pool of this.pools.values()) {
|
||
pool.prewarm(count);
|
||
}
|
||
}
|
||
/**
|
||
* 清空所有池
|
||
*/
|
||
clearAll() {
|
||
for (const pool of this.pools.values()) {
|
||
pool.clear();
|
||
}
|
||
}
|
||
/**
|
||
* 重置管理器,移除所有注册的池
|
||
*/
|
||
reset() {
|
||
this.pools.clear();
|
||
}
|
||
/**
|
||
* 获取池统计信息
|
||
*/
|
||
getPoolStats() {
|
||
const stats = /* @__PURE__ */ new Map();
|
||
for (const [name, pool] of this.pools) {
|
||
stats.set(name, {
|
||
available: pool.getAvailableCount(),
|
||
maxSize: pool.getMaxSize()
|
||
});
|
||
}
|
||
return stats;
|
||
}
|
||
/**
|
||
* 获取池利用率信息(用于调试)
|
||
*/
|
||
getPoolUtilization() {
|
||
const utilization = /* @__PURE__ */ new Map();
|
||
for (const [name, pool] of this.pools) {
|
||
const available = pool.getAvailableCount();
|
||
const maxSize = pool.getMaxSize();
|
||
const used = maxSize - available;
|
||
const utilRate = maxSize > 0 ? used / maxSize * 100 : 0;
|
||
utilization.set(name, {
|
||
used,
|
||
total: maxSize,
|
||
utilization: utilRate
|
||
});
|
||
}
|
||
return utilization;
|
||
}
|
||
/**
|
||
* 获取指定组件的池利用率
|
||
*/
|
||
getComponentUtilization(componentName) {
|
||
const pool = this.pools.get(componentName);
|
||
if (!pool)
|
||
return 0;
|
||
const available = pool.getAvailableCount();
|
||
const maxSize = pool.getMaxSize();
|
||
const used = maxSize - available;
|
||
return maxSize > 0 ? used / maxSize * 100 : 0;
|
||
}
|
||
}
|
||
class ComponentDataCollector {
|
||
/**
|
||
* 收集组件数据(轻量版,不计算实际内存大小)
|
||
* @param scene 场景实例
|
||
*/
|
||
collectComponentData(scene) {
|
||
if (!scene) {
|
||
return {
|
||
componentTypes: 0,
|
||
componentInstances: 0,
|
||
componentStats: []
|
||
};
|
||
}
|
||
const entityList = scene.entities;
|
||
if (!entityList?.buffer) {
|
||
return {
|
||
componentTypes: 0,
|
||
componentInstances: 0,
|
||
componentStats: []
|
||
};
|
||
}
|
||
const componentStats = /* @__PURE__ */ new Map();
|
||
let totalInstances = 0;
|
||
entityList.buffer.forEach((entity) => {
|
||
if (entity.components) {
|
||
entity.components.forEach((component) => {
|
||
const typeName = getComponentInstanceTypeName(component);
|
||
const stats = componentStats.get(typeName) || { count: 0, entities: 0 };
|
||
stats.count++;
|
||
totalInstances++;
|
||
componentStats.set(typeName, stats);
|
||
});
|
||
}
|
||
});
|
||
let poolUtilizations = /* @__PURE__ */ new Map();
|
||
let poolSizes = /* @__PURE__ */ new Map();
|
||
try {
|
||
const poolManager = ComponentPoolManager.getInstance();
|
||
const poolStats = poolManager.getPoolStats();
|
||
const utilizations = poolManager.getPoolUtilization();
|
||
for (const [typeName, stats] of poolStats.entries()) {
|
||
poolSizes.set(typeName, stats.maxSize);
|
||
}
|
||
for (const [typeName, util] of utilizations.entries()) {
|
||
poolUtilizations.set(typeName, util.utilization);
|
||
}
|
||
} catch (error) {
|
||
}
|
||
return {
|
||
componentTypes: componentStats.size,
|
||
componentInstances: totalInstances,
|
||
componentStats: Array.from(componentStats.entries()).map(([typeName, stats]) => {
|
||
const poolSize = poolSizes.get(typeName) || 0;
|
||
const poolUtilization = poolUtilizations.get(typeName) || 0;
|
||
const memoryPerInstance = this.getEstimatedComponentSize(typeName, scene);
|
||
return {
|
||
typeName,
|
||
instanceCount: stats.count,
|
||
memoryPerInstance,
|
||
totalMemory: stats.count * memoryPerInstance,
|
||
poolSize,
|
||
poolUtilization,
|
||
averagePerEntity: stats.count / entityList.buffer.length
|
||
};
|
||
})
|
||
};
|
||
}
|
||
/**
|
||
* 获取组件类型的估算内存大小(基于预设值,不进行实际计算)
|
||
*/
|
||
getEstimatedComponentSize(typeName, scene) {
|
||
if (ComponentDataCollector.componentSizeCache.has(typeName)) {
|
||
return ComponentDataCollector.componentSizeCache.get(typeName);
|
||
}
|
||
if (!scene)
|
||
return 64;
|
||
const entityList = scene.entities;
|
||
if (!entityList?.buffer)
|
||
return 64;
|
||
let calculatedSize = 64;
|
||
try {
|
||
for (const entity of entityList.buffer) {
|
||
if (entity.components) {
|
||
const component = entity.components.find((c) => getComponentInstanceTypeName(c) === typeName);
|
||
if (component) {
|
||
calculatedSize = this.calculateQuickObjectSize(component);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
calculatedSize = 64;
|
||
}
|
||
ComponentDataCollector.componentSizeCache.set(typeName, calculatedSize);
|
||
return calculatedSize;
|
||
}
|
||
calculateQuickObjectSize(obj) {
|
||
if (!obj || typeof obj !== "object")
|
||
return 8;
|
||
let size = 32;
|
||
const visited = /* @__PURE__ */ new WeakSet();
|
||
const calculate = (item, depth = 0) => {
|
||
if (!item || typeof item !== "object" || visited.has(item) || depth > 3) {
|
||
return 0;
|
||
}
|
||
visited.add(item);
|
||
let itemSize = 0;
|
||
try {
|
||
const keys = Object.keys(item);
|
||
for (let i = 0; i < Math.min(keys.length, 20); i++) {
|
||
const key = keys[i];
|
||
if (key === "entity" || key === "_entity" || key === "constructor")
|
||
continue;
|
||
const value = item[key];
|
||
itemSize += key.length * 2;
|
||
if (typeof value === "string") {
|
||
itemSize += Math.min(value.length * 2, 200);
|
||
} else if (typeof value === "number") {
|
||
itemSize += 8;
|
||
} else if (typeof value === "boolean") {
|
||
itemSize += 4;
|
||
} else if (typeof value === "object" && value !== null) {
|
||
itemSize += calculate(value, depth + 1);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
return 32;
|
||
}
|
||
return itemSize;
|
||
};
|
||
size += calculate(obj);
|
||
return Math.max(size, 32);
|
||
}
|
||
/**
|
||
* 为内存快照功能提供的详细内存计算
|
||
* 只在用户主动请求内存快照时调用
|
||
* @param typeName 组件类型名称
|
||
* @param scene 场景实例
|
||
*/
|
||
calculateDetailedComponentMemory(typeName, scene) {
|
||
if (!scene)
|
||
return this.getEstimatedComponentSize(typeName, scene);
|
||
const entityList = scene.entities;
|
||
if (!entityList?.buffer)
|
||
return this.getEstimatedComponentSize(typeName, scene);
|
||
try {
|
||
for (const entity of entityList.buffer) {
|
||
if (entity.components) {
|
||
const component = entity.components.find((c) => getComponentInstanceTypeName(c) === typeName);
|
||
if (component) {
|
||
return this.estimateObjectSize(component);
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
}
|
||
return this.getEstimatedComponentSize(typeName, scene);
|
||
}
|
||
/**
|
||
* 估算对象内存大小(仅用于内存快照)
|
||
* 优化版本:减少递归深度,提高性能
|
||
*/
|
||
estimateObjectSize(obj, visited = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
||
if (obj === null || obj === void 0 || depth > 10)
|
||
return 0;
|
||
if (visited.has(obj))
|
||
return 0;
|
||
let size = 0;
|
||
const type = typeof obj;
|
||
switch (type) {
|
||
case "boolean":
|
||
size = 4;
|
||
break;
|
||
case "number":
|
||
size = 8;
|
||
break;
|
||
case "string":
|
||
size = 24 + Math.min(obj.length * 2, 1e3);
|
||
break;
|
||
case "object":
|
||
visited.add(obj);
|
||
if (Array.isArray(obj)) {
|
||
size = 40 + obj.length * 8;
|
||
const maxElements = Math.min(obj.length, 50);
|
||
for (let i = 0; i < maxElements; i++) {
|
||
size += this.estimateObjectSize(obj[i], visited, depth + 1);
|
||
}
|
||
} else {
|
||
size = 32;
|
||
try {
|
||
const ownKeys = Object.getOwnPropertyNames(obj);
|
||
const maxProps = Math.min(ownKeys.length, 30);
|
||
for (let i = 0; i < maxProps; i++) {
|
||
const key = ownKeys[i];
|
||
if (key === "constructor" || key === "__proto__" || key === "entity" || key === "_entity" || key.startsWith("_cc_") || key.startsWith("__")) {
|
||
continue;
|
||
}
|
||
try {
|
||
size += 16 + key.length * 2;
|
||
const value = obj[key];
|
||
if (value !== void 0 && value !== null) {
|
||
size += this.estimateObjectSize(value, visited, depth + 1);
|
||
}
|
||
} catch (error) {
|
||
continue;
|
||
}
|
||
}
|
||
} catch (error) {
|
||
size = 128;
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
size = 8;
|
||
}
|
||
return Math.ceil(size / 8) * 8;
|
||
}
|
||
static clearCache() {
|
||
ComponentDataCollector.componentSizeCache.clear();
|
||
}
|
||
}
|
||
ComponentDataCollector.componentSizeCache = /* @__PURE__ */ new Map();
|
||
class SceneDataCollector {
|
||
constructor() {
|
||
this.sceneStartTime = Date.now();
|
||
}
|
||
/**
|
||
* 收集场景数据
|
||
* @param scene 场景实例
|
||
*/
|
||
collectSceneData(scene) {
|
||
if (!scene) {
|
||
return {
|
||
currentSceneName: "No Scene",
|
||
isInitialized: false,
|
||
sceneRunTime: 0,
|
||
sceneEntityCount: 0,
|
||
sceneSystemCount: 0,
|
||
sceneMemory: 0,
|
||
sceneUptime: 0
|
||
};
|
||
}
|
||
const currentTime = Date.now();
|
||
const runTime = (currentTime - this.sceneStartTime) / 1e3;
|
||
const entityList = scene.entities;
|
||
const entityProcessors = scene.entityProcessors;
|
||
return {
|
||
currentSceneName: scene.name || "Unnamed Scene",
|
||
isInitialized: scene._didSceneBegin || false,
|
||
sceneRunTime: runTime,
|
||
sceneEntityCount: entityList?.buffer?.length || 0,
|
||
sceneSystemCount: entityProcessors?.processors?.length || 0,
|
||
sceneMemory: 0,
|
||
// TODO: 计算实际场景内存
|
||
sceneUptime: runTime
|
||
};
|
||
}
|
||
/**
|
||
* 设置场景开始时间
|
||
*/
|
||
setSceneStartTime(time) {
|
||
this.sceneStartTime = time;
|
||
}
|
||
}
|
||
class WebSocketManager {
|
||
constructor(url, autoReconnect = true) {
|
||
this.isConnected = false;
|
||
this.reconnectAttempts = 0;
|
||
this.maxReconnectAttempts = 5;
|
||
this.reconnectInterval = 2e3;
|
||
this.url = url;
|
||
this.autoReconnect = autoReconnect;
|
||
}
|
||
/**
|
||
* 设置消息处理回调
|
||
*/
|
||
setMessageHandler(handler) {
|
||
this.messageHandler = handler;
|
||
}
|
||
/**
|
||
* 连接WebSocket
|
||
*/
|
||
connect() {
|
||
return new Promise((resolve, reject) => {
|
||
try {
|
||
this.ws = new WebSocket(this.url);
|
||
this.ws.onopen = (event) => {
|
||
this.handleOpen(event);
|
||
resolve();
|
||
};
|
||
this.ws.onclose = (event) => {
|
||
this.handleClose(event);
|
||
};
|
||
this.ws.onerror = (error) => {
|
||
this.handleError(error);
|
||
reject(error);
|
||
};
|
||
this.ws.onmessage = (event) => {
|
||
this.handleMessage(event);
|
||
};
|
||
} catch (error) {
|
||
this.handleConnectionFailure(error);
|
||
reject(error);
|
||
}
|
||
});
|
||
}
|
||
/**
|
||
* 断开连接
|
||
*/
|
||
disconnect() {
|
||
if (this.ws) {
|
||
this.autoReconnect = false;
|
||
this.ws.close();
|
||
this.ws = void 0;
|
||
}
|
||
this.isConnected = false;
|
||
}
|
||
/**
|
||
* 发送数据
|
||
*/
|
||
send(data) {
|
||
if (!this.isConnected || !this.ws) {
|
||
return;
|
||
}
|
||
try {
|
||
const message = typeof data === "string" ? data : JSON.stringify(data);
|
||
this.ws.send(message);
|
||
} catch (error) {
|
||
}
|
||
}
|
||
/**
|
||
* 获取连接状态
|
||
*/
|
||
getConnectionStatus() {
|
||
return this.isConnected;
|
||
}
|
||
/**
|
||
* 设置最大重连次数
|
||
*/
|
||
setMaxReconnectAttempts(attempts) {
|
||
this.maxReconnectAttempts = attempts;
|
||
}
|
||
/**
|
||
* 设置重连间隔
|
||
*/
|
||
setReconnectInterval(interval) {
|
||
this.reconnectInterval = interval;
|
||
}
|
||
/**
|
||
* 计划重连
|
||
*/
|
||
scheduleReconnect() {
|
||
if (this.reconnectTimer) {
|
||
clearTimeout(this.reconnectTimer);
|
||
}
|
||
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
|
||
this.reconnectAttempts++;
|
||
this.reconnectTimer = setTimeout(() => {
|
||
this.connect().catch((error) => {
|
||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||
this.scheduleReconnect();
|
||
}
|
||
});
|
||
}, delay);
|
||
}
|
||
/**
|
||
* 处理接收到的消息
|
||
*/
|
||
handleMessage(event) {
|
||
try {
|
||
const message = JSON.parse(event.data);
|
||
if (this.messageHandler) {
|
||
this.messageHandler(message);
|
||
}
|
||
} catch (error) {
|
||
}
|
||
}
|
||
handleOpen(event) {
|
||
this.isConnected = true;
|
||
this.reconnectAttempts = 0;
|
||
if (this.onOpen) {
|
||
this.onOpen(event);
|
||
}
|
||
}
|
||
handleClose(event) {
|
||
this.isConnected = false;
|
||
if (this.onClose) {
|
||
this.onClose(event);
|
||
}
|
||
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
||
this.scheduleReconnect();
|
||
}
|
||
}
|
||
handleError(error) {
|
||
if (this.onError) {
|
||
this.onError(error);
|
||
}
|
||
}
|
||
handleConnectionFailure(error) {
|
||
if (this.onError) {
|
||
this.onError(error);
|
||
}
|
||
}
|
||
}
|
||
class DebugManager {
|
||
/**
|
||
* 构造调试管理器
|
||
* @param core Core实例
|
||
* @param config 调试配置
|
||
*/
|
||
constructor(core, config) {
|
||
this.frameCounter = 0;
|
||
this.lastSendTime = 0;
|
||
this.isRunning = false;
|
||
this.config = config;
|
||
this.sceneProvider = () => core.scene || core.constructor.scene;
|
||
this.performanceMonitorProvider = () => core._performanceMonitor;
|
||
this.entityCollector = new EntityDataCollector();
|
||
this.systemCollector = new SystemDataCollector();
|
||
this.performanceCollector = new PerformanceDataCollector();
|
||
this.componentCollector = new ComponentDataCollector();
|
||
this.sceneCollector = new SceneDataCollector();
|
||
this.webSocketManager = new WebSocketManager(config.websocketUrl, config.autoReconnect !== false);
|
||
this.webSocketManager.setMessageHandler(this.handleMessage.bind(this));
|
||
const debugFrameRate = config.debugFrameRate || 30;
|
||
this.sendInterval = 1e3 / debugFrameRate;
|
||
this.start();
|
||
}
|
||
/**
|
||
* 启动调试管理器
|
||
*/
|
||
start() {
|
||
if (this.isRunning)
|
||
return;
|
||
this.isRunning = true;
|
||
this.connectWebSocket();
|
||
}
|
||
/**
|
||
* 停止调试管理器
|
||
*/
|
||
stop() {
|
||
if (!this.isRunning)
|
||
return;
|
||
this.isRunning = false;
|
||
this.webSocketManager.disconnect();
|
||
}
|
||
/**
|
||
* 更新配置
|
||
*/
|
||
updateConfig(config) {
|
||
this.config = config;
|
||
const debugFrameRate = config.debugFrameRate || 30;
|
||
this.sendInterval = 1e3 / debugFrameRate;
|
||
if (this.webSocketManager && config.websocketUrl) {
|
||
this.webSocketManager.disconnect();
|
||
this.webSocketManager = new WebSocketManager(config.websocketUrl, config.autoReconnect !== false);
|
||
this.webSocketManager.setMessageHandler(this.handleMessage.bind(this));
|
||
this.connectWebSocket();
|
||
}
|
||
}
|
||
/**
|
||
* 帧更新回调
|
||
*/
|
||
onFrameUpdate(deltaTime) {
|
||
if (!this.isRunning || !this.config.enabled)
|
||
return;
|
||
this.frameCounter++;
|
||
const currentTime = Date.now();
|
||
if (currentTime - this.lastSendTime >= this.sendInterval) {
|
||
this.sendDebugData();
|
||
this.lastSendTime = currentTime;
|
||
}
|
||
}
|
||
/**
|
||
* 场景变更回调
|
||
*/
|
||
onSceneChanged() {
|
||
if (this.isRunning && this.config.enabled) {
|
||
this.sendDebugData();
|
||
}
|
||
}
|
||
/**
|
||
* 处理来自调试面板的消息
|
||
*/
|
||
handleMessage(message) {
|
||
try {
|
||
switch (message.type) {
|
||
case "capture_memory_snapshot":
|
||
this.handleMemorySnapshotRequest();
|
||
break;
|
||
case "config_update":
|
||
if (message.config) {
|
||
this.updateConfig({ ...this.config, ...message.config });
|
||
}
|
||
break;
|
||
case "expand_lazy_object":
|
||
this.handleExpandLazyObjectRequest(message);
|
||
break;
|
||
case "get_component_properties":
|
||
this.handleGetComponentPropertiesRequest(message);
|
||
break;
|
||
case "get_raw_entity_list":
|
||
this.handleGetRawEntityListRequest(message);
|
||
break;
|
||
case "get_entity_details":
|
||
this.handleGetEntityDetailsRequest(message);
|
||
break;
|
||
case "ping":
|
||
this.webSocketManager.send({
|
||
type: "pong",
|
||
timestamp: Date.now()
|
||
});
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
} catch (error) {
|
||
if (message.requestId) {
|
||
this.webSocketManager.send({
|
||
type: "error_response",
|
||
requestId: message.requestId,
|
||
error: error instanceof Error ? error.message : String(error)
|
||
});
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 处理展开懒加载对象请求
|
||
*/
|
||
handleExpandLazyObjectRequest(message) {
|
||
try {
|
||
const { entityId, componentIndex, propertyPath, requestId } = message;
|
||
if (entityId === void 0 || componentIndex === void 0 || !propertyPath) {
|
||
this.webSocketManager.send({
|
||
type: "expand_lazy_object_response",
|
||
requestId,
|
||
error: "缺少必要参数"
|
||
});
|
||
return;
|
||
}
|
||
const expandedData = this.entityCollector.expandLazyObject(entityId, componentIndex, propertyPath);
|
||
this.webSocketManager.send({
|
||
type: "expand_lazy_object_response",
|
||
requestId,
|
||
data: expandedData
|
||
});
|
||
} catch (error) {
|
||
this.webSocketManager.send({
|
||
type: "expand_lazy_object_response",
|
||
requestId: message.requestId,
|
||
error: error instanceof Error ? error.message : String(error)
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 处理获取组件属性请求
|
||
*/
|
||
handleGetComponentPropertiesRequest(message) {
|
||
try {
|
||
const { entityId, componentIndex, requestId } = message;
|
||
if (entityId === void 0 || componentIndex === void 0) {
|
||
this.webSocketManager.send({
|
||
type: "get_component_properties_response",
|
||
requestId,
|
||
error: "缺少必要参数"
|
||
});
|
||
return;
|
||
}
|
||
const properties = this.entityCollector.getComponentProperties(entityId, componentIndex);
|
||
this.webSocketManager.send({
|
||
type: "get_component_properties_response",
|
||
requestId,
|
||
data: properties
|
||
});
|
||
} catch (error) {
|
||
this.webSocketManager.send({
|
||
type: "get_component_properties_response",
|
||
requestId: message.requestId,
|
||
error: error instanceof Error ? error.message : String(error)
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 处理获取原始实体列表请求
|
||
*/
|
||
handleGetRawEntityListRequest(message) {
|
||
try {
|
||
const { requestId } = message;
|
||
const rawEntityList = this.entityCollector.getRawEntityList();
|
||
this.webSocketManager.send({
|
||
type: "get_raw_entity_list_response",
|
||
requestId,
|
||
data: rawEntityList
|
||
});
|
||
} catch (error) {
|
||
this.webSocketManager.send({
|
||
type: "get_raw_entity_list_response",
|
||
requestId: message.requestId,
|
||
error: error instanceof Error ? error.message : String(error)
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 处理获取实体详情请求
|
||
*/
|
||
handleGetEntityDetailsRequest(message) {
|
||
try {
|
||
const { entityId, requestId } = message;
|
||
if (entityId === void 0) {
|
||
this.webSocketManager.send({
|
||
type: "get_entity_details_response",
|
||
requestId,
|
||
error: "缺少实体ID参数"
|
||
});
|
||
return;
|
||
}
|
||
const entityDetails = this.entityCollector.getEntityDetails(entityId);
|
||
this.webSocketManager.send({
|
||
type: "get_entity_details_response",
|
||
requestId,
|
||
data: entityDetails
|
||
});
|
||
} catch (error) {
|
||
this.webSocketManager.send({
|
||
type: "get_entity_details_response",
|
||
requestId: message.requestId,
|
||
error: error instanceof Error ? error.message : String(error)
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 处理内存快照请求
|
||
*/
|
||
handleMemorySnapshotRequest() {
|
||
try {
|
||
const memorySnapshot = this.captureMemorySnapshot();
|
||
this.webSocketManager.send({
|
||
type: "memory_snapshot_response",
|
||
data: memorySnapshot
|
||
});
|
||
} catch (error) {
|
||
this.webSocketManager.send({
|
||
type: "memory_snapshot_error",
|
||
error: error instanceof Error ? error.message : "内存快照捕获失败"
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 捕获内存快照
|
||
*/
|
||
captureMemorySnapshot() {
|
||
const timestamp = Date.now();
|
||
const baseMemoryInfo = this.collectBaseMemoryInfo();
|
||
const scene = this.sceneProvider();
|
||
const entityData = this.entityCollector.collectEntityDataWithMemory(scene);
|
||
const componentMemoryStats = scene?.entities ? this.collectComponentMemoryStats(scene.entities) : { totalMemory: 0, componentTypes: 0, totalInstances: 0, breakdown: [] };
|
||
const systemMemoryStats = this.collectSystemMemoryStats();
|
||
const poolMemoryStats = this.collectPoolMemoryStats();
|
||
const performanceStats = this.collectPerformanceStats();
|
||
const totalEntityMemory = entityData.entitiesPerArchetype.reduce((sum, arch) => sum + arch.memory, 0);
|
||
return {
|
||
timestamp,
|
||
version: "2.0",
|
||
summary: {
|
||
totalEntities: entityData.totalEntities,
|
||
totalMemoryUsage: baseMemoryInfo.usedMemory,
|
||
totalMemoryLimit: baseMemoryInfo.totalMemory,
|
||
memoryUtilization: baseMemoryInfo.usedMemory / baseMemoryInfo.totalMemory * 100,
|
||
gcCollections: baseMemoryInfo.gcCollections,
|
||
entityMemory: totalEntityMemory,
|
||
componentMemory: componentMemoryStats.totalMemory,
|
||
systemMemory: systemMemoryStats.totalMemory,
|
||
poolMemory: poolMemoryStats.totalMemory
|
||
},
|
||
baseMemory: baseMemoryInfo,
|
||
entities: {
|
||
totalMemory: totalEntityMemory,
|
||
entityCount: entityData.totalEntities,
|
||
archetypes: entityData.entitiesPerArchetype,
|
||
largestEntities: entityData.topEntitiesByComponents
|
||
},
|
||
components: componentMemoryStats,
|
||
systems: systemMemoryStats,
|
||
pools: poolMemoryStats,
|
||
performance: performanceStats
|
||
};
|
||
}
|
||
/**
|
||
* 收集基础内存信息
|
||
*/
|
||
collectBaseMemoryInfo() {
|
||
const memoryInfo = {
|
||
totalMemory: 0,
|
||
usedMemory: 0,
|
||
freeMemory: 0,
|
||
gcCollections: 0,
|
||
heapInfo: null,
|
||
detailedMemory: void 0
|
||
};
|
||
try {
|
||
const performanceWithMemory = performance;
|
||
if (performanceWithMemory.memory) {
|
||
const perfMemory = performanceWithMemory.memory;
|
||
memoryInfo.totalMemory = perfMemory.jsHeapSizeLimit || 512 * 1024 * 1024;
|
||
memoryInfo.usedMemory = perfMemory.usedJSHeapSize || 0;
|
||
memoryInfo.freeMemory = memoryInfo.totalMemory - memoryInfo.usedMemory;
|
||
memoryInfo.heapInfo = {
|
||
totalJSHeapSize: perfMemory.totalJSHeapSize || 0,
|
||
usedJSHeapSize: perfMemory.usedJSHeapSize || 0,
|
||
jsHeapSizeLimit: perfMemory.jsHeapSizeLimit || 0
|
||
};
|
||
} else {
|
||
memoryInfo.totalMemory = 512 * 1024 * 1024;
|
||
memoryInfo.freeMemory = 512 * 1024 * 1024;
|
||
}
|
||
if (performanceWithMemory.measureUserAgentSpecificMemory) {
|
||
performanceWithMemory.measureUserAgentSpecificMemory().then((result) => {
|
||
memoryInfo.detailedMemory = result;
|
||
}).catch(() => {
|
||
});
|
||
}
|
||
} catch (error) {
|
||
}
|
||
return memoryInfo;
|
||
}
|
||
/**
|
||
* 收集组件内存统计(仅用于内存快照)
|
||
*/
|
||
collectComponentMemoryStats(entityList) {
|
||
const componentStats = /* @__PURE__ */ new Map();
|
||
let totalComponentMemory = 0;
|
||
const componentTypeCounts = /* @__PURE__ */ new Map();
|
||
for (const entity of entityList.buffer) {
|
||
if (!entity || entity.destroyed || !entity.components)
|
||
continue;
|
||
for (const component of entity.components) {
|
||
const typeName = getComponentInstanceTypeName(component);
|
||
componentTypeCounts.set(typeName, (componentTypeCounts.get(typeName) || 0) + 1);
|
||
}
|
||
}
|
||
for (const [typeName, count] of componentTypeCounts.entries()) {
|
||
const detailedMemoryPerInstance = this.componentCollector.calculateDetailedComponentMemory(typeName);
|
||
const totalMemoryForType = detailedMemoryPerInstance * count;
|
||
totalComponentMemory += totalMemoryForType;
|
||
const instances = [];
|
||
let instanceCount = 0;
|
||
for (const entity of entityList.buffer) {
|
||
if (!entity || entity.destroyed || !entity.components)
|
||
continue;
|
||
for (const component of entity.components) {
|
||
if (getComponentInstanceTypeName(component) === typeName) {
|
||
instances.push({
|
||
entityId: entity.id,
|
||
entityName: entity.name || `Entity_${entity.id}`,
|
||
memory: detailedMemoryPerInstance
|
||
// 使用统一的详细计算结果
|
||
});
|
||
instanceCount++;
|
||
if (instanceCount >= 100)
|
||
break;
|
||
}
|
||
}
|
||
if (instanceCount >= 100)
|
||
break;
|
||
}
|
||
componentStats.set(typeName, {
|
||
count,
|
||
totalMemory: totalMemoryForType,
|
||
instances: instances.slice(0, 10)
|
||
// 只保留前10个实例用于显示
|
||
});
|
||
}
|
||
const componentBreakdown = Array.from(componentStats.entries()).map(([typeName, stats]) => ({
|
||
typeName,
|
||
instanceCount: stats.count,
|
||
totalMemory: stats.totalMemory,
|
||
averageMemory: stats.totalMemory / stats.count,
|
||
percentage: totalComponentMemory > 0 ? stats.totalMemory / totalComponentMemory * 100 : 0,
|
||
largestInstances: stats.instances.sort((a, b) => b.memory - a.memory).slice(0, 3)
|
||
})).sort((a, b) => b.totalMemory - a.totalMemory);
|
||
return {
|
||
totalMemory: totalComponentMemory,
|
||
componentTypes: componentStats.size,
|
||
totalInstances: Array.from(componentStats.values()).reduce((sum, stats) => sum + stats.count, 0),
|
||
breakdown: componentBreakdown
|
||
};
|
||
}
|
||
collectSystemMemoryStats() {
|
||
const scene = this.sceneProvider();
|
||
let totalSystemMemory = 0;
|
||
const systemBreakdown = [];
|
||
try {
|
||
const entityProcessors = scene?.entityProcessors;
|
||
if (entityProcessors && entityProcessors.processors) {
|
||
const systemTypeMemoryCache = /* @__PURE__ */ new Map();
|
||
for (const system of entityProcessors.processors) {
|
||
const systemTypeName = getSystemInstanceTypeName(system);
|
||
let systemMemory;
|
||
if (systemTypeMemoryCache.has(systemTypeName)) {
|
||
systemMemory = systemTypeMemoryCache.get(systemTypeName);
|
||
} else {
|
||
systemMemory = this.calculateQuickSystemSize(system);
|
||
systemTypeMemoryCache.set(systemTypeName, systemMemory);
|
||
}
|
||
totalSystemMemory += systemMemory;
|
||
systemBreakdown.push({
|
||
name: systemTypeName,
|
||
memory: systemMemory,
|
||
enabled: system.enabled !== false,
|
||
updateOrder: system.updateOrder || 0
|
||
});
|
||
}
|
||
}
|
||
} catch (error) {
|
||
}
|
||
return {
|
||
totalMemory: totalSystemMemory,
|
||
systemCount: systemBreakdown.length,
|
||
breakdown: systemBreakdown.sort((a, b) => b.memory - a.memory)
|
||
};
|
||
}
|
||
calculateQuickSystemSize(system) {
|
||
if (!system || typeof system !== "object")
|
||
return 64;
|
||
let size = 128;
|
||
try {
|
||
const keys = Object.keys(system);
|
||
for (let i = 0; i < Math.min(keys.length, 15); i++) {
|
||
const key = keys[i];
|
||
if (key === "entities" || key === "scene" || key === "constructor")
|
||
continue;
|
||
const value = system[key];
|
||
size += key.length * 2;
|
||
if (typeof value === "string") {
|
||
size += Math.min(value.length * 2, 100);
|
||
} else if (typeof value === "number") {
|
||
size += 8;
|
||
} else if (typeof value === "boolean") {
|
||
size += 4;
|
||
} else if (Array.isArray(value)) {
|
||
size += 40 + Math.min(value.length * 8, 200);
|
||
} else if (typeof value === "object" && value !== null) {
|
||
size += 64;
|
||
}
|
||
}
|
||
} catch (error) {
|
||
return 128;
|
||
}
|
||
return Math.max(size, 64);
|
||
}
|
||
/**
|
||
* 收集对象池内存统计
|
||
*/
|
||
collectPoolMemoryStats() {
|
||
let totalPoolMemory = 0;
|
||
const poolBreakdown = [];
|
||
try {
|
||
const poolManager = ComponentPoolManager.getInstance();
|
||
const poolStats = poolManager.getPoolStats();
|
||
for (const [typeName, stats] of poolStats.entries()) {
|
||
const poolData = stats;
|
||
const poolMemory = poolData.maxSize * 32;
|
||
totalPoolMemory += poolMemory;
|
||
poolBreakdown.push({
|
||
typeName,
|
||
maxSize: poolData.maxSize,
|
||
currentSize: poolData.currentSize || 0,
|
||
estimatedMemory: poolMemory,
|
||
utilization: poolData.currentSize ? poolData.currentSize / poolData.maxSize * 100 : 0
|
||
});
|
||
}
|
||
} catch (error) {
|
||
}
|
||
try {
|
||
const poolStats = Pool.getAllPoolStats();
|
||
for (const [typeName, stats] of Object.entries(poolStats)) {
|
||
const poolData = stats;
|
||
totalPoolMemory += poolData.estimatedMemoryUsage;
|
||
poolBreakdown.push({
|
||
typeName: `Pool_${typeName}`,
|
||
maxSize: poolData.maxSize,
|
||
currentSize: poolData.size,
|
||
estimatedMemory: poolData.estimatedMemoryUsage,
|
||
utilization: poolData.size / poolData.maxSize * 100,
|
||
hitRate: poolData.hitRate * 100
|
||
});
|
||
}
|
||
} catch (error) {
|
||
}
|
||
return {
|
||
totalMemory: totalPoolMemory,
|
||
poolCount: poolBreakdown.length,
|
||
breakdown: poolBreakdown.sort((a, b) => b.estimatedMemory - a.estimatedMemory)
|
||
};
|
||
}
|
||
/**
|
||
* 收集性能统计信息
|
||
*/
|
||
collectPerformanceStats() {
|
||
try {
|
||
const performanceMonitor = this.performanceMonitorProvider();
|
||
if (!performanceMonitor) {
|
||
return { enabled: false };
|
||
}
|
||
const stats = performanceMonitor.getAllSystemStats();
|
||
const warnings = performanceMonitor.getPerformanceWarnings();
|
||
return {
|
||
enabled: performanceMonitor.enabled ?? false,
|
||
systemCount: stats.size,
|
||
warnings: warnings.slice(0, 10),
|
||
// 最多10个警告
|
||
topSystems: Array.from(stats.entries()).map((entry) => {
|
||
const [name, stat] = entry;
|
||
return {
|
||
name,
|
||
averageTime: stat.averageTime,
|
||
maxTime: stat.maxTime,
|
||
samples: stat.executionCount
|
||
};
|
||
}).sort((a, b) => b.averageTime - a.averageTime).slice(0, 5)
|
||
};
|
||
} catch (error) {
|
||
return { enabled: false, error: error instanceof Error ? error.message : String(error) };
|
||
}
|
||
}
|
||
/**
|
||
* 获取调试数据
|
||
*/
|
||
getDebugData() {
|
||
const currentTime = Date.now();
|
||
const scene = this.sceneProvider();
|
||
const debugData = {
|
||
timestamp: currentTime,
|
||
frameworkVersion: "1.0.0",
|
||
// 可以从package.json读取
|
||
isRunning: this.isRunning,
|
||
frameworkLoaded: true,
|
||
currentScene: scene?.name || "Unknown"
|
||
};
|
||
if (this.config.channels.entities) {
|
||
debugData.entities = this.entityCollector.collectEntityData(scene);
|
||
}
|
||
if (this.config.channels.systems) {
|
||
const performanceMonitor = this.performanceMonitorProvider();
|
||
debugData.systems = this.systemCollector.collectSystemData(performanceMonitor, scene);
|
||
}
|
||
if (this.config.channels.performance) {
|
||
const performanceMonitor = this.performanceMonitorProvider();
|
||
debugData.performance = this.performanceCollector.collectPerformanceData(performanceMonitor);
|
||
}
|
||
if (this.config.channels.components) {
|
||
debugData.components = this.componentCollector.collectComponentData(scene);
|
||
}
|
||
if (this.config.channels.scenes) {
|
||
debugData.scenes = this.sceneCollector.collectSceneData(scene);
|
||
}
|
||
return debugData;
|
||
}
|
||
/**
|
||
* 连接WebSocket
|
||
*/
|
||
async connectWebSocket() {
|
||
try {
|
||
await this.webSocketManager.connect();
|
||
} catch (error) {
|
||
}
|
||
}
|
||
/**
|
||
* 发送调试数据
|
||
*/
|
||
sendDebugData() {
|
||
if (!this.webSocketManager.getConnectionStatus()) {
|
||
return;
|
||
}
|
||
try {
|
||
const debugData = this.getDebugData();
|
||
const message = {
|
||
type: "debug_data",
|
||
data: debugData
|
||
};
|
||
this.webSocketManager.send(message);
|
||
} catch (error) {
|
||
}
|
||
}
|
||
}
|
||
class Core {
|
||
/**
|
||
* 创建核心实例
|
||
*
|
||
* @param config - Core配置对象
|
||
*/
|
||
constructor(config = {}) {
|
||
this._globalManagers = [];
|
||
Core._instance = this;
|
||
this._config = {
|
||
debug: true,
|
||
enableEntitySystems: true,
|
||
...config
|
||
};
|
||
this._timerManager = new TimerManager();
|
||
Core.registerGlobalManager(this._timerManager);
|
||
this._performanceMonitor = PerformanceMonitor.instance;
|
||
if (this._config.debug) {
|
||
this._performanceMonitor.enable();
|
||
}
|
||
this._poolManager = PoolManager.getInstance();
|
||
Core.entitySystemsEnabled = this._config.enableEntitySystems ?? true;
|
||
this.debug = this._config.debug ?? true;
|
||
if (this._config.debugConfig?.enabled) {
|
||
this._debugManager = new DebugManager(this, this._config.debugConfig);
|
||
}
|
||
this.initialize();
|
||
}
|
||
/**
|
||
* 获取核心实例
|
||
*
|
||
* @returns 全局核心实例
|
||
*/
|
||
static get Instance() {
|
||
return this._instance;
|
||
}
|
||
/**
|
||
* 获取当前活动的场景(属性访问器)
|
||
*
|
||
* @returns 当前场景实例,如果没有则返回null
|
||
*/
|
||
static get scene() {
|
||
return this.getScene();
|
||
}
|
||
/**
|
||
* 获取当前活动的场景(方法调用)
|
||
*
|
||
* @returns 当前场景实例,如果没有则返回null
|
||
*/
|
||
static getScene() {
|
||
if (!this._instance) {
|
||
return null;
|
||
}
|
||
this._instance.ensureDefaultWorld();
|
||
const defaultWorld = this._instance._worldManager.getWorld(this.DEFAULT_WORLD_ID);
|
||
return defaultWorld?.getScene(this.DEFAULT_SCENE_ID) || null;
|
||
}
|
||
/**
|
||
* 设置当前场景
|
||
*
|
||
* @param scene - 要设置的场景实例
|
||
* @returns 设置的场景实例,便于链式调用
|
||
*/
|
||
static setScene(scene) {
|
||
if (!this._instance) {
|
||
throw new Error("Core实例未创建,请先调用Core.create()");
|
||
}
|
||
this._instance.ensureDefaultWorld();
|
||
const defaultWorld = this._instance._worldManager.getWorld(this.DEFAULT_WORLD_ID);
|
||
if (defaultWorld.getScene(this.DEFAULT_SCENE_ID)) {
|
||
defaultWorld.removeScene(this.DEFAULT_SCENE_ID);
|
||
}
|
||
defaultWorld.createScene(this.DEFAULT_SCENE_ID, scene);
|
||
defaultWorld.setSceneActive(this.DEFAULT_SCENE_ID, true);
|
||
this._instance.onSceneChanged();
|
||
return scene;
|
||
}
|
||
/**
|
||
* 创建Core实例
|
||
*
|
||
* 如果实例已存在,则返回现有实例。
|
||
*
|
||
* @param config - Core配置,也可以直接传入boolean表示debug模式(向后兼容)
|
||
* @returns Core实例
|
||
*/
|
||
static create(config = true) {
|
||
if (this._instance == null) {
|
||
const coreConfig = typeof config === "boolean" ? { debug: config, enableEntitySystems: true } : config;
|
||
this._instance = new Core(coreConfig);
|
||
}
|
||
return this._instance;
|
||
}
|
||
/**
|
||
* 更新游戏逻辑
|
||
*
|
||
* 此方法应该在游戏引擎的更新循环中调用。
|
||
*
|
||
* @param deltaTime - 外部引擎提供的帧时间间隔(秒)
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // Laya引擎
|
||
* Laya.timer.frameLoop(1, this, () => {
|
||
* const deltaTime = Laya.timer.delta / 1000;
|
||
* Core.update(deltaTime);
|
||
* });
|
||
*
|
||
* // Cocos Creator
|
||
* update(deltaTime: number) {
|
||
* Core.update(deltaTime);
|
||
* }
|
||
*
|
||
|
||
* ```
|
||
*/
|
||
static update(deltaTime) {
|
||
if (!this._instance) {
|
||
Core._logger.warn("Core实例未创建,请先调用Core.create()");
|
||
return;
|
||
}
|
||
this._instance.updateInternal(deltaTime);
|
||
}
|
||
/**
|
||
* 注册全局管理器
|
||
*
|
||
* 将管理器添加到全局管理器列表中,并启用它。
|
||
*
|
||
* @param manager - 要注册的全局管理器
|
||
*/
|
||
static registerGlobalManager(manager) {
|
||
this._instance._globalManagers.push(manager);
|
||
manager.enabled = true;
|
||
}
|
||
/**
|
||
* 注销全局管理器
|
||
*
|
||
* 从全局管理器列表中移除管理器,并禁用它。
|
||
*
|
||
* @param manager - 要注销的全局管理器
|
||
*/
|
||
static unregisterGlobalManager(manager) {
|
||
this._instance._globalManagers.splice(this._instance._globalManagers.indexOf(manager), 1);
|
||
manager.enabled = false;
|
||
}
|
||
/**
|
||
* 获取指定类型的全局管理器
|
||
*
|
||
* @param type - 管理器类型构造函数
|
||
* @returns 管理器实例,如果未找到则返回null
|
||
*/
|
||
static getGlobalManager(type) {
|
||
for (const manager of this._instance._globalManagers) {
|
||
if (manager instanceof type)
|
||
return manager;
|
||
}
|
||
return null;
|
||
}
|
||
/**
|
||
* 调度定时器
|
||
*
|
||
* 创建一个定时器,在指定时间后执行回调函数。
|
||
*
|
||
* @param timeInSeconds - 延迟时间(秒)
|
||
* @param repeats - 是否重复执行,默认为false
|
||
* @param context - 回调函数的上下文,默认为null
|
||
* @param onTime - 定时器触发时的回调函数
|
||
* @returns 创建的定时器实例
|
||
*/
|
||
static schedule(timeInSeconds, repeats = false, context, onTime) {
|
||
if (!onTime) {
|
||
throw new Error("onTime callback is required");
|
||
}
|
||
return this._instance._timerManager.schedule(timeInSeconds, repeats, context, onTime);
|
||
}
|
||
/**
|
||
* 获取ECS流式API
|
||
*
|
||
* @returns ECS API实例,如果未初始化则返回null
|
||
*/
|
||
static get ecsAPI() {
|
||
return this._instance?._ecsAPI || null;
|
||
}
|
||
/**
|
||
* 启用调试功能
|
||
*
|
||
* @param config 调试配置
|
||
*/
|
||
static enableDebug(config) {
|
||
if (!this._instance) {
|
||
Core._logger.warn("Core实例未创建,请先调用Core.create()");
|
||
return;
|
||
}
|
||
if (this._instance._debugManager) {
|
||
this._instance._debugManager.updateConfig(config);
|
||
} else {
|
||
this._instance._debugManager = new DebugManager(this._instance, config);
|
||
}
|
||
this._instance._config.debugConfig = config;
|
||
}
|
||
/**
|
||
* 禁用调试功能
|
||
*/
|
||
static disableDebug() {
|
||
if (!this._instance)
|
||
return;
|
||
if (this._instance._debugManager) {
|
||
this._instance._debugManager.stop();
|
||
this._instance._debugManager = void 0;
|
||
}
|
||
if (this._instance._config.debugConfig) {
|
||
this._instance._config.debugConfig.enabled = false;
|
||
}
|
||
}
|
||
/**
|
||
* 获取调试数据
|
||
*
|
||
* @returns 当前调试数据,如果调试未启用则返回null
|
||
*/
|
||
static getDebugData() {
|
||
if (!this._instance?._debugManager) {
|
||
return null;
|
||
}
|
||
return this._instance._debugManager.getDebugData();
|
||
}
|
||
/**
|
||
* 检查调试是否启用
|
||
*
|
||
* @returns 调试状态
|
||
*/
|
||
static get isDebugEnabled() {
|
||
return this._instance?._config.debugConfig?.enabled || false;
|
||
}
|
||
/**
|
||
* 获取WorldManager实例
|
||
*
|
||
* @param config 可选的WorldManager配置,用于覆盖默认配置
|
||
* @returns WorldManager实例,如果未初始化则自动创建
|
||
*/
|
||
static getWorldManager(config) {
|
||
if (!this._instance) {
|
||
throw new Error("Core实例未创建,请先调用Core.create()");
|
||
}
|
||
if (!this._instance._worldManager) {
|
||
const defaultConfig = {
|
||
maxWorlds: 50,
|
||
autoCleanup: true,
|
||
cleanupInterval: 6e4,
|
||
debug: this._instance._config.debug
|
||
};
|
||
this._instance._worldManager = WorldManager.getInstance({
|
||
...defaultConfig,
|
||
...config
|
||
// 用户传入的配置会覆盖默认配置
|
||
});
|
||
}
|
||
return this._instance._worldManager;
|
||
}
|
||
/**
|
||
* 启用World管理
|
||
*
|
||
* 显式启用World功能,用于多房间/多世界架构
|
||
*
|
||
* @param config 可选的WorldManager配置,用于覆盖默认配置
|
||
*/
|
||
static enableWorldManager(config) {
|
||
return this.getWorldManager(config);
|
||
}
|
||
/**
|
||
* 确保默认World存在
|
||
*
|
||
* 内部方法,用于懒初始化默认World
|
||
*/
|
||
ensureDefaultWorld() {
|
||
if (!this._worldManager) {
|
||
this._worldManager = WorldManager.getInstance({
|
||
maxWorlds: 1,
|
||
// 单场景用户只需要1个World
|
||
autoCleanup: false,
|
||
// 单场景不需要自动清理
|
||
cleanupInterval: 0,
|
||
// 禁用清理定时器
|
||
debug: this._config.debug
|
||
});
|
||
}
|
||
if (!this._worldManager.getWorld(Core.DEFAULT_WORLD_ID)) {
|
||
this._worldManager.createWorld(Core.DEFAULT_WORLD_ID, {
|
||
name: "DefaultWorld",
|
||
maxScenes: 1,
|
||
autoCleanup: false
|
||
});
|
||
this._worldManager.setWorldActive(Core.DEFAULT_WORLD_ID, true);
|
||
}
|
||
}
|
||
/**
|
||
* 场景切换回调
|
||
*
|
||
* 在场景切换时调用,用于重置时间系统等。
|
||
*/
|
||
onSceneChanged() {
|
||
Time.sceneChanged();
|
||
const currentScene = Core.getScene();
|
||
if (currentScene && currentScene.querySystem && currentScene.eventSystem) {
|
||
this._ecsAPI = createECSAPI(currentScene, currentScene.querySystem, currentScene.eventSystem);
|
||
}
|
||
if (this._debugManager) {
|
||
queueMicrotask(() => {
|
||
this._debugManager?.onSceneChanged();
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 初始化核心系统
|
||
*
|
||
* 执行核心系统的初始化逻辑。
|
||
*/
|
||
initialize() {
|
||
}
|
||
/**
|
||
* 内部更新方法
|
||
*
|
||
* @param deltaTime - 帧时间间隔(秒)
|
||
*/
|
||
updateInternal(deltaTime) {
|
||
if (Core.paused)
|
||
return;
|
||
const frameStartTime = this._performanceMonitor.startMonitoring("Core.update");
|
||
Time.update(deltaTime);
|
||
if ("updateFPS" in this._performanceMonitor && typeof this._performanceMonitor.updateFPS === "function") {
|
||
this._performanceMonitor.updateFPS(Time.deltaTime);
|
||
}
|
||
const managersStartTime = this._performanceMonitor.startMonitoring("GlobalManagers.update");
|
||
for (const globalManager of this._globalManagers) {
|
||
if (globalManager.enabled)
|
||
globalManager.update();
|
||
}
|
||
this._performanceMonitor.endMonitoring("GlobalManagers.update", managersStartTime, this._globalManagers.length);
|
||
this._poolManager.update();
|
||
if (this._worldManager) {
|
||
const worldsStartTime = this._performanceMonitor.startMonitoring("Worlds.update");
|
||
const activeWorlds = this._worldManager.getActiveWorlds();
|
||
let totalWorldEntities = 0;
|
||
for (const world of activeWorlds) {
|
||
world.updateGlobalSystems();
|
||
world.updateScenes();
|
||
const worldStats = world.getStats();
|
||
totalWorldEntities += worldStats.totalEntities;
|
||
}
|
||
this._performanceMonitor.endMonitoring("Worlds.update", worldsStartTime, totalWorldEntities);
|
||
}
|
||
if (this._debugManager) {
|
||
this._debugManager.onFrameUpdate(deltaTime);
|
||
}
|
||
this._performanceMonitor.endMonitoring("Core.update", frameStartTime);
|
||
}
|
||
}
|
||
Core.paused = false;
|
||
Core.DEFAULT_WORLD_ID = "__default__";
|
||
Core.DEFAULT_SCENE_ID = "__main__";
|
||
Core._logger = createLogger("Core");
|
||
class Component {
|
||
/**
|
||
* 创建组件实例
|
||
*
|
||
* 自动分配唯一ID给组件。
|
||
*/
|
||
constructor() {
|
||
this._enabled = true;
|
||
this._updateOrder = 0;
|
||
this.id = Component._idGenerator++;
|
||
}
|
||
/**
|
||
* 获取组件启用状态
|
||
*
|
||
* 组件的实际启用状态取决于自身状态和所属实体的状态。
|
||
*
|
||
* @deprecated 不符合ECS架构规范,建议自己实现DisabledComponent标记组件替代
|
||
* @returns 如果组件和所属实体都启用则返回true
|
||
*/
|
||
get enabled() {
|
||
return this.entity ? this.entity.enabled && this._enabled : this._enabled;
|
||
}
|
||
/**
|
||
* 设置组件启用状态
|
||
*
|
||
* 当状态改变时会触发相应的生命周期回调。
|
||
*
|
||
* @deprecated 不符合ECS架构规范,建议自己实现DisabledComponent标记组件替代
|
||
* @param value - 新的启用状态
|
||
*/
|
||
set enabled(value) {
|
||
if (this._enabled !== value) {
|
||
this._enabled = value;
|
||
if (this._enabled) {
|
||
this.onEnabled();
|
||
} else {
|
||
this.onDisabled();
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 获取更新顺序
|
||
*
|
||
* @deprecated 不符合ECS架构规范,更新顺序应该由EntitySystem管理
|
||
* @see EntitySystem
|
||
* @returns 组件的更新顺序值
|
||
*/
|
||
get updateOrder() {
|
||
return this._updateOrder;
|
||
}
|
||
/**
|
||
* 设置更新顺序
|
||
*
|
||
* @deprecated 不符合ECS架构规范,更新顺序应该由EntitySystem管理
|
||
* @see EntitySystem
|
||
* @param value - 新的更新顺序值
|
||
*/
|
||
set updateOrder(value) {
|
||
this._updateOrder = value;
|
||
}
|
||
/**
|
||
* 组件添加到实体时的回调
|
||
*
|
||
* 当组件被添加到实体时调用,可以在此方法中进行初始化操作。
|
||
*/
|
||
onAddedToEntity() {
|
||
}
|
||
/**
|
||
* 组件从实体移除时的回调
|
||
*
|
||
* 当组件从实体中移除时调用,可以在此方法中进行清理操作。
|
||
*/
|
||
onRemovedFromEntity() {
|
||
}
|
||
/**
|
||
* 组件启用时的回调
|
||
*
|
||
* 当组件被启用时调用。
|
||
*/
|
||
onEnabled() {
|
||
}
|
||
/**
|
||
* 组件禁用时的回调
|
||
*
|
||
* 当组件被禁用时调用。
|
||
*/
|
||
onDisabled() {
|
||
}
|
||
/**
|
||
* 更新组件
|
||
*
|
||
* @deprecated 不符合ECS架构规范,建议使用EntitySystem来处理更新逻辑
|
||
*/
|
||
update() {
|
||
}
|
||
}
|
||
Component._idGenerator = 0;
|
||
class Matcher {
|
||
constructor() {
|
||
this.condition = {
|
||
all: [],
|
||
any: [],
|
||
none: []
|
||
};
|
||
}
|
||
/**
|
||
* 创建匹配器,要求所有指定的组件
|
||
* @param types 组件类型
|
||
*/
|
||
static all(...types) {
|
||
const matcher = new Matcher();
|
||
return matcher.all(...types);
|
||
}
|
||
/**
|
||
* 创建匹配器,要求至少一个指定的组件
|
||
* @param types 组件类型
|
||
*/
|
||
static any(...types) {
|
||
const matcher = new Matcher();
|
||
return matcher.any(...types);
|
||
}
|
||
/**
|
||
* 创建匹配器,排除指定的组件
|
||
* @param types 组件类型
|
||
*/
|
||
static none(...types) {
|
||
const matcher = new Matcher();
|
||
return matcher.none(...types);
|
||
}
|
||
/**
|
||
* 创建按标签查询的匙配器
|
||
* @param tag 标签值
|
||
*/
|
||
static byTag(tag) {
|
||
const matcher = new Matcher();
|
||
return matcher.withTag(tag);
|
||
}
|
||
/**
|
||
* 创建按名称查询的匙配器
|
||
* @param name 实体名称
|
||
*/
|
||
static byName(name) {
|
||
const matcher = new Matcher();
|
||
return matcher.withName(name);
|
||
}
|
||
/**
|
||
* 创建单组件查询的匙配器
|
||
* @param componentType 组件类型
|
||
*/
|
||
static byComponent(componentType) {
|
||
const matcher = new Matcher();
|
||
return matcher.withComponent(componentType);
|
||
}
|
||
/**
|
||
* 创建复杂查询构建器
|
||
*/
|
||
static complex() {
|
||
return new Matcher();
|
||
}
|
||
/**
|
||
* 创建空匙配器
|
||
*/
|
||
static empty() {
|
||
return new Matcher();
|
||
}
|
||
/**
|
||
* 必须包含所有指定组件
|
||
* @param types 组件类型
|
||
*/
|
||
all(...types) {
|
||
this.condition.all.push(...types);
|
||
return this;
|
||
}
|
||
/**
|
||
* 必须包含至少一个指定组件
|
||
* @param types 组件类型
|
||
*/
|
||
any(...types) {
|
||
this.condition.any.push(...types);
|
||
return this;
|
||
}
|
||
/**
|
||
* 不能包含任何指定组件
|
||
* @param types 组件类型
|
||
*/
|
||
none(...types) {
|
||
this.condition.none.push(...types);
|
||
return this;
|
||
}
|
||
/**
|
||
* 排除指定组件(别名方法)
|
||
* @param types 组件类型
|
||
*/
|
||
exclude(...types) {
|
||
return this.none(...types);
|
||
}
|
||
/**
|
||
* 至少包含其中之一(别名方法)
|
||
* @param types 组件类型
|
||
*/
|
||
one(...types) {
|
||
return this.any(...types);
|
||
}
|
||
/**
|
||
* 按标签查询
|
||
* @param tag 标签值
|
||
*/
|
||
withTag(tag) {
|
||
this.condition.tag = tag;
|
||
return this;
|
||
}
|
||
/**
|
||
* 按名称查询
|
||
* @param name 实体名称
|
||
*/
|
||
withName(name) {
|
||
this.condition.name = name;
|
||
return this;
|
||
}
|
||
/**
|
||
* 单组件查询
|
||
* @param componentType 组件类型
|
||
*/
|
||
withComponent(componentType) {
|
||
this.condition.component = componentType;
|
||
return this;
|
||
}
|
||
/**
|
||
* 移除标签条件
|
||
*/
|
||
withoutTag() {
|
||
delete this.condition.tag;
|
||
return this;
|
||
}
|
||
/**
|
||
* 移除名称条件
|
||
*/
|
||
withoutName() {
|
||
delete this.condition.name;
|
||
return this;
|
||
}
|
||
/**
|
||
* 移除单组件条件
|
||
*/
|
||
withoutComponent() {
|
||
delete this.condition.component;
|
||
return this;
|
||
}
|
||
/**
|
||
* 获取查询条件(只读)
|
||
*/
|
||
getCondition() {
|
||
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
|
||
};
|
||
}
|
||
/**
|
||
* 检查是否为空条件
|
||
*/
|
||
isEmpty() {
|
||
return this.condition.all.length === 0 && this.condition.any.length === 0 && this.condition.none.length === 0 && this.condition.tag === void 0 && this.condition.name === void 0 && this.condition.component === void 0;
|
||
}
|
||
/**
|
||
* 重置所有条件
|
||
*/
|
||
reset() {
|
||
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;
|
||
}
|
||
/**
|
||
* 克隆匹配器
|
||
*/
|
||
clone() {
|
||
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 !== void 0) {
|
||
cloned.condition.tag = this.condition.tag;
|
||
}
|
||
if (this.condition.name !== void 0) {
|
||
cloned.condition.name = this.condition.name;
|
||
}
|
||
if (this.condition.component !== void 0) {
|
||
cloned.condition.component = this.condition.component;
|
||
}
|
||
return cloned;
|
||
}
|
||
/**
|
||
* 字符串表示
|
||
*/
|
||
toString() {
|
||
const parts = [];
|
||
if (this.condition.all.length > 0) {
|
||
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(", ")})`);
|
||
}
|
||
if (this.condition.none.length > 0) {
|
||
parts.push(`none(${this.condition.none.map((t) => getComponentTypeName(t)).join(", ")})`);
|
||
}
|
||
if (this.condition.tag !== void 0) {
|
||
parts.push(`tag(${this.condition.tag})`);
|
||
}
|
||
if (this.condition.name !== void 0) {
|
||
parts.push(`name(${this.condition.name})`);
|
||
}
|
||
if (this.condition.component !== void 0) {
|
||
parts.push(`component(${getComponentTypeName(this.condition.component)})`);
|
||
}
|
||
return `Matcher[${parts.join(" & ")}]`;
|
||
}
|
||
}
|
||
class EntitySystem {
|
||
/**
|
||
* 获取系统处理的实体列表
|
||
*/
|
||
get entities() {
|
||
if (this._entityCache.frame !== null) {
|
||
return this._entityCache.frame;
|
||
}
|
||
if (this._entityCache.persistent === null) {
|
||
this._entityCache.persistent = this.queryEntities();
|
||
}
|
||
return this._entityCache.persistent;
|
||
}
|
||
/**
|
||
* 获取系统的更新时序
|
||
*/
|
||
get updateOrder() {
|
||
return this._updateOrder;
|
||
}
|
||
set updateOrder(value) {
|
||
this.setUpdateOrder(value);
|
||
}
|
||
/**
|
||
* 获取系统的启用状态
|
||
*/
|
||
get enabled() {
|
||
return this._enabled;
|
||
}
|
||
/**
|
||
* 设置系统的启用状态
|
||
*/
|
||
set enabled(value) {
|
||
this._enabled = value;
|
||
}
|
||
/**
|
||
* 获取系统名称
|
||
*/
|
||
get systemName() {
|
||
return this._systemName;
|
||
}
|
||
constructor(matcher) {
|
||
this._updateOrder = 0;
|
||
this._enabled = true;
|
||
this._performanceMonitor = PerformanceMonitor.instance;
|
||
this._systemName = getSystemInstanceTypeName(this);
|
||
this._initialized = false;
|
||
this._matcher = matcher || Matcher.empty();
|
||
this._eventListeners = [];
|
||
this._scene = null;
|
||
this._entityIdMap = null;
|
||
this._entityIdMapVersion = -1;
|
||
this._entityIdMapSize = 0;
|
||
this._entityCache = {
|
||
frame: null,
|
||
persistent: null,
|
||
tracked: /* @__PURE__ */ new Set(),
|
||
invalidate() {
|
||
this.persistent = null;
|
||
},
|
||
clearFrame() {
|
||
this.frame = null;
|
||
},
|
||
clearAll() {
|
||
this.frame = null;
|
||
this.persistent = null;
|
||
this.tracked.clear();
|
||
}
|
||
};
|
||
}
|
||
/**
|
||
* 这个系统所属的场景
|
||
*/
|
||
get scene() {
|
||
return this._scene;
|
||
}
|
||
set scene(value) {
|
||
this._scene = value;
|
||
}
|
||
/**
|
||
* 获取实体匹配器
|
||
*/
|
||
get matcher() {
|
||
return this._matcher;
|
||
}
|
||
/**
|
||
* 设置更新时序
|
||
* @param order 更新时序
|
||
*/
|
||
setUpdateOrder(order) {
|
||
this._updateOrder = order;
|
||
if (this.scene && this.scene.entityProcessors) {
|
||
this.scene.entityProcessors.setDirty();
|
||
}
|
||
}
|
||
/**
|
||
* 系统初始化(框架调用)
|
||
*
|
||
* 在系统创建时调用。框架内部使用,用户不应直接调用。
|
||
*/
|
||
initialize() {
|
||
if (this._initialized) {
|
||
return;
|
||
}
|
||
this._initialized = true;
|
||
if (this.scene) {
|
||
this._entityCache.invalidate();
|
||
this.queryEntities();
|
||
}
|
||
this.onInitialize();
|
||
}
|
||
/**
|
||
* 系统初始化回调
|
||
*
|
||
* 子类可以重写此方法进行初始化操作。
|
||
*/
|
||
onInitialize() {
|
||
}
|
||
/**
|
||
* 清除实体缓存(内部使用)
|
||
* 当Scene中的实体发生变化时调用
|
||
*/
|
||
clearEntityCache() {
|
||
this._entityCache.invalidate();
|
||
}
|
||
/**
|
||
* 重置系统状态
|
||
*
|
||
* 当系统从场景中移除时调用,重置初始化状态以便重新添加时能正确初始化。
|
||
*/
|
||
reset() {
|
||
this.scene = null;
|
||
this._initialized = false;
|
||
this._entityCache.clearAll();
|
||
this._entityIdMap = null;
|
||
this._entityIdMapVersion = -1;
|
||
this._entityIdMapSize = 0;
|
||
this.cleanupEventListeners();
|
||
this.onDestroy();
|
||
}
|
||
/**
|
||
* 查询匹配的实体
|
||
*/
|
||
queryEntities() {
|
||
if (!this.scene?.querySystem || !this._matcher) {
|
||
return [];
|
||
}
|
||
const condition = this._matcher.getCondition();
|
||
const querySystem = this.scene.querySystem;
|
||
let currentEntities = [];
|
||
if (this._matcher.isEmpty()) {
|
||
currentEntities = querySystem.getAllEntities();
|
||
} else if (this.isSingleCondition(condition)) {
|
||
currentEntities = this.executeSingleConditionQuery(condition, querySystem);
|
||
} else {
|
||
currentEntities = this.executeComplexQuery(condition, querySystem);
|
||
}
|
||
this.updateEntityTracking(currentEntities);
|
||
return currentEntities;
|
||
}
|
||
/**
|
||
* 检查是否为单一条件查询
|
||
*/
|
||
isSingleCondition(condition) {
|
||
const flags = (condition.all.length > 0 ? 1 : 0) | (condition.any.length > 0 ? 2 : 0) | (condition.none.length > 0 ? 4 : 0) | (condition.tag !== void 0 ? 8 : 0) | (condition.name !== void 0 ? 16 : 0) | (condition.component !== void 0 ? 32 : 0);
|
||
return flags !== 0 && (flags & flags - 1) === 0;
|
||
}
|
||
/**
|
||
* 执行单一条件查询
|
||
*/
|
||
executeSingleConditionQuery(condition, querySystem) {
|
||
if (condition.tag !== void 0) {
|
||
return querySystem.queryByTag(condition.tag).entities;
|
||
}
|
||
if (condition.name !== void 0) {
|
||
return querySystem.queryByName(condition.name).entities;
|
||
}
|
||
if (condition.component !== void 0) {
|
||
return querySystem.queryByComponent(condition.component).entities;
|
||
}
|
||
if (condition.all.length > 0 && condition.any.length === 0 && condition.none.length === 0) {
|
||
return querySystem.queryAll(...condition.all).entities;
|
||
}
|
||
if (condition.all.length === 0 && condition.any.length > 0 && condition.none.length === 0) {
|
||
return querySystem.queryAny(...condition.any).entities;
|
||
}
|
||
if (condition.all.length === 0 && condition.any.length === 0 && condition.none.length > 0) {
|
||
return querySystem.queryNone(...condition.none).entities;
|
||
}
|
||
return [];
|
||
}
|
||
/**
|
||
* 执行复合查询
|
||
*/
|
||
executeComplexQueryWithIdSets(condition, querySystem) {
|
||
let resultIds = null;
|
||
if (condition.tag !== void 0) {
|
||
const tagResult = querySystem.queryByTag(condition.tag);
|
||
resultIds = this.extractEntityIds(tagResult.entities);
|
||
}
|
||
if (condition.name !== void 0) {
|
||
const nameIds = this.extractEntityIds(querySystem.queryByName(condition.name).entities);
|
||
resultIds = resultIds ? this.intersectIdSets(resultIds, nameIds) : nameIds;
|
||
}
|
||
if (condition.component !== void 0) {
|
||
const componentIds = this.extractEntityIds(querySystem.queryByComponent(condition.component).entities);
|
||
resultIds = resultIds ? this.intersectIdSets(resultIds, componentIds) : componentIds;
|
||
}
|
||
if (condition.all.length > 0) {
|
||
const allIds = this.extractEntityIds(querySystem.queryAll(...condition.all).entities);
|
||
resultIds = resultIds ? this.intersectIdSets(resultIds, allIds) : allIds;
|
||
}
|
||
if (condition.any.length > 0) {
|
||
const anyIds = this.extractEntityIds(querySystem.queryAny(...condition.any).entities);
|
||
resultIds = resultIds ? this.intersectIdSets(resultIds, anyIds) : anyIds;
|
||
}
|
||
if (condition.none.length > 0) {
|
||
if (!resultIds) {
|
||
resultIds = this.extractEntityIds(querySystem.getAllEntities());
|
||
}
|
||
const noneResult = querySystem.queryAny(...condition.none);
|
||
const noneIds = this.extractEntityIds(noneResult.entities);
|
||
resultIds = this.differenceIdSets(resultIds, noneIds);
|
||
}
|
||
return resultIds ? this.idSetToEntityArray(resultIds, querySystem.getAllEntities()) : [];
|
||
}
|
||
/**
|
||
* 提取实体ID集合
|
||
*/
|
||
extractEntityIds(entities) {
|
||
const len = entities.length;
|
||
const idSet = /* @__PURE__ */ new Set();
|
||
for (let i = 0; i < len; i = i + 1 | 0) {
|
||
idSet.add(entities[i].id | 0);
|
||
}
|
||
return idSet;
|
||
}
|
||
/**
|
||
* ID集合交集运算
|
||
*/
|
||
intersectIdSets(setA, setB) {
|
||
let smaller, larger;
|
||
if (setA.size <= setB.size) {
|
||
smaller = setA;
|
||
larger = setB;
|
||
} else {
|
||
smaller = setB;
|
||
larger = setA;
|
||
}
|
||
const result = /* @__PURE__ */ new Set();
|
||
for (const id of smaller) {
|
||
if (larger.has(id)) {
|
||
result.add(id);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* ID集合差集运算
|
||
*/
|
||
differenceIdSets(setA, setB) {
|
||
const result = /* @__PURE__ */ new Set();
|
||
for (const id of setA) {
|
||
if (!setB.has(id)) {
|
||
result.add(id);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 获取或构建实体ID映射
|
||
*/
|
||
getEntityIdMap(allEntities) {
|
||
const currentVersion = this.scene?.querySystem?.version ?? 0;
|
||
if (this._entityIdMap !== null && this._entityIdMapVersion === currentVersion) {
|
||
return this._entityIdMap;
|
||
}
|
||
return this.rebuildEntityIdMap(allEntities, currentVersion);
|
||
}
|
||
/**
|
||
* 重建实体ID映射
|
||
*/
|
||
rebuildEntityIdMap(allEntities, version) {
|
||
let entityMap = this._entityIdMap;
|
||
if (!entityMap) {
|
||
entityMap = /* @__PURE__ */ new Map();
|
||
} else {
|
||
entityMap.clear();
|
||
}
|
||
const len = allEntities.length;
|
||
for (let i = 0; i < len; i = i + 1 | 0) {
|
||
const entity = allEntities[i];
|
||
entityMap.set(entity.id | 0, entity);
|
||
}
|
||
this._entityIdMap = entityMap;
|
||
this._entityIdMapVersion = version;
|
||
this._entityIdMapSize = len;
|
||
return entityMap;
|
||
}
|
||
/**
|
||
* 从ID集合构建Entity数组
|
||
*/
|
||
idSetToEntityArray(idSet, allEntities) {
|
||
const entityMap = this.getEntityIdMap(allEntities);
|
||
const size = idSet.size;
|
||
const result = new Array(size);
|
||
let index = 0;
|
||
for (const id of idSet) {
|
||
const entity = entityMap.get(id);
|
||
if (entity !== void 0) {
|
||
result[index] = entity;
|
||
index = index + 1 | 0;
|
||
}
|
||
}
|
||
if (index < size) {
|
||
result.length = index;
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 执行复合查询
|
||
*
|
||
* 使用基于ID集合的单次扫描算法进行复杂查询
|
||
*/
|
||
executeComplexQuery(condition, querySystem) {
|
||
return this.executeComplexQueryWithIdSets(condition, querySystem);
|
||
}
|
||
/**
|
||
* 更新系统
|
||
*/
|
||
update() {
|
||
if (!this._enabled || !this.onCheckProcessing()) {
|
||
return;
|
||
}
|
||
const startTime = this._performanceMonitor.startMonitoring(this._systemName);
|
||
let entityCount = 0;
|
||
try {
|
||
this.onBegin();
|
||
this._entityCache.frame = this.queryEntities();
|
||
entityCount = this._entityCache.frame.length;
|
||
this.process(this._entityCache.frame);
|
||
} finally {
|
||
this._performanceMonitor.endMonitoring(this._systemName, startTime, entityCount);
|
||
}
|
||
}
|
||
/**
|
||
* 后期更新系统
|
||
*/
|
||
lateUpdate() {
|
||
if (!this._enabled || !this.onCheckProcessing()) {
|
||
return;
|
||
}
|
||
const startTime = this._performanceMonitor.startMonitoring(`${this._systemName}_Late`);
|
||
let entityCount = 0;
|
||
try {
|
||
const entities = this._entityCache.frame || [];
|
||
entityCount = entities.length;
|
||
this.lateProcess(entities);
|
||
this.onEnd();
|
||
} finally {
|
||
this._performanceMonitor.endMonitoring(`${this._systemName}_Late`, startTime, entityCount);
|
||
this._entityCache.clearFrame();
|
||
}
|
||
}
|
||
/**
|
||
* 在系统处理开始前调用
|
||
*
|
||
* 子类可以重写此方法进行预处理操作。
|
||
*/
|
||
onBegin() {
|
||
}
|
||
/**
|
||
* 处理实体列表
|
||
*
|
||
* 系统的核心逻辑,子类必须实现此方法来定义具体的处理逻辑。
|
||
*
|
||
* @param entities 要处理的实体列表
|
||
*/
|
||
process(entities) {
|
||
}
|
||
/**
|
||
* 后期处理实体列表
|
||
*
|
||
* 在主要处理逻辑之后执行,子类可以重写此方法。
|
||
*
|
||
* @param entities 要处理的实体列表
|
||
*/
|
||
lateProcess(_entities) {
|
||
}
|
||
/**
|
||
* 系统处理完毕后调用
|
||
*
|
||
* 子类可以重写此方法进行后处理操作。
|
||
*/
|
||
onEnd() {
|
||
}
|
||
/**
|
||
* 检查系统是否需要处理
|
||
*
|
||
* 在启用系统时有用,但仅偶尔需要处理。
|
||
* 这只影响处理,不影响事件或订阅列表。
|
||
*
|
||
* @returns 如果系统应该处理,则为true,如果不处理则为false
|
||
*/
|
||
onCheckProcessing() {
|
||
return true;
|
||
}
|
||
/**
|
||
* 获取系统的性能数据
|
||
*
|
||
* @returns 性能数据或undefined
|
||
*/
|
||
getPerformanceData() {
|
||
return this._performanceMonitor.getSystemData(this._systemName);
|
||
}
|
||
/**
|
||
* 获取系统的性能统计
|
||
*
|
||
* @returns 性能统计或undefined
|
||
*/
|
||
getPerformanceStats() {
|
||
return this._performanceMonitor.getSystemStats(this._systemName);
|
||
}
|
||
/**
|
||
* 重置系统的性能数据
|
||
*/
|
||
resetPerformanceData() {
|
||
this._performanceMonitor.resetSystem(this._systemName);
|
||
}
|
||
/**
|
||
* 获取系统信息的字符串表示
|
||
*
|
||
* @returns 系统信息字符串
|
||
*/
|
||
toString() {
|
||
const entityCount = this.entities.length;
|
||
const perfData = this.getPerformanceData();
|
||
const perfInfo = perfData ? ` (${perfData.executionTime.toFixed(2)}ms)` : "";
|
||
return `${this._systemName}[${entityCount} entities]${perfInfo}`;
|
||
}
|
||
/**
|
||
* 更新实体跟踪,检查新增和移除的实体
|
||
*/
|
||
updateEntityTracking(currentEntities) {
|
||
const currentSet = new Set(currentEntities);
|
||
let hasChanged = false;
|
||
for (const entity of currentEntities) {
|
||
if (!this._entityCache.tracked.has(entity)) {
|
||
this._entityCache.tracked.add(entity);
|
||
this.onAdded(entity);
|
||
hasChanged = true;
|
||
}
|
||
}
|
||
for (const entity of this._entityCache.tracked) {
|
||
if (!currentSet.has(entity)) {
|
||
this._entityCache.tracked.delete(entity);
|
||
this.onRemoved(entity);
|
||
hasChanged = true;
|
||
}
|
||
}
|
||
if (hasChanged) {
|
||
this._entityCache.invalidate();
|
||
}
|
||
}
|
||
/**
|
||
* 当实体被添加到系统时调用
|
||
*
|
||
* 子类可以重写此方法来处理实体添加事件。
|
||
*
|
||
* @param entity 被添加的实体
|
||
*/
|
||
onAdded(entity) {
|
||
}
|
||
/**
|
||
* 当实体从系统中移除时调用
|
||
*
|
||
* 子类可以重写此方法来处理实体移除事件。
|
||
*
|
||
* @param entity 被移除的实体
|
||
*/
|
||
onRemoved(entity) {
|
||
}
|
||
/**
|
||
* 添加事件监听器
|
||
*
|
||
* 推荐使用此方法而不是直接调用eventSystem.on(),
|
||
* 这样可以确保系统移除时自动清理监听器,避免内存泄漏。
|
||
*
|
||
* @param eventType 事件类型
|
||
* @param handler 事件处理函数
|
||
* @param config 监听器配置
|
||
*/
|
||
addEventListener(eventType, handler, config) {
|
||
if (!this.scene?.eventSystem) {
|
||
console.warn(`[${this.systemName}] Cannot add event listener: scene.eventSystem not available`);
|
||
return;
|
||
}
|
||
const listenerRef = this.scene.eventSystem.on(eventType, handler, config);
|
||
if (listenerRef) {
|
||
this._eventListeners.push({
|
||
eventSystem: this.scene.eventSystem,
|
||
eventType,
|
||
handler,
|
||
listenerRef
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 移除特定的事件监听器
|
||
*
|
||
* @param eventType 事件类型
|
||
* @param handler 事件处理函数
|
||
*/
|
||
removeEventListener(eventType, handler) {
|
||
const listenerIndex = this._eventListeners.findIndex((listener) => listener.eventType === eventType && listener.handler === handler);
|
||
if (listenerIndex >= 0) {
|
||
const listener = this._eventListeners[listenerIndex];
|
||
listener.eventSystem.off(eventType, listener.listenerRef);
|
||
this._eventListeners.splice(listenerIndex, 1);
|
||
}
|
||
}
|
||
/**
|
||
* 清理所有事件监听器
|
||
*
|
||
* 系统移除时自动调用,清理所有通过addEventListener添加的监听器。
|
||
*/
|
||
cleanupEventListeners() {
|
||
for (const listener of this._eventListeners) {
|
||
try {
|
||
listener.eventSystem.off(listener.eventType, listener.listenerRef);
|
||
} catch (error) {
|
||
console.warn(`[${this.systemName}] Failed to remove event listener for "${listener.eventType}":`, error);
|
||
}
|
||
}
|
||
this._eventListeners.length = 0;
|
||
}
|
||
/**
|
||
* 系统销毁时的回调
|
||
*
|
||
* 当系统从场景中移除时调用,子类可以重写此方法进行清理操作。
|
||
* 注意:事件监听器会被框架自动清理,无需手动处理。
|
||
*/
|
||
onDestroy() {
|
||
}
|
||
}
|
||
class WorkerEntitySystem extends EntitySystem {
|
||
constructor(matcher, config = {}) {
|
||
super(matcher);
|
||
this.workerPool = null;
|
||
this.isProcessing = false;
|
||
this.sharedBuffer = null;
|
||
this.sharedFloatArray = null;
|
||
this.config = {
|
||
enableWorker: config.enableWorker ?? true,
|
||
workerCount: config.workerCount ?? this.getOptimalWorkerCount(),
|
||
systemConfig: config.systemConfig,
|
||
useSharedArrayBuffer: config.useSharedArrayBuffer ?? this.isSharedArrayBufferSupported(),
|
||
entityDataSize: config.entityDataSize ?? this.getDefaultEntityDataSize(),
|
||
maxEntities: config.maxEntities ?? 1e4
|
||
};
|
||
if (this.config.enableWorker && this.isWorkerSupported()) {
|
||
if (this.config.useSharedArrayBuffer) {
|
||
this.initializeSharedArrayBuffer();
|
||
}
|
||
this.initializeWorkerPool();
|
||
}
|
||
}
|
||
/**
|
||
* 检查是否支持Worker
|
||
*/
|
||
isWorkerSupported() {
|
||
return typeof Worker !== "undefined" && typeof Blob !== "undefined";
|
||
}
|
||
/**
|
||
* 检查是否支持SharedArrayBuffer
|
||
*/
|
||
isSharedArrayBufferSupported() {
|
||
return typeof SharedArrayBuffer !== "undefined" && self.crossOriginIsolated;
|
||
}
|
||
/**
|
||
* 获取最优Worker数量
|
||
*/
|
||
getOptimalWorkerCount() {
|
||
if (typeof navigator !== "undefined" && navigator.hardwareConcurrency) {
|
||
return Math.min(navigator.hardwareConcurrency, 4);
|
||
}
|
||
return 2;
|
||
}
|
||
/**
|
||
* 初始化SharedArrayBuffer
|
||
*/
|
||
initializeSharedArrayBuffer() {
|
||
try {
|
||
if (!this.isSharedArrayBufferSupported()) {
|
||
console.warn(`[${this.systemName}] SharedArrayBuffer not supported, falling back to single Worker mode for collision detection integrity`);
|
||
this.config.useSharedArrayBuffer = false;
|
||
this.config.workerCount = 1;
|
||
return;
|
||
}
|
||
const bufferSize = this.config.maxEntities * this.config.entityDataSize * 4;
|
||
this.sharedBuffer = new SharedArrayBuffer(bufferSize);
|
||
this.sharedFloatArray = new Float32Array(this.sharedBuffer);
|
||
console.log(`[${this.systemName}] SharedArrayBuffer initialized successfully (${bufferSize} bytes)`);
|
||
} catch (error) {
|
||
console.warn(`[${this.systemName}] SharedArrayBuffer init failed, falling back to single Worker mode for collision detection integrity:`, error);
|
||
this.config.useSharedArrayBuffer = false;
|
||
this.sharedBuffer = null;
|
||
this.sharedFloatArray = null;
|
||
this.config.workerCount = 1;
|
||
}
|
||
}
|
||
/**
|
||
* 初始化Worker池
|
||
*/
|
||
initializeWorkerPool() {
|
||
try {
|
||
const script = this.createWorkerScript();
|
||
this.workerPool = new WebWorkerPool(
|
||
this.config.workerCount,
|
||
script,
|
||
this.sharedBuffer
|
||
// 传递SharedArrayBuffer给Worker池
|
||
);
|
||
} catch (error) {
|
||
console.error(`[${this.systemName}] Failed to initialize worker pool:`, error);
|
||
this.config.enableWorker = false;
|
||
}
|
||
}
|
||
/**
|
||
* 创建Worker脚本
|
||
*/
|
||
createWorkerScript() {
|
||
const methodStr = this.workerProcess.toString();
|
||
const functionBodyMatch = methodStr.match(/\{([\s\S]*)\}/);
|
||
if (!functionBodyMatch) {
|
||
throw new Error("无法解析workerProcess方法");
|
||
}
|
||
const functionBody = functionBodyMatch[1];
|
||
const entityDataSize = this.config.entityDataSize;
|
||
const sharedProcessMethod = this.getSharedArrayBufferProcessFunction?.() || null;
|
||
let sharedProcessFunctionBody = "";
|
||
if (sharedProcessMethod) {
|
||
const sharedMethodStr = sharedProcessMethod.toString();
|
||
const sharedFunctionBodyMatch = sharedMethodStr.match(/\{([\s\S]*)\}/);
|
||
if (sharedFunctionBodyMatch) {
|
||
sharedProcessFunctionBody = sharedFunctionBodyMatch[1];
|
||
}
|
||
}
|
||
return `
|
||
// Worker脚本 - 支持SharedArrayBuffer
|
||
let sharedFloatArray = null;
|
||
const ENTITY_DATA_SIZE = ${entityDataSize};
|
||
|
||
self.onmessage = function(e) {
|
||
const { type, id, entities, deltaTime, systemConfig, startIndex, endIndex, sharedBuffer } = e.data;
|
||
|
||
|
||
try {
|
||
// 处理SharedArrayBuffer初始化
|
||
if (type === 'init' && sharedBuffer) {
|
||
sharedFloatArray = new Float32Array(sharedBuffer);
|
||
self.postMessage({ type: 'init', success: true });
|
||
return;
|
||
}
|
||
|
||
// 处理SharedArrayBuffer数据
|
||
if (type === 'shared' && sharedFloatArray) {
|
||
processSharedArrayBuffer(startIndex, endIndex, deltaTime, systemConfig);
|
||
self.postMessage({ id, result: null }); // SharedArrayBuffer不需要返回数据
|
||
return;
|
||
}
|
||
|
||
// 传统处理方式
|
||
if (entities) {
|
||
// 定义处理函数
|
||
function workerProcess(entities, deltaTime, systemConfig) {
|
||
${functionBody}
|
||
}
|
||
|
||
// 执行处理
|
||
const result = workerProcess(entities, deltaTime, systemConfig);
|
||
|
||
// 处理Promise返回值
|
||
if (result && typeof result.then === 'function') {
|
||
result.then(finalResult => {
|
||
self.postMessage({ id, result: finalResult });
|
||
}).catch(error => {
|
||
self.postMessage({ id, error: error.message });
|
||
});
|
||
} else {
|
||
self.postMessage({ id, result });
|
||
}
|
||
}
|
||
} catch (error) {
|
||
self.postMessage({ id, error: error.message });
|
||
}
|
||
};
|
||
|
||
// SharedArrayBuffer处理函数 - 由子类定义
|
||
function processSharedArrayBuffer(startIndex, endIndex, deltaTime, systemConfig) {
|
||
if (!sharedFloatArray) return;
|
||
|
||
${sharedProcessFunctionBody ? `
|
||
// 用户定义的处理函数
|
||
const userProcessFunction = function(sharedFloatArray, startIndex, endIndex, deltaTime, systemConfig) {
|
||
${sharedProcessFunctionBody}
|
||
};
|
||
userProcessFunction(sharedFloatArray, startIndex, endIndex, deltaTime, systemConfig);
|
||
` : ``}
|
||
}
|
||
`;
|
||
}
|
||
/**
|
||
* 重写process方法,支持Worker并行处理
|
||
*/
|
||
process(entities) {
|
||
if (this.isProcessing)
|
||
return;
|
||
this.isProcessing = true;
|
||
try {
|
||
if (this.config.enableWorker && this.workerPool) {
|
||
if (this.config.useSharedArrayBuffer && this.sharedFloatArray && this.isSharedArrayBufferSupported()) {
|
||
this.processWithSharedArrayBuffer(entities).finally(() => {
|
||
this.isProcessing = false;
|
||
});
|
||
} else {
|
||
if (this.config.useSharedArrayBuffer) {
|
||
console.log(`[${this.systemName}] Falling back to traditional Worker mode for this frame`);
|
||
}
|
||
this.processWithWorker(entities).finally(() => {
|
||
this.isProcessing = false;
|
||
});
|
||
}
|
||
} else {
|
||
console.log(`[${this.systemName}] Worker not available, processing synchronously`);
|
||
this.processSynchronously(entities);
|
||
this.isProcessing = false;
|
||
}
|
||
} catch (error) {
|
||
this.isProcessing = false;
|
||
console.error(`[${this.systemName}] Processing failed:`, error);
|
||
throw error;
|
||
}
|
||
}
|
||
/**
|
||
* 使用SharedArrayBuffer优化的Worker处理
|
||
*/
|
||
async processWithSharedArrayBuffer(entities) {
|
||
if (!this.sharedFloatArray) {
|
||
throw new Error("SharedArrayBuffer not initialized");
|
||
}
|
||
this.writeEntitiesToSharedBuffer(entities);
|
||
const promises = this.createSharedArrayBufferTasks(entities.length);
|
||
await Promise.all(promises);
|
||
this.readResultsFromSharedBuffer(entities);
|
||
}
|
||
/**
|
||
* 使用Worker并行处理
|
||
*/
|
||
async processWithWorker(entities) {
|
||
const entityData = [];
|
||
for (let i = 0; i < entities.length; i++) {
|
||
entityData[i] = this.extractEntityData(entities[i]);
|
||
}
|
||
const batches = this.createBatches(entityData);
|
||
const deltaTime = Time.deltaTime;
|
||
const promises = batches.map((batch) => this.workerPool.execute({
|
||
entities: batch,
|
||
deltaTime,
|
||
systemConfig: this.config.systemConfig
|
||
}));
|
||
const results = await Promise.all(promises);
|
||
let entityIndex = 0;
|
||
for (const batchResult of results) {
|
||
for (const result of batchResult) {
|
||
if (entityIndex < entities.length) {
|
||
const entity = entities[entityIndex];
|
||
if (entity && result) {
|
||
this.applyResult(entity, result);
|
||
}
|
||
}
|
||
entityIndex++;
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 同步处理(fallback)
|
||
*/
|
||
processSynchronously(entities) {
|
||
const entityData = entities.map((entity) => this.extractEntityData(entity));
|
||
const deltaTime = Time.deltaTime;
|
||
const results = this.workerProcess(entityData, deltaTime, this.config.systemConfig);
|
||
if (results && typeof results.then === "function") {
|
||
results.then((finalResults) => {
|
||
entities.forEach((entity, index) => {
|
||
this.applyResult(entity, finalResults[index]);
|
||
});
|
||
});
|
||
} else {
|
||
entities.forEach((entity, index) => {
|
||
this.applyResult(entity, results[index]);
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 创建数据批次 - 按Worker数量平均分配
|
||
*/
|
||
createBatches(data) {
|
||
const workerCount = this.config.workerCount;
|
||
const batches = [];
|
||
const batchSize = Math.ceil(data.length / workerCount);
|
||
for (let i = 0; i < workerCount; i++) {
|
||
const startIndex = i * batchSize;
|
||
const endIndex = Math.min(startIndex + batchSize, data.length);
|
||
if (startIndex < data.length) {
|
||
batches.push(data.slice(startIndex, endIndex));
|
||
}
|
||
}
|
||
return batches;
|
||
}
|
||
/**
|
||
* 将实体数据写入SharedArrayBuffer
|
||
*/
|
||
writeEntitiesToSharedBuffer(entities) {
|
||
if (!this.sharedFloatArray)
|
||
return;
|
||
for (let i = 0; i < entities.length && i < this.config.maxEntities; i++) {
|
||
const entity = entities[i];
|
||
const data = this.extractEntityData(entity);
|
||
const offset = i * this.config.entityDataSize;
|
||
this.writeEntityToBuffer(data, offset);
|
||
}
|
||
}
|
||
/**
|
||
* 创建SharedArrayBuffer任务
|
||
*/
|
||
createSharedArrayBufferTasks(entityCount) {
|
||
const promises = [];
|
||
const entitiesPerWorker = Math.ceil(entityCount / this.config.workerCount);
|
||
for (let i = 0; i < this.config.workerCount; i++) {
|
||
const startIndex = i * entitiesPerWorker;
|
||
const endIndex = Math.min(startIndex + entitiesPerWorker, entityCount);
|
||
if (startIndex < entityCount) {
|
||
const promise = this.workerPool.executeSharedBuffer({
|
||
startIndex,
|
||
endIndex,
|
||
deltaTime: Time.deltaTime,
|
||
systemConfig: this.config.systemConfig
|
||
});
|
||
promises.push(promise);
|
||
}
|
||
}
|
||
return promises;
|
||
}
|
||
/**
|
||
* 从SharedArrayBuffer读取结果并应用
|
||
*/
|
||
readResultsFromSharedBuffer(entities) {
|
||
if (!this.sharedFloatArray)
|
||
return;
|
||
for (let i = 0; i < entities.length && i < this.config.maxEntities; i++) {
|
||
const entity = entities[i];
|
||
const offset = i * this.config.entityDataSize;
|
||
const result = this.readEntityFromBuffer(offset);
|
||
if (result) {
|
||
this.applyResult(entity, result);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 更新Worker配置
|
||
*/
|
||
updateConfig(newConfig) {
|
||
const oldConfig = { ...this.config };
|
||
Object.assign(this.config, newConfig);
|
||
if (oldConfig.useSharedArrayBuffer !== this.config.useSharedArrayBuffer) {
|
||
this.reinitializeWorkerSystem();
|
||
return;
|
||
}
|
||
if (oldConfig.workerCount !== this.config.workerCount) {
|
||
this.reinitializeWorkerPool();
|
||
return;
|
||
}
|
||
if (!this.config.enableWorker && this.workerPool) {
|
||
this.workerPool.destroy();
|
||
this.workerPool = null;
|
||
}
|
||
if (this.config.enableWorker && !this.workerPool && this.isWorkerSupported()) {
|
||
this.initializeWorkerPool();
|
||
}
|
||
}
|
||
/**
|
||
* 重新初始化整个 Worker 系统(包括 SharedArrayBuffer)
|
||
*/
|
||
reinitializeWorkerSystem() {
|
||
if (this.workerPool) {
|
||
this.workerPool.destroy();
|
||
this.workerPool = null;
|
||
}
|
||
this.sharedBuffer = null;
|
||
this.sharedFloatArray = null;
|
||
if (!this.config.useSharedArrayBuffer) {
|
||
this.config.workerCount = 1;
|
||
}
|
||
if (this.config.enableWorker && this.isWorkerSupported()) {
|
||
if (this.config.useSharedArrayBuffer) {
|
||
this.initializeSharedArrayBuffer();
|
||
}
|
||
this.initializeWorkerPool();
|
||
}
|
||
}
|
||
/**
|
||
* 重新初始化 Worker 池(保持 SharedArrayBuffer)
|
||
*/
|
||
reinitializeWorkerPool() {
|
||
if (this.workerPool) {
|
||
this.workerPool.destroy();
|
||
this.workerPool = null;
|
||
}
|
||
if (this.config.enableWorker && this.isWorkerSupported()) {
|
||
this.initializeWorkerPool();
|
||
}
|
||
}
|
||
/**
|
||
* 获取系统性能信息
|
||
*/
|
||
getWorkerInfo() {
|
||
let currentMode = "sync";
|
||
if (this.config.enableWorker && this.workerPool) {
|
||
if (this.config.useSharedArrayBuffer && this.sharedFloatArray && this.isSharedArrayBufferSupported()) {
|
||
currentMode = "shared-buffer";
|
||
} else {
|
||
currentMode = "worker";
|
||
}
|
||
}
|
||
return {
|
||
enabled: this.config.enableWorker,
|
||
workerCount: this.config.workerCount,
|
||
isProcessing: this.isProcessing,
|
||
sharedArrayBufferSupported: this.isSharedArrayBufferSupported(),
|
||
sharedArrayBufferEnabled: this.config.useSharedArrayBuffer,
|
||
currentMode
|
||
};
|
||
}
|
||
/**
|
||
* 销毁系统时清理Worker池
|
||
*/
|
||
onDestroy() {
|
||
super.onDestroy();
|
||
if (this.workerPool) {
|
||
this.workerPool.destroy();
|
||
this.workerPool = null;
|
||
}
|
||
}
|
||
}
|
||
class WebWorkerPool {
|
||
constructor(workerCount, script, sharedBuffer) {
|
||
this.workers = [];
|
||
this.taskQueue = [];
|
||
this.busyWorkers = /* @__PURE__ */ new Set();
|
||
this.taskCounter = 0;
|
||
this.sharedBuffer = null;
|
||
this.sharedBuffer = sharedBuffer || null;
|
||
const blob = new Blob([script], { type: "application/javascript" });
|
||
const scriptURL = URL.createObjectURL(blob);
|
||
for (let i = 0; i < workerCount; i++) {
|
||
const worker = new Worker(scriptURL);
|
||
worker.onmessage = (e) => this.handleWorkerMessage(i, e.data);
|
||
worker.onerror = (error) => this.handleWorkerError(i, error);
|
||
if (sharedBuffer) {
|
||
worker.postMessage({
|
||
type: "init",
|
||
sharedBuffer
|
||
});
|
||
}
|
||
this.workers.push(worker);
|
||
}
|
||
URL.revokeObjectURL(scriptURL);
|
||
}
|
||
/**
|
||
* 执行SharedArrayBuffer任务
|
||
*/
|
||
executeSharedBuffer(data) {
|
||
return new Promise((resolve, reject) => {
|
||
const task = {
|
||
id: `shared-task-${++this.taskCounter}`,
|
||
data: { ...data, type: "shared" },
|
||
resolve: () => resolve(),
|
||
// SharedArrayBuffer不需要返回数据
|
||
reject
|
||
};
|
||
this.taskQueue.push(task);
|
||
this.processQueue();
|
||
});
|
||
}
|
||
/**
|
||
* 执行任务
|
||
*/
|
||
execute(data) {
|
||
return new Promise((resolve, reject) => {
|
||
const task = {
|
||
id: `task-${++this.taskCounter}`,
|
||
data,
|
||
resolve: (result) => {
|
||
resolve(result);
|
||
},
|
||
reject
|
||
};
|
||
this.taskQueue.push(task);
|
||
this.processQueue();
|
||
});
|
||
}
|
||
/**
|
||
* 处理任务队列
|
||
*/
|
||
processQueue() {
|
||
if (this.taskQueue.length === 0)
|
||
return;
|
||
for (let i = 0; i < this.workers.length; i++) {
|
||
if (!this.busyWorkers.has(i) && this.taskQueue.length > 0) {
|
||
const task = this.taskQueue.shift();
|
||
this.busyWorkers.add(i);
|
||
this.workers[i].postMessage({
|
||
id: task.id,
|
||
...task.data
|
||
});
|
||
this.workers[i]._currentTask = task;
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 处理Worker消息
|
||
*/
|
||
handleWorkerMessage(workerIndex, data) {
|
||
const worker = this.workers[workerIndex];
|
||
const task = worker._currentTask;
|
||
if (!task)
|
||
return;
|
||
this.busyWorkers.delete(workerIndex);
|
||
worker._currentTask = null;
|
||
if (data.error) {
|
||
task.reject(new Error(data.error));
|
||
} else {
|
||
task.resolve(data.result);
|
||
}
|
||
this.processQueue();
|
||
}
|
||
/**
|
||
* 处理Worker错误
|
||
*/
|
||
handleWorkerError(workerIndex, error) {
|
||
const worker = this.workers[workerIndex];
|
||
const task = worker._currentTask;
|
||
if (task) {
|
||
this.busyWorkers.delete(workerIndex);
|
||
worker._currentTask = null;
|
||
task.reject(new Error(error.message));
|
||
}
|
||
this.processQueue();
|
||
}
|
||
/**
|
||
* 销毁Worker池
|
||
*/
|
||
destroy() {
|
||
for (const worker of this.workers) {
|
||
worker.terminate();
|
||
}
|
||
this.workers.length = 0;
|
||
this.taskQueue.length = 0;
|
||
this.busyWorkers.clear();
|
||
}
|
||
}
|
||
var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
|
||
var __decorateClass$3 = (decorators, target, key, kind) => {
|
||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
|
||
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
||
if (decorator = decorators[i])
|
||
result = decorator(result) || result;
|
||
return result;
|
||
};
|
||
let Position = class extends Component {
|
||
constructor(x = 0, y = 0) {
|
||
super();
|
||
this.x = 0;
|
||
this.y = 0;
|
||
this.x = x;
|
||
this.y = y;
|
||
}
|
||
set(x, y) {
|
||
this.x = x;
|
||
this.y = y;
|
||
}
|
||
};
|
||
Position = __decorateClass$3([
|
||
ECSComponent("Position")
|
||
], Position);
|
||
let Velocity = class extends Component {
|
||
constructor(dx = 0, dy = 0) {
|
||
super();
|
||
this.dx = 0;
|
||
this.dy = 0;
|
||
this.dx = dx;
|
||
this.dy = dy;
|
||
}
|
||
set(dx, dy) {
|
||
this.dx = dx;
|
||
this.dy = dy;
|
||
}
|
||
scale(factor) {
|
||
this.dx *= factor;
|
||
this.dy *= factor;
|
||
}
|
||
};
|
||
Velocity = __decorateClass$3([
|
||
ECSComponent("Velocity")
|
||
], Velocity);
|
||
let Physics = class extends Component {
|
||
constructor(mass = 1, bounce = 0.8, friction = 0.95) {
|
||
super();
|
||
this.mass = 1;
|
||
this.bounce = 0.8;
|
||
this.friction = 0.95;
|
||
this.mass = mass;
|
||
this.bounce = bounce;
|
||
this.friction = friction;
|
||
}
|
||
};
|
||
Physics = __decorateClass$3([
|
||
ECSComponent("Physics")
|
||
], Physics);
|
||
let Renderable = class extends Component {
|
||
constructor(color = "#ffffff", size = 5, shape = "circle") {
|
||
super();
|
||
this.color = "#ffffff";
|
||
this.size = 5;
|
||
this.shape = "circle";
|
||
this.color = color;
|
||
this.size = size;
|
||
this.shape = shape;
|
||
}
|
||
};
|
||
Renderable = __decorateClass$3([
|
||
ECSComponent("Renderable")
|
||
], Renderable);
|
||
let Lifetime = class extends Component {
|
||
constructor(maxAge = 5) {
|
||
super();
|
||
this.maxAge = 5;
|
||
this.currentAge = 0;
|
||
this.maxAge = maxAge;
|
||
this.currentAge = 0;
|
||
}
|
||
isDead() {
|
||
return this.currentAge >= this.maxAge;
|
||
}
|
||
};
|
||
Lifetime = __decorateClass$3([
|
||
ECSComponent("Lifetime")
|
||
], Lifetime);
|
||
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
|
||
var __decorateClass$2 = (decorators, target, key, kind) => {
|
||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
|
||
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
||
if (decorator = decorators[i])
|
||
result = decorator(result) || result;
|
||
return result;
|
||
};
|
||
let PhysicsWorkerSystem = class extends WorkerEntitySystem {
|
||
constructor(enableWorker = true) {
|
||
const defaultConfig = {
|
||
gravity: 100,
|
||
canvasWidth: 800,
|
||
canvasHeight: 600,
|
||
groundFriction: 0.98
|
||
};
|
||
const isSharedArrayBufferAvailable = typeof SharedArrayBuffer !== "undefined" && self.crossOriginIsolated;
|
||
super(
|
||
Matcher.empty().all(Position, Velocity, Physics),
|
||
{
|
||
enableWorker,
|
||
// 当 SharedArrayBuffer 可用时使用多 Worker,否则使用单 Worker 保证碰撞检测完整性
|
||
workerCount: isSharedArrayBufferAvailable ? navigator.hardwareConcurrency || 2 : 1,
|
||
systemConfig: defaultConfig,
|
||
useSharedArrayBuffer: true
|
||
// 优先使用 SharedArrayBuffer
|
||
}
|
||
);
|
||
this.physicsConfig = {
|
||
gravity: 100,
|
||
canvasWidth: 800,
|
||
canvasHeight: 600,
|
||
groundFriction: 0.98
|
||
// 减少地面摩擦
|
||
};
|
||
this.startTime = 0;
|
||
}
|
||
extractEntityData(entity) {
|
||
const position = entity.getComponent(Position);
|
||
const velocity = entity.getComponent(Velocity);
|
||
const physics = entity.getComponent(Physics);
|
||
const renderable = entity.getComponent(Renderable);
|
||
return {
|
||
id: entity.id,
|
||
x: position.x,
|
||
y: position.y,
|
||
dx: velocity.dx,
|
||
dy: velocity.dy,
|
||
mass: physics.mass,
|
||
bounce: physics.bounce,
|
||
friction: physics.friction,
|
||
radius: renderable.size
|
||
};
|
||
}
|
||
/**
|
||
* Worker处理函数 - 纯函数,会被序列化到Worker中执行
|
||
* 注意:这个函数内部不能访问外部变量,必须是纯函数
|
||
* 非SharedArrayBuffer模式:每个Worker只能看到分配给它的实体批次
|
||
* 这会导致跨批次的碰撞检测缺失,但单批次内的碰撞是正确的
|
||
*/
|
||
workerProcess(entities, deltaTime, systemConfig) {
|
||
const config = systemConfig || {
|
||
gravity: 100,
|
||
canvasWidth: 800,
|
||
canvasHeight: 600,
|
||
groundFriction: 0.98
|
||
};
|
||
const result = entities.map((e) => ({ ...e }));
|
||
for (let i = 0; i < result.length; i++) {
|
||
const entity = result[i];
|
||
entity.dy += config.gravity * deltaTime;
|
||
entity.x += entity.dx * deltaTime;
|
||
entity.y += entity.dy * deltaTime;
|
||
if (entity.x <= entity.radius) {
|
||
entity.x = entity.radius;
|
||
entity.dx = -entity.dx * entity.bounce;
|
||
} else if (entity.x >= config.canvasWidth - entity.radius) {
|
||
entity.x = config.canvasWidth - entity.radius;
|
||
entity.dx = -entity.dx * entity.bounce;
|
||
}
|
||
if (entity.y <= entity.radius) {
|
||
entity.y = entity.radius;
|
||
entity.dy = -entity.dy * entity.bounce;
|
||
} else if (entity.y >= config.canvasHeight - entity.radius) {
|
||
entity.y = config.canvasHeight - entity.radius;
|
||
entity.dy = -entity.dy * entity.bounce;
|
||
entity.dx *= config.groundFriction;
|
||
}
|
||
entity.dx *= entity.friction;
|
||
entity.dy *= entity.friction;
|
||
}
|
||
for (let i = 0; i < result.length; i++) {
|
||
for (let j = i + 1; j < result.length; j++) {
|
||
const ball1 = result[i];
|
||
const ball2 = result[j];
|
||
const dx = ball2.x - ball1.x;
|
||
const dy = ball2.y - ball1.y;
|
||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||
const minDistance = ball1.radius + ball2.radius;
|
||
if (distance < minDistance && distance > 0) {
|
||
const nx = dx / distance;
|
||
const ny = dy / distance;
|
||
const overlap = minDistance - distance;
|
||
const separationX = nx * overlap * 0.5;
|
||
const separationY = ny * overlap * 0.5;
|
||
ball1.x -= separationX;
|
||
ball1.y -= separationY;
|
||
ball2.x += separationX;
|
||
ball2.y += separationY;
|
||
const relativeVelocityX = ball2.dx - ball1.dx;
|
||
const relativeVelocityY = ball2.dy - ball1.dy;
|
||
const velocityAlongNormal = relativeVelocityX * nx + relativeVelocityY * ny;
|
||
if (velocityAlongNormal > 0) continue;
|
||
const restitution = (ball1.bounce + ball2.bounce) * 0.5;
|
||
const impulseScalar = -(1 + restitution) * velocityAlongNormal / (1 / ball1.mass + 1 / ball2.mass);
|
||
const impulseX = impulseScalar * nx;
|
||
const impulseY = impulseScalar * ny;
|
||
ball1.dx -= impulseX / ball1.mass;
|
||
ball1.dy -= impulseY / ball1.mass;
|
||
ball2.dx += impulseX / ball2.mass;
|
||
ball2.dy += impulseY / ball2.mass;
|
||
const energyLoss = 0.98;
|
||
ball1.dx *= energyLoss;
|
||
ball1.dy *= energyLoss;
|
||
ball2.dx *= energyLoss;
|
||
ball2.dy *= energyLoss;
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/**
|
||
* 应用处理结果
|
||
*/
|
||
applyResult(entity, result) {
|
||
if (!entity || !entity.enabled) {
|
||
return;
|
||
}
|
||
const position = entity.getComponent(Position);
|
||
const velocity = entity.getComponent(Velocity);
|
||
if (!position || !velocity) {
|
||
return;
|
||
}
|
||
position.set(result.x, result.y);
|
||
velocity.set(result.dx, result.dy);
|
||
}
|
||
/**
|
||
* 更新物理配置
|
||
*/
|
||
updatePhysicsConfig(newConfig) {
|
||
Object.assign(this.physicsConfig, newConfig);
|
||
this.updateConfig({ systemConfig: this.physicsConfig });
|
||
}
|
||
/**
|
||
* 获取物理配置
|
||
*/
|
||
getPhysicsConfig() {
|
||
return { ...this.physicsConfig };
|
||
}
|
||
/**
|
||
* 禁用 SharedArrayBuffer(用于测试降级行为)
|
||
*/
|
||
disableSharedArrayBuffer() {
|
||
console.log(`[${this.systemName}] Disabling SharedArrayBuffer for testing - falling back to single Worker mode`);
|
||
this.updateConfig({
|
||
useSharedArrayBuffer: false
|
||
});
|
||
}
|
||
/**
|
||
* 获取当前运行状态
|
||
*/
|
||
getCurrentStatus() {
|
||
const workerInfo = this.getWorkerInfo();
|
||
let mode = "sync";
|
||
if (workerInfo.enabled) {
|
||
if (workerInfo.sharedArrayBufferEnabled && workerInfo.sharedArrayBufferSupported) {
|
||
mode = "shared-buffer";
|
||
} else if (workerInfo.workerCount === 1) {
|
||
mode = "single-worker";
|
||
} else {
|
||
mode = "multi-worker";
|
||
}
|
||
}
|
||
return {
|
||
mode,
|
||
sharedArrayBufferEnabled: workerInfo.sharedArrayBufferEnabled,
|
||
workerCount: workerInfo.workerCount,
|
||
workerEnabled: workerInfo.enabled
|
||
};
|
||
}
|
||
/**
|
||
* 性能监控
|
||
*/
|
||
onEnd() {
|
||
super.onEnd();
|
||
const endTime = performance.now();
|
||
const executionTime = endTime - this.startTime;
|
||
window.physicsExecutionTime = executionTime;
|
||
}
|
||
/**
|
||
* 获取实体数据大小
|
||
*/
|
||
getDefaultEntityDataSize() {
|
||
return 9;
|
||
}
|
||
/**
|
||
* 将实体数据写入SharedArrayBuffer
|
||
*/
|
||
writeEntityToBuffer(entityData, offset) {
|
||
const sharedArray = this.sharedFloatArray;
|
||
if (!sharedArray) return;
|
||
const currentEntityCount = Math.floor(offset / 9) + 1;
|
||
sharedArray[0] = currentEntityCount;
|
||
const dataOffset = offset + 9;
|
||
sharedArray[dataOffset + 0] = entityData.id;
|
||
sharedArray[dataOffset + 1] = entityData.x;
|
||
sharedArray[dataOffset + 2] = entityData.y;
|
||
sharedArray[dataOffset + 3] = entityData.dx;
|
||
sharedArray[dataOffset + 4] = entityData.dy;
|
||
sharedArray[dataOffset + 5] = entityData.mass;
|
||
sharedArray[dataOffset + 6] = entityData.bounce;
|
||
sharedArray[dataOffset + 7] = entityData.friction;
|
||
sharedArray[dataOffset + 8] = entityData.radius;
|
||
}
|
||
/**
|
||
* 性能监控开始
|
||
*/
|
||
onBegin() {
|
||
super.onBegin();
|
||
this.startTime = performance.now();
|
||
}
|
||
/**
|
||
* 从SharedArrayBuffer读取实体数据
|
||
*/
|
||
readEntityFromBuffer(offset) {
|
||
const sharedArray = this.sharedFloatArray;
|
||
if (!sharedArray) return null;
|
||
const dataOffset = offset + 9;
|
||
return {
|
||
id: sharedArray[dataOffset + 0],
|
||
x: sharedArray[dataOffset + 1],
|
||
y: sharedArray[dataOffset + 2],
|
||
dx: sharedArray[dataOffset + 3],
|
||
dy: sharedArray[dataOffset + 4],
|
||
mass: sharedArray[dataOffset + 5],
|
||
bounce: sharedArray[dataOffset + 6],
|
||
friction: sharedArray[dataOffset + 7],
|
||
radius: sharedArray[dataOffset + 8]
|
||
};
|
||
}
|
||
/**
|
||
* SharedArrayBuffer处理函数
|
||
*/
|
||
getSharedArrayBufferProcessFunction() {
|
||
return function(sharedFloatArray, startIndex, endIndex, deltaTime, systemConfig) {
|
||
const config = systemConfig || {
|
||
gravity: 100,
|
||
canvasWidth: 800,
|
||
canvasHeight: 600,
|
||
groundFriction: 0.98
|
||
};
|
||
const actualEntityCount = sharedFloatArray[0];
|
||
for (let i = startIndex; i < endIndex && i < actualEntityCount; i++) {
|
||
const offset = i * 9 + 9;
|
||
const id = sharedFloatArray[offset + 0];
|
||
if (id === 0) continue;
|
||
let x = sharedFloatArray[offset + 1];
|
||
let y = sharedFloatArray[offset + 2];
|
||
let dx = sharedFloatArray[offset + 3];
|
||
let dy = sharedFloatArray[offset + 4];
|
||
const bounce = sharedFloatArray[offset + 6];
|
||
const friction = sharedFloatArray[offset + 7];
|
||
const radius = sharedFloatArray[offset + 8];
|
||
dy += config.gravity * deltaTime;
|
||
x += dx * deltaTime;
|
||
y += dy * deltaTime;
|
||
if (x <= radius) {
|
||
x = radius;
|
||
dx = -dx * bounce;
|
||
} else if (x >= config.canvasWidth - radius) {
|
||
x = config.canvasWidth - radius;
|
||
dx = -dx * bounce;
|
||
}
|
||
if (y <= radius) {
|
||
y = radius;
|
||
dy = -dy * bounce;
|
||
} else if (y >= config.canvasHeight - radius) {
|
||
y = config.canvasHeight - radius;
|
||
dy = -dy * bounce;
|
||
dx *= config.groundFriction;
|
||
}
|
||
dx *= friction;
|
||
dy *= friction;
|
||
sharedFloatArray[offset + 1] = x;
|
||
sharedFloatArray[offset + 2] = y;
|
||
sharedFloatArray[offset + 3] = dx;
|
||
sharedFloatArray[offset + 4] = dy;
|
||
}
|
||
for (let i = startIndex; i < endIndex && i < actualEntityCount; i++) {
|
||
const offset1 = i * 9 + 9;
|
||
const id1 = sharedFloatArray[offset1 + 0];
|
||
if (id1 === 0) continue;
|
||
let x1 = sharedFloatArray[offset1 + 1];
|
||
let y1 = sharedFloatArray[offset1 + 2];
|
||
let dx1 = sharedFloatArray[offset1 + 3];
|
||
let dy1 = sharedFloatArray[offset1 + 4];
|
||
const mass1 = sharedFloatArray[offset1 + 5];
|
||
const bounce1 = sharedFloatArray[offset1 + 6];
|
||
const radius1 = sharedFloatArray[offset1 + 8];
|
||
for (let j = 0; j < actualEntityCount; j++) {
|
||
if (i === j) continue;
|
||
const offset2 = j * 9 + 9;
|
||
const id2 = sharedFloatArray[offset2 + 0];
|
||
if (id2 === 0) continue;
|
||
const x2 = sharedFloatArray[offset2 + 1];
|
||
const y2 = sharedFloatArray[offset2 + 2];
|
||
const dx2 = sharedFloatArray[offset2 + 3];
|
||
const dy2 = sharedFloatArray[offset2 + 4];
|
||
const mass2 = sharedFloatArray[offset2 + 5];
|
||
const bounce2 = sharedFloatArray[offset2 + 6];
|
||
const radius2 = sharedFloatArray[offset2 + 8];
|
||
if (isNaN(x2) || isNaN(y2) || isNaN(radius2) || radius2 <= 0) continue;
|
||
const deltaX = x2 - x1;
|
||
const deltaY = y2 - y1;
|
||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||
const minDistance = radius1 + radius2;
|
||
if (distance < minDistance && distance > 0) {
|
||
const nx = deltaX / distance;
|
||
const ny = deltaY / distance;
|
||
const overlap = minDistance - distance;
|
||
const separationX = nx * overlap * 0.5;
|
||
const separationY = ny * overlap * 0.5;
|
||
x1 -= separationX;
|
||
y1 -= separationY;
|
||
const relativeVelocityX = dx2 - dx1;
|
||
const relativeVelocityY = dy2 - dy1;
|
||
const velocityAlongNormal = relativeVelocityX * nx + relativeVelocityY * ny;
|
||
if (velocityAlongNormal > 0) continue;
|
||
const restitution = (bounce1 + bounce2) * 0.5;
|
||
const impulseScalar = -(1 + restitution) * velocityAlongNormal / (1 / mass1 + 1 / mass2);
|
||
const impulseX = impulseScalar * nx;
|
||
const impulseY = impulseScalar * ny;
|
||
dx1 -= impulseX / mass1;
|
||
dy1 -= impulseY / mass1;
|
||
const energyLoss = 0.98;
|
||
dx1 *= energyLoss;
|
||
dy1 *= energyLoss;
|
||
}
|
||
}
|
||
sharedFloatArray[offset1 + 1] = x1;
|
||
sharedFloatArray[offset1 + 2] = y1;
|
||
sharedFloatArray[offset1 + 3] = dx1;
|
||
sharedFloatArray[offset1 + 4] = dy1;
|
||
}
|
||
};
|
||
}
|
||
};
|
||
PhysicsWorkerSystem = __decorateClass$2([
|
||
ECSSystem("PhysicsWorkerSystem")
|
||
], PhysicsWorkerSystem);
|
||
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
||
var __decorateClass$1 = (decorators, target, key, kind) => {
|
||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
||
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
||
if (decorator = decorators[i])
|
||
result = decorator(result) || result;
|
||
return result;
|
||
};
|
||
let RenderSystem = class extends EntitySystem {
|
||
constructor(canvas) {
|
||
super(Matcher.empty().all(Position, Renderable));
|
||
this.startTime = 0;
|
||
this.batchCount = 0;
|
||
this.drawCallCount = 0;
|
||
this.canvas = canvas;
|
||
this.ctx = canvas.getContext("2d");
|
||
}
|
||
onBegin() {
|
||
super.onBegin();
|
||
this.startTime = performance.now();
|
||
this.ctx.fillStyle = "#000000";
|
||
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||
}
|
||
process(entities) {
|
||
let lastColor = "";
|
||
this.drawCallCount = 0;
|
||
for (const entity of entities) {
|
||
const position = entity.getComponent(Position);
|
||
const renderable = entity.getComponent(Renderable);
|
||
if (renderable.color !== lastColor) {
|
||
this.ctx.fillStyle = renderable.color;
|
||
lastColor = renderable.color;
|
||
}
|
||
if (renderable.shape === "circle") {
|
||
this.ctx.beginPath();
|
||
this.ctx.arc(position.x, position.y, renderable.size, 0, Math.PI * 2);
|
||
this.ctx.fill();
|
||
this.drawCallCount++;
|
||
} else if (renderable.shape === "square") {
|
||
this.ctx.fillRect(
|
||
position.x - renderable.size / 2,
|
||
position.y - renderable.size / 2,
|
||
renderable.size,
|
||
renderable.size
|
||
);
|
||
this.drawCallCount++;
|
||
}
|
||
}
|
||
const uniqueColors = new Set(entities.map((e) => e.getComponent(Renderable).color));
|
||
this.batchCount = uniqueColors.size;
|
||
}
|
||
onEnd() {
|
||
super.onEnd();
|
||
const endTime = performance.now();
|
||
const executionTime = endTime - this.startTime;
|
||
window.renderExecutionTime = executionTime;
|
||
this.drawDebugInfo();
|
||
}
|
||
drawDebugInfo() {
|
||
const entities = this.entities;
|
||
this.ctx.fillStyle = "#00ff00";
|
||
this.ctx.font = "14px Arial";
|
||
this.ctx.fillText(`实体数量: ${entities.length}`, 10, 20);
|
||
this.ctx.fillText(`渲染批次: ${this.batchCount}`, 10, 140);
|
||
this.ctx.fillText(`绘制调用: ${this.drawCallCount}`, 10, 160);
|
||
const workerInfo = window.workerInfo;
|
||
if (workerInfo) {
|
||
this.ctx.fillStyle = workerInfo.enabled ? "#00ff00" : "#ff0000";
|
||
this.ctx.fillText(`Worker: ${workerInfo.enabled ? "启用" : "禁用"}`, 10, 40);
|
||
if (workerInfo.enabled) {
|
||
this.ctx.fillStyle = "#ffff00";
|
||
const entitiesPerWorker = Math.ceil(entities.length / workerInfo.workerCount);
|
||
this.ctx.fillText(`每个Worker实体: ${entitiesPerWorker}`, 10, 60);
|
||
this.ctx.fillText(`Worker数量: ${workerInfo.workerCount}`, 10, 80);
|
||
}
|
||
}
|
||
const physicsTime = window.physicsExecutionTime || 0;
|
||
const renderTime = window.renderExecutionTime || 0;
|
||
this.ctx.fillStyle = physicsTime > 16 ? "#ff0000" : physicsTime > 8 ? "#ffff00" : "#00ff00";
|
||
this.ctx.fillText(`物理: ${physicsTime.toFixed(2)}ms`, 10, 100);
|
||
this.ctx.fillStyle = renderTime > 16 ? "#ff0000" : renderTime > 8 ? "#ffff00" : "#00ff00";
|
||
this.ctx.fillText(`渲染: ${renderTime.toFixed(2)}ms`, 10, 120);
|
||
}
|
||
};
|
||
RenderSystem = __decorateClass$1([
|
||
ECSSystem("RenderSystem")
|
||
], RenderSystem);
|
||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||
var __decorateClass = (decorators, target, key, kind) => {
|
||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
||
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
||
if (decorator = decorators[i])
|
||
result = decorator(result) || result;
|
||
return result;
|
||
};
|
||
let LifetimeSystem = class extends EntitySystem {
|
||
constructor() {
|
||
super(Matcher.empty().all(Lifetime));
|
||
}
|
||
process(entities) {
|
||
const entitiesToRemove = [];
|
||
for (const entity of entities) {
|
||
const lifetime = entity.getComponent(Lifetime);
|
||
lifetime.currentAge += Time.deltaTime;
|
||
if (lifetime.isDead()) {
|
||
entitiesToRemove.push(entity);
|
||
}
|
||
}
|
||
for (const entity of entitiesToRemove) {
|
||
entity.destroy();
|
||
}
|
||
}
|
||
};
|
||
LifetimeSystem = __decorateClass([
|
||
ECSSystem("LifetimeSystem")
|
||
], LifetimeSystem);
|
||
class GameScene extends Scene {
|
||
constructor(canvas) {
|
||
super();
|
||
this.canvas = canvas;
|
||
}
|
||
initialize() {
|
||
this.name = "WorkerDemoScene";
|
||
this.physicsSystem = new PhysicsWorkerSystem(true);
|
||
this.renderSystem = new RenderSystem(this.canvas);
|
||
this.lifetimeSystem = new LifetimeSystem();
|
||
this.physicsSystem.updateOrder = 1;
|
||
this.lifetimeSystem.updateOrder = 2;
|
||
this.renderSystem.updateOrder = 3;
|
||
this.addSystem(this.physicsSystem);
|
||
this.addSystem(this.lifetimeSystem);
|
||
this.addSystem(this.renderSystem);
|
||
}
|
||
onStart() {
|
||
console.log("Worker演示场景已启动");
|
||
this.spawnInitialEntities();
|
||
}
|
||
unload() {
|
||
console.log("Worker演示场景已卸载");
|
||
}
|
||
/**
|
||
* 生成初始实体
|
||
*/
|
||
spawnInitialEntities(count = 1e3) {
|
||
this.clearAllEntities();
|
||
for (let i = 0; i < count; i++) {
|
||
this.createParticle();
|
||
}
|
||
}
|
||
/**
|
||
* 创建一个粒子实体
|
||
*/
|
||
createParticle() {
|
||
const entity = this.createEntity(`Particle_${Date.now()}_${Math.random()}`);
|
||
const x = Math.random() * (this.canvas.width - 20) + 10;
|
||
const y = Math.random() * (this.canvas.height - 20) + 10;
|
||
const dx = (Math.random() - 0.5) * 200;
|
||
const dy = (Math.random() - 0.5) * 200;
|
||
const mass = Math.random() * 3 + 2;
|
||
const bounce = 0.85 + Math.random() * 0.15;
|
||
const friction = 0.998 + Math.random() * 2e-3;
|
||
const colors = [
|
||
"#ff4444",
|
||
"#44ff44",
|
||
"#4444ff",
|
||
"#ffff44",
|
||
"#ff44ff",
|
||
"#44ffff",
|
||
"#ffffff",
|
||
"#ff8844",
|
||
"#88ff44",
|
||
"#4488ff",
|
||
"#ff4488",
|
||
"#88ff88",
|
||
"#8888ff",
|
||
"#ffaa44",
|
||
"#aaff44",
|
||
"#44aaff",
|
||
"#ff44aa",
|
||
"#aa44ff",
|
||
"#44ffaa",
|
||
"#cccccc"
|
||
];
|
||
const color = colors[Math.floor(Math.random() * colors.length)];
|
||
const size = Math.random() * 6 + 3;
|
||
entity.addComponent(new Position(x, y));
|
||
entity.addComponent(new Velocity(dx, dy));
|
||
entity.addComponent(new Physics(mass, bounce, friction));
|
||
entity.addComponent(new Renderable(color, size, "circle"));
|
||
entity.addComponent(new Lifetime(5 + Math.random() * 10));
|
||
}
|
||
/**
|
||
* 生成粒子爆发效果
|
||
*/
|
||
spawnParticleExplosion(centerX, centerY, count = 50) {
|
||
for (let i = 0; i < count; i++) {
|
||
const entity = this.createEntity(`Explosion_${Date.now()}_${i}`);
|
||
const angle = Math.PI * 2 * i / count + (Math.random() - 0.5) * 0.5;
|
||
const distance = Math.random() * 30;
|
||
const x = centerX + Math.cos(angle) * distance;
|
||
const y = centerY + Math.sin(angle) * distance;
|
||
const speed = 100 + Math.random() * 150;
|
||
const dx = Math.cos(angle) * speed;
|
||
const dy = Math.sin(angle) * speed;
|
||
const mass = 0.5 + Math.random() * 1;
|
||
const bounce = 0.8 + Math.random() * 0.2;
|
||
const colors = ["#ffaa00", "#ff6600", "#ff0066", "#ff3300", "#ffff00"];
|
||
const color = colors[Math.floor(Math.random() * colors.length)];
|
||
const size = Math.random() * 4 + 2;
|
||
entity.addComponent(new Position(x, y));
|
||
entity.addComponent(new Velocity(dx, dy));
|
||
entity.addComponent(new Physics(mass, bounce, 0.999));
|
||
entity.addComponent(new Renderable(color, size, "circle"));
|
||
entity.addComponent(new Lifetime(2 + Math.random() * 3));
|
||
}
|
||
}
|
||
/**
|
||
* 清空所有实体
|
||
*/
|
||
clearAllEntities() {
|
||
const entities = [...this.entities.buffer];
|
||
for (const entity of entities) {
|
||
entity.destroy();
|
||
}
|
||
}
|
||
/**
|
||
* 切换Worker启用状态
|
||
*/
|
||
toggleWorker() {
|
||
const workerInfo = this.physicsSystem.getWorkerInfo();
|
||
const newWorkerEnabled = !workerInfo.enabled;
|
||
this.removeSystem(this.physicsSystem);
|
||
this.physicsSystem = new PhysicsWorkerSystem(newWorkerEnabled);
|
||
this.physicsSystem.updateOrder = 1;
|
||
this.addSystem(this.physicsSystem);
|
||
return newWorkerEnabled;
|
||
}
|
||
/**
|
||
* 更新Worker配置
|
||
*/
|
||
updateWorkerConfig(config) {
|
||
if (config.gravity !== void 0 || config.friction !== void 0) {
|
||
const physicsConfig = this.physicsSystem.getPhysicsConfig();
|
||
this.physicsSystem.updatePhysicsConfig({
|
||
gravity: config.gravity ?? physicsConfig.gravity,
|
||
groundFriction: config.friction ?? physicsConfig.groundFriction
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* 切换 SharedArrayBuffer 状态
|
||
*/
|
||
toggleSharedArrayBuffer() {
|
||
this.physicsSystem.disableSharedArrayBuffer();
|
||
}
|
||
/**
|
||
* 获取物理系统状态
|
||
*/
|
||
getPhysicsSystemStatus() {
|
||
return this.physicsSystem.getCurrentStatus();
|
||
}
|
||
/**
|
||
* 获取系统信息
|
||
*/
|
||
getSystemInfo() {
|
||
return {
|
||
physics: this.physicsSystem.getWorkerInfo(),
|
||
entityCount: this.entities.count,
|
||
physicsConfig: this.physicsSystem.getPhysicsConfig()
|
||
};
|
||
}
|
||
}
|
||
class WorkerDemo {
|
||
constructor() {
|
||
this.isRunning = false;
|
||
this.lastTime = 0;
|
||
this.frameCount = 0;
|
||
this.fpsUpdateTime = 0;
|
||
this.currentFPS = 0;
|
||
this.lastWorkerStatusUpdate = 0;
|
||
this.elements = {};
|
||
this.gameLoop = () => {
|
||
if (!this.isRunning) return;
|
||
const currentTime = performance.now();
|
||
const deltaTime = (currentTime - this.lastTime) / 1e3;
|
||
this.lastTime = currentTime;
|
||
const frameStartTime = performance.now();
|
||
Core.update(deltaTime);
|
||
const frameEndTime = performance.now();
|
||
this.updatePerformanceStats({
|
||
fps: this.currentFPS,
|
||
frameTime: frameEndTime - frameStartTime,
|
||
physicsTime: window.physicsExecutionTime || 0,
|
||
renderTime: window.renderExecutionTime || 0,
|
||
memoryUsage: this.getMemoryUsage()
|
||
});
|
||
this.frameCount++;
|
||
if (currentTime - this.fpsUpdateTime >= 1e3) {
|
||
this.currentFPS = this.frameCount;
|
||
this.frameCount = 0;
|
||
this.fpsUpdateTime = currentTime;
|
||
}
|
||
this.updateUI();
|
||
requestAnimationFrame(this.gameLoop);
|
||
};
|
||
this.canvas = document.getElementById("gameCanvas");
|
||
if (!this.canvas) {
|
||
throw new Error("Canvas element not found");
|
||
}
|
||
this.initializeUIElements();
|
||
Core.create({
|
||
debug: true,
|
||
enableEntitySystems: true
|
||
});
|
||
this.gameScene = new GameScene(this.canvas);
|
||
Core.setScene(this.gameScene);
|
||
this.bindEvents();
|
||
this.start();
|
||
}
|
||
initializeUIElements() {
|
||
const elementIds = [
|
||
"entityCount",
|
||
"entityCountValue",
|
||
"toggleWorker",
|
||
"toggleSAB",
|
||
"gravity",
|
||
"gravityValue",
|
||
"friction",
|
||
"frictionValue",
|
||
"spawnParticles",
|
||
"clearEntities",
|
||
"resetDemo",
|
||
"fps",
|
||
"entityCountStat",
|
||
"workerStatus",
|
||
"workerLoad",
|
||
"physicsTime",
|
||
"renderTime",
|
||
"frameTime",
|
||
"memoryUsage",
|
||
"sabStatus"
|
||
];
|
||
for (const id of elementIds) {
|
||
const element = document.getElementById(id);
|
||
if (element) {
|
||
this.elements[id] = element;
|
||
} else {
|
||
console.warn(`Element with id '${id}' not found`);
|
||
}
|
||
}
|
||
}
|
||
bindEvents() {
|
||
if (this.elements.entityCount && this.elements.entityCountValue) {
|
||
const slider = this.elements.entityCount;
|
||
slider.addEventListener("input", () => {
|
||
this.elements.entityCountValue.textContent = slider.value;
|
||
});
|
||
slider.addEventListener("change", () => {
|
||
const count = parseInt(slider.value);
|
||
this.gameScene.spawnInitialEntities(count);
|
||
});
|
||
}
|
||
if (this.elements.toggleWorker) {
|
||
this.elements.toggleWorker.addEventListener("click", () => {
|
||
const workerEnabled = this.gameScene.toggleWorker();
|
||
this.elements.toggleWorker.textContent = workerEnabled ? "禁用 Worker" : "启用 Worker";
|
||
this.updateWorkerStatus();
|
||
});
|
||
}
|
||
if (this.elements.toggleSAB) {
|
||
this.elements.toggleSAB.addEventListener("click", () => {
|
||
this.gameScene.toggleSharedArrayBuffer();
|
||
this.updateWorkerStatus();
|
||
});
|
||
}
|
||
if (this.elements.gravity && this.elements.gravityValue) {
|
||
const slider = this.elements.gravity;
|
||
slider.addEventListener("input", () => {
|
||
this.elements.gravityValue.textContent = slider.value;
|
||
});
|
||
slider.addEventListener("change", () => {
|
||
const gravity = parseInt(slider.value);
|
||
this.gameScene.updateWorkerConfig({ gravity });
|
||
});
|
||
}
|
||
if (this.elements.friction && this.elements.frictionValue) {
|
||
const slider = this.elements.friction;
|
||
slider.addEventListener("input", () => {
|
||
const value = parseInt(slider.value);
|
||
this.elements.frictionValue.textContent = `${value}%`;
|
||
});
|
||
slider.addEventListener("change", () => {
|
||
const friction = parseInt(slider.value) / 100;
|
||
this.gameScene.updateWorkerConfig({ friction });
|
||
});
|
||
}
|
||
if (this.elements.spawnParticles) {
|
||
this.elements.spawnParticles.addEventListener("click", () => {
|
||
const centerX = this.canvas.width / 2;
|
||
const centerY = this.canvas.height / 2;
|
||
this.gameScene.spawnParticleExplosion(centerX, centerY, 100);
|
||
});
|
||
}
|
||
if (this.elements.clearEntities) {
|
||
this.elements.clearEntities.addEventListener("click", () => {
|
||
this.gameScene.clearAllEntities();
|
||
});
|
||
}
|
||
if (this.elements.resetDemo) {
|
||
this.elements.resetDemo.addEventListener("click", () => {
|
||
this.resetDemo();
|
||
});
|
||
}
|
||
this.canvas.addEventListener("click", (event) => {
|
||
const rect = this.canvas.getBoundingClientRect();
|
||
const x = event.clientX - rect.left;
|
||
const y = event.clientY - rect.top;
|
||
this.gameScene.spawnParticleExplosion(x, y, 30);
|
||
});
|
||
}
|
||
start() {
|
||
this.isRunning = true;
|
||
this.lastTime = performance.now();
|
||
this.gameLoop();
|
||
console.log("Worker演示已启动");
|
||
}
|
||
updatePerformanceStats(stats) {
|
||
if (this.elements.fps) {
|
||
this.elements.fps.textContent = stats.fps.toString();
|
||
this.elements.fps.className = stats.fps >= 55 ? "performance-high" : stats.fps >= 30 ? "performance-medium" : "performance-low";
|
||
}
|
||
if (this.elements.frameTime) {
|
||
this.elements.frameTime.textContent = stats.frameTime.toFixed(2);
|
||
this.elements.frameTime.className = stats.frameTime <= 16 ? "performance-high" : stats.frameTime <= 33 ? "performance-medium" : "performance-low";
|
||
}
|
||
if (this.elements.physicsTime) {
|
||
this.elements.physicsTime.textContent = stats.physicsTime.toFixed(2);
|
||
this.elements.physicsTime.className = stats.physicsTime <= 8 ? "performance-high" : stats.physicsTime <= 16 ? "performance-medium" : "performance-low";
|
||
}
|
||
if (this.elements.renderTime) {
|
||
this.elements.renderTime.textContent = stats.renderTime.toFixed(2);
|
||
this.elements.renderTime.className = stats.renderTime <= 8 ? "performance-high" : stats.renderTime <= 16 ? "performance-medium" : "performance-low";
|
||
}
|
||
if (this.elements.memoryUsage) {
|
||
this.elements.memoryUsage.textContent = stats.memoryUsage.toFixed(1);
|
||
}
|
||
}
|
||
updateUI() {
|
||
const currentTime = performance.now();
|
||
const systemInfo = this.gameScene.getSystemInfo();
|
||
if (this.elements.entityCountStat) {
|
||
this.elements.entityCountStat.textContent = systemInfo.entityCount.toString();
|
||
}
|
||
if (currentTime - this.lastWorkerStatusUpdate >= 500) {
|
||
this.updateWorkerStatus();
|
||
this.lastWorkerStatusUpdate = currentTime;
|
||
}
|
||
window.workerInfo = systemInfo.physics;
|
||
}
|
||
updateWorkerStatus() {
|
||
const systemInfo = this.gameScene.getSystemInfo();
|
||
const workerInfo = systemInfo.physics;
|
||
const entityCount = systemInfo.entityCount;
|
||
const status = this.gameScene.getPhysicsSystemStatus();
|
||
if (this.elements.workerStatus) {
|
||
if (workerInfo.enabled) {
|
||
this.elements.workerStatus.textContent = `启用 (${workerInfo.workerCount} Workers)`;
|
||
this.elements.workerStatus.className = "worker-enabled";
|
||
} else {
|
||
this.elements.workerStatus.textContent = "禁用";
|
||
this.elements.workerStatus.className = "worker-disabled";
|
||
}
|
||
}
|
||
if (this.elements.workerLoad) {
|
||
if (workerInfo.enabled && entityCount > 0) {
|
||
const entitiesPerWorker = Math.ceil(entityCount / workerInfo.workerCount);
|
||
this.elements.workerLoad.textContent = `${entitiesPerWorker}/Worker (共${workerInfo.workerCount}个)`;
|
||
} else {
|
||
this.elements.workerLoad.textContent = "N/A";
|
||
}
|
||
}
|
||
if (this.elements.sabStatus) {
|
||
const modeNames = {
|
||
"shared-buffer": "SharedArrayBuffer模式",
|
||
"single-worker": "单Worker模式",
|
||
"multi-worker": "多Worker模式",
|
||
"sync": "同步模式"
|
||
};
|
||
this.elements.sabStatus.textContent = modeNames[status.mode] || status.mode;
|
||
this.elements.sabStatus.className = status.mode === "shared-buffer" ? "worker-enabled" : "worker-disabled";
|
||
}
|
||
if (this.elements.toggleSAB) {
|
||
if (status.sharedArrayBufferEnabled) {
|
||
this.elements.toggleSAB.textContent = "禁用 SharedArrayBuffer";
|
||
} else {
|
||
this.elements.toggleSAB.textContent = "启用 SharedArrayBuffer";
|
||
this.elements.toggleSAB.setAttribute("disabled", "true");
|
||
}
|
||
}
|
||
}
|
||
getMemoryUsage() {
|
||
if ("memory" in performance) {
|
||
const memory = performance.memory;
|
||
return memory.usedJSHeapSize / (1024 * 1024);
|
||
}
|
||
return 0;
|
||
}
|
||
resetDemo() {
|
||
if (this.elements.entityCount) {
|
||
this.elements.entityCount.value = "1000";
|
||
this.elements.entityCountValue.textContent = "1000";
|
||
}
|
||
if (this.elements.gravity) {
|
||
this.elements.gravity.value = "100";
|
||
this.elements.gravityValue.textContent = "100";
|
||
}
|
||
if (this.elements.friction) {
|
||
this.elements.friction.value = "95";
|
||
this.elements.frictionValue.textContent = "95%";
|
||
}
|
||
const workerInfo = this.gameScene.getSystemInfo().physics;
|
||
if (!workerInfo.enabled) {
|
||
this.gameScene.toggleWorker();
|
||
}
|
||
if (this.elements.toggleWorker) {
|
||
this.elements.toggleWorker.textContent = "禁用 Worker";
|
||
}
|
||
this.gameScene.spawnInitialEntities(1e3);
|
||
this.gameScene.updateWorkerConfig({
|
||
gravity: 100,
|
||
friction: 0.95
|
||
});
|
||
console.log("演示已重置");
|
||
}
|
||
stop() {
|
||
this.isRunning = false;
|
||
}
|
||
}
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
try {
|
||
new WorkerDemo();
|
||
} catch (error) {
|
||
console.error("启动演示失败:", error);
|
||
document.body.innerHTML = `
|
||
<div style="padding: 20px; color: red;">
|
||
<h1>启动失败</h1>
|
||
<p>错误: ${error}</p>
|
||
<p>请确保浏览器支持Web Workers和Canvas API</p>
|
||
</div>
|
||
`;
|
||
}
|
||
});
|