From bb19f752a1698858ffe78963e39a36d7267a7683 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Wed, 2 Jul 2025 00:13:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=80=A7=E8=83=BD=E7=BB=93?= =?UTF-8?q?=E6=9E=84/=E5=BB=B6=E8=BF=9F=E5=8A=A0=E8=BD=BD=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/scripts/ecs/ECSManager.ts | 11 +- .../scripts/ecs/components/AIComponent.ts | 328 +++++++ .../ecs/components/AIComponent.ts.meta | 9 + .../ecs/components/NetworkComponent.ts | 413 ++++++++ .../ecs/components/NetworkComponent.ts.meta | 9 + .../scripts/ecs/components/NodeComponent.ts | 346 +++++++ .../ecs/components/NodeComponent.ts.meta | 9 + .../assets/scripts/ecs/components/index.ts | 5 +- .../assets/scripts/ecs/scenes/GameScene.ts | 311 ++++-- .../assets/scripts/ecs/systems/AISystem.ts | 317 ++++++ .../scripts/ecs/systems/AISystem.ts.meta | 9 + .../scripts/ecs/systems/NetworkSystem.ts | 448 +++++++++ .../scripts/ecs/systems/NetworkSystem.ts.meta | 9 + .../scripts/ecs/systems/NodeRenderSystem.ts | 475 +++++++++ .../ecs/systems/NodeRenderSystem.ts.meta | 9 + .../assets/scripts/ecs/systems/index.ts | 5 +- .../cocos-ecs/extensions/cocos-ecs-extension | 2 +- .../cocos-ecs/extensions/cocos-terrain-gen | 2 +- extensions/cocos/cocos-ecs/package-lock.json | 9 +- extensions/cocos/cocos-ecs/package.json | 6 +- src/Utils/Debug/DebugManager.ts | 274 +++--- src/Utils/Debug/EntityDataCollector.ts | 929 ++++++++++++------ thirdparty/BehaviourTree-ai | 2 +- 23 files changed, 3425 insertions(+), 512 deletions(-) create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/components/AIComponent.ts create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/components/AIComponent.ts.meta create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NetworkComponent.ts create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NetworkComponent.ts.meta create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NodeComponent.ts create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NodeComponent.ts.meta create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/AISystem.ts create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/AISystem.ts.meta create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NetworkSystem.ts create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NetworkSystem.ts.meta create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NodeRenderSystem.ts create mode 100644 extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NodeRenderSystem.ts.meta diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts index 1fefe266..50d0d91d 100644 --- a/extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts @@ -42,11 +42,12 @@ export class ECSManager extends Component { // 1. 创建Core实例,启用调试功能 if (this.debugMode) { Core.create({ + debug: true, + enableEntitySystems: true, debugConfig: { enabled: true, websocketUrl: 'ws://localhost:8080', autoReconnect: true, - updateInterval: 100, debugFrameRate: 30, channels: { entities: true, @@ -57,9 +58,13 @@ export class ECSManager extends Component { } } }); - // ECS调试模式已启用 + console.log('✅ ECS调试模式已启用'); } else { - Core.create(false); + Core.create({ + debug: false, + enableEntitySystems: true + }); + console.log('ℹ️ ECS调试模式已禁用'); } // 2. 创建游戏场景 diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/AIComponent.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/AIComponent.ts new file mode 100644 index 00000000..b670bb13 --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/AIComponent.ts @@ -0,0 +1,328 @@ +import { Component } from '@esengine/ecs-framework'; +import { Vec3 } from 'cc'; + +/** + * AI组件 - 复杂的AI行为和状态管理 + */ +export class AIComponent extends Component { + /** AI状态 */ + public currentState: 'idle' | 'patrol' | 'chase' | 'attack' | 'flee' | 'dead' = 'idle'; + + /** 目标ID(避免循环引用) */ + public targetId: number | null = null; + + /** AI伙伴ID列表(避免循环引用) */ + public allyIds: number[] = []; + + /** 敌人ID列表 */ + public enemyIds: number[] = []; + + /** 复杂的AI配置 */ + public config: { + personality: { + aggression: number; // 攻击性 0-1 + curiosity: number; // 好奇心 0-1 + loyalty: number; // 忠诚度 0-1 + intelligence: number; // 智力 0-1 + }; + capabilities: { + sightRange: number; + hearingRange: number; + movementSpeed: number; + attackDamage: number; + health: number; + }; + behaviorTree: { + rootNode: BehaviorNode; + blackboard: Map; + executionHistory: BehaviorExecution[]; + }; + memory: { + lastSeenEnemyPosition: Vec3 | null; + lastSeenEnemyTime: number; + knownLocations: Array<{ + position: Vec3; + type: 'safe' | 'danger' | 'resource' | 'patrol'; + confidence: number; + lastVisited: number; + }>; + relationships: Map; + }>; + }; + }; + + /** 状态机 */ + public stateMachine: { + states: Map; + transitions: Map boolean; + priority: number; + }>>; + stateHistory: Array<{ + state: string; + enterTime: number; + exitTime: number; + data: any; + }>; + }; + + /** 感知系统 */ + public perception: { + visibleEntities: Array<{ + entityId: number; + position: Vec3; + distance: number; + angle: number; + lastSeen: number; + componentId?: number; // 使用组件ID避免循环引用 + }>; + audibleSounds: Array<{ + source: Vec3; + volume: number; + type: string; + timestamp: number; + }>; + tacticleInfo: Array<{ + entityId: number; + contactPoint: Vec3; + force: number; + timestamp: number; + }>; + }; + + constructor() { + super(); + + // 初始化AI配置 + this.config = { + personality: { + aggression: Math.random(), + curiosity: Math.random(), + loyalty: Math.random(), + intelligence: Math.random() + }, + capabilities: { + sightRange: 100 + Math.random() * 100, + hearingRange: 50 + Math.random() * 50, + movementSpeed: 80 + Math.random() * 40, + attackDamage: 10 + Math.random() * 20, + health: 80 + Math.random() * 40 + }, + behaviorTree: { + rootNode: new BehaviorNode('root'), + blackboard: new Map(), + executionHistory: [] + }, + memory: { + lastSeenEnemyPosition: null, + lastSeenEnemyTime: 0, + knownLocations: [], + relationships: new Map() + } + }; + + // 初始化状态机 + this.stateMachine = { + states: new Map(), + transitions: new Map(), + stateHistory: [] + }; + + // 初始化感知系统 + this.perception = { + visibleEntities: [], + audibleSounds: [], + tacticleInfo: [] + }; + + this.initializeStateMachine(); + this.initializeBehaviorTree(); + } + + /** + * 初始化状态机 + */ + private initializeStateMachine(): void { + // 添加基本状态 + this.stateMachine.states.set('idle', new AIState('idle', this)); + this.stateMachine.states.set('patrol', new AIState('patrol', this)); + this.stateMachine.states.set('chase', new AIState('chase', this)); + this.stateMachine.states.set('attack', new AIState('attack', this)); + this.stateMachine.states.set('flee', new AIState('flee', this)); + + // 设置状态转换 + this.stateMachine.transitions.set('idle', [ + { targetState: 'patrol', condition: () => Math.random() > 0.8, priority: 1 }, + { targetState: 'chase', condition: () => this.perception.visibleEntities.length > 0, priority: 3 } + ]); + + this.stateMachine.transitions.set('patrol', [ + { targetState: 'idle', condition: () => Math.random() > 0.9, priority: 1 }, + { targetState: 'chase', condition: () => this.hasVisibleEnemies(), priority: 3 } + ]); + } + + /** + * 初始化行为树 + */ + private initializeBehaviorTree(): void { + const root = this.config.behaviorTree.rootNode; + + // 构建简单的行为树结构 + const selectorNode = new BehaviorNode('selector'); + const sequenceNode = new BehaviorNode('sequence'); + const conditionNode = new BehaviorNode('condition'); + const actionNode = new BehaviorNode('action'); + + root.addChild(selectorNode); + selectorNode.addChild(sequenceNode); + sequenceNode.addChild(conditionNode); + sequenceNode.addChild(actionNode); + + // 设置黑板数据 + this.config.behaviorTree.blackboard.set('lastPatrolPoint', new Vec3()); + this.config.behaviorTree.blackboard.set('alertLevel', 0); + this.config.behaviorTree.blackboard.set('energy', 100); + } + + /** + * 添加盟友(避免循环引用) + */ + public addAlly(allyEntityId: number): void { + if (!this.allyIds.includes(allyEntityId)) { + this.allyIds.push(allyEntityId); + + // 更新关系记录 + this.config.memory.relationships.set(allyEntityId, { + entityId: allyEntityId, + relationship: 'ally', + trustLevel: 0.8, + lastInteraction: Date.now(), + interactionHistory: [] + }); + } + } + + /** + * 设置目标(避免循环引用) + */ + public setTarget(targetEntityId: number): void { + this.targetId = targetEntityId; + // 不再需要双向引用 + } + + /** + * 更新感知信息 + */ + public updatePerception(deltaTime: number): void { + // 清理过期的感知信息 + const currentTime = Date.now(); + this.perception.visibleEntities = this.perception.visibleEntities.filter( + entity => currentTime - entity.lastSeen < 5000 + ); + + this.perception.audibleSounds = this.perception.audibleSounds.filter( + sound => currentTime - sound.timestamp < 2000 + ); + + // 更新记忆中的位置信息 + this.config.memory.knownLocations.forEach(location => { + location.confidence *= 0.999; // 随时间衰减可信度 + }); + } + + /** + * 检查是否有可见敌人 + */ + private hasVisibleEnemies(): boolean { + return this.perception.visibleEntities.some(entity => + this.config.memory.relationships.get(entity.entityId)?.relationship === 'enemy' + ); + } + + /** + * 重置组件 + */ + public reset(): void { + // 清理ID数组(不再需要处理循环引用) + this.allyIds = []; + this.enemyIds = []; + this.targetId = null; + this.currentState = 'idle'; + + this.config.behaviorTree.blackboard.clear(); + this.config.memory.relationships.clear(); + this.perception.visibleEntities = []; + this.perception.audibleSounds = []; + this.perception.tacticleInfo = []; + } +} + +/** + * 行为树节点 + */ +class BehaviorNode { + public name: string; + public children: BehaviorNode[] = []; + public parent: BehaviorNode | null = null; + public data: Map = new Map(); + + constructor(name: string) { + this.name = name; + } + + public addChild(child: BehaviorNode): void { + this.children.push(child); + child.parent = this; + } +} + +/** + * 行为执行记录 + */ +interface BehaviorExecution { + nodeName: string; + startTime: number; + endTime: number; + result: 'success' | 'failure' | 'running'; + data: any; +} + +/** + * AI状态 + */ +class AIState { + public name: string; + public owner: AIComponent; + public enterTime: number = 0; + public data: Map = new Map(); + + constructor(name: string, owner: AIComponent) { + this.name = name; + this.owner = owner; + } + + public enter(): void { + this.enterTime = Date.now(); + } + + public exit(): void { + // 记录状态历史 + this.owner.stateMachine.stateHistory.push({ + state: this.name, + enterTime: this.enterTime, + exitTime: Date.now(), + data: Object.fromEntries(this.data) + }); + } +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/AIComponent.ts.meta b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/AIComponent.ts.meta new file mode 100644 index 00000000..86f2d7b7 --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/AIComponent.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "cc0d3d0d-0c12-4007-8568-11b2cafdfb8f", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NetworkComponent.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NetworkComponent.ts new file mode 100644 index 00000000..afb9e2e3 --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NetworkComponent.ts @@ -0,0 +1,413 @@ +import { Component } from '@esengine/ecs-framework'; + +/** + * 网络组件 - 模拟复杂的网络连接和数据同步(已移除循环引用) + */ +export class NetworkComponent extends Component { + /** 网络ID */ + public networkId: string = ''; + + /** 连接状态 */ + public connectionState: 'disconnected' | 'connecting' | 'connected' | 'error' = 'disconnected'; + + /** 网络连接信息 */ + public connection: { + sessionId: string; + serverId: string; + roomId: string; + playerId: string; + ping: number; + packetLoss: number; + bandwidth: number; + lastHeartbeat: number; + }; + + /** 同步数据 */ + public syncData: { + dirtyFlags: Set; + lastSyncTime: number; + syncHistory: Array<{ + timestamp: number; + dataSize: number; + properties: string[]; + success: boolean; + }>; + queuedUpdates: Array<{ + property: string; + value: any; + timestamp: number; + priority: number; + }>; + }; + + /** 网络统计 */ + public networkStats: { + totalBytesSent: number; + totalBytesReceived: number; + packetsPerSecond: number; + averageLatency: number; + latencyHistory: number[]; + connectionQuality: 'excellent' | 'good' | 'fair' | 'poor'; + errorCount: number; + reconnectCount: number; + lastErrorTime: number; + errorLog: Array<{ + timestamp: number; + errorType: string; + message: string; + stack?: string; + }>; + }; + + /** 连接的玩家ID列表(避免循环引用) */ + public connectedPlayerIds: Set = new Set(); + + /** 群组成员ID(避免循环引用) */ + public groupMemberIds: string[] = []; + + /** 群组领导者ID(避免循环引用) */ + public groupLeaderId: string | null = null; + + /** 复杂的网络配置 */ + public config: { + autoReconnect: boolean; + maxReconnectAttempts: number; + heartbeatInterval: number; + syncFrequency: number; + compressionEnabled: boolean; + encryptionEnabled: boolean; + priorityLevels: Map; + filters: Array<{ + property: string; + condition: (value: any) => boolean; + action: 'allow' | 'deny' | 'transform'; + transformer?: (value: any) => any; + }>; + bufferSettings: { + maxBufferSize: number; + flushInterval: number; + compressionThreshold: number; + }; + }; + + /** 消息队列 */ + public messageQueue: { + incoming: Array<{ + senderId: string; + messageType: string; + data: any; + timestamp: number; + processed: boolean; + }>; + outgoing: Array<{ + targetId: string; + messageType: string; + data: any; + priority: number; + attempts: number; + maxAttempts: number; + }>; + processing: Map; + }; + + /** 复杂的网络缓存系统 */ + public cacheSystem: { + playerCache: Map; + messageCache: Map; + syncCache: Map; + }; + + constructor(networkId: string = '') { + super(); + + this.networkId = networkId || this.generateNetworkId(); + + this.connection = { + sessionId: '', + serverId: '', + roomId: '', + playerId: '', + ping: 0, + packetLoss: 0, + bandwidth: 0, + lastHeartbeat: 0 + }; + + this.syncData = { + dirtyFlags: new Set(), + lastSyncTime: 0, + syncHistory: [], + queuedUpdates: [] + }; + + this.networkStats = { + totalBytesSent: 0, + totalBytesReceived: 0, + packetsPerSecond: 0, + averageLatency: 0, + latencyHistory: [], + connectionQuality: 'excellent', + errorCount: 0, + reconnectCount: 0, + lastErrorTime: 0, + errorLog: [] + }; + + this.config = { + autoReconnect: true, + maxReconnectAttempts: 5, + heartbeatInterval: 1000, + syncFrequency: 60, + compressionEnabled: true, + encryptionEnabled: false, + priorityLevels: new Map([ + ['critical', 10], + ['high', 7], + ['medium', 5], + ['low', 2] + ]), + filters: [], + bufferSettings: { + maxBufferSize: 1024 * 1024, // 1MB + flushInterval: 100, + compressionThreshold: 1024 + } + }; + + this.messageQueue = { + incoming: [], + outgoing: [], + processing: new Map() + }; + + this.cacheSystem = { + playerCache: new Map(), + messageCache: new Map(), + syncCache: new Map() + }; + } + + /** + * 生成网络ID + */ + private generateNetworkId(): string { + return 'net_' + Math.random().toString(36).substring(2, 15) + + Math.random().toString(36).substring(2, 15); + } + + /** + * 连接到其他网络组件(避免循环引用) + */ + public connectToPlayer(playerNetworkId: string): void { + if (!this.connectedPlayerIds.has(playerNetworkId)) { + this.connectedPlayerIds.add(playerNetworkId); + + // 记录连接事件 + this.logNetworkEvent('player_connected', { + playerId: playerNetworkId, + timestamp: Date.now() + }); + } + } + + /** + * 加入群组(避免循环引用) + */ + public joinGroup(memberIds: string[], leaderId?: string): void { + this.groupMemberIds = [...memberIds]; + this.groupLeaderId = leaderId || null; + + // 更新缓存 + memberIds.forEach(memberId => { + this.cacheSystem.playerCache.set(memberId, { + playerId: memberId, + lastSeen: Date.now(), + cachedData: {}, + cacheExpiry: Date.now() + 300000 // 5分钟缓存 + }); + }); + } + + /** + * 发送消息 + */ + public sendMessage(targetId: string, messageType: string, data: any, priority: number = 5): void { + const message = { + targetId, + messageType, + data: this.processOutgoingData(data), + priority, + attempts: 0, + maxAttempts: 3 + }; + + this.messageQueue.outgoing.push(message); + this.messageQueue.outgoing.sort((a, b) => b.priority - a.priority); + + // 更新统计 + this.networkStats.totalBytesSent += JSON.stringify(data).length; + } + + /** + * 处理传出数据 + */ + private processOutgoingData(data: any): any { + let processedData = data; + + // 应用过滤器 + this.config.filters.forEach(filter => { + if (filter.condition(processedData)) { + if (filter.action === 'transform' && filter.transformer) { + processedData = filter.transformer(processedData); + } + } + }); + + // 压缩数据 + if (this.config.compressionEnabled) { + processedData = this.compressData(processedData); + } + + return processedData; + } + + /** + * 压缩数据(模拟) + */ + private compressData(data: any): any { + // 模拟压缩算法 + const serialized = JSON.stringify(data); + if (serialized.length > this.config.bufferSettings.compressionThreshold) { + // 模拟压缩 + return { + compressed: true, + originalSize: serialized.length, + compressedSize: Math.floor(serialized.length * 0.6), + data: serialized.substring(0, Math.floor(serialized.length * 0.6)) + }; + } + return data; + } + + /** + * 标记属性为脏 + */ + public markDirty(property: string): void { + this.syncData.dirtyFlags.add(property); + } + + /** + * 更新网络统计 + */ + public updateNetworkStats(deltaTime: number): void { + // 更新延迟历史 + if (this.networkStats.latencyHistory.length > 100) { + this.networkStats.latencyHistory.shift(); + } + this.networkStats.latencyHistory.push(this.connection.ping); + + // 计算平均延迟 + this.networkStats.averageLatency = this.networkStats.latencyHistory.reduce((a, b) => a + b, 0) / + this.networkStats.latencyHistory.length; + + // 更新连接质量 + if (this.networkStats.averageLatency < 50) { + this.networkStats.connectionQuality = 'excellent'; + } else if (this.networkStats.averageLatency < 100) { + this.networkStats.connectionQuality = 'good'; + } else if (this.networkStats.averageLatency < 200) { + this.networkStats.connectionQuality = 'fair'; + } else { + this.networkStats.connectionQuality = 'poor'; + } + + // 更新包率 + this.networkStats.packetsPerSecond = this.messageQueue.outgoing.length / deltaTime; + + // 清理过期缓存 + this.cleanupExpiredCache(); + } + + /** + * 清理过期缓存 + */ + private cleanupExpiredCache(): void { + const now = Date.now(); + + // 清理玩家缓存 + for (const [key, value] of this.cacheSystem.playerCache) { + if (value.cacheExpiry < now) { + this.cacheSystem.playerCache.delete(key); + } + } + + // 清理消息缓存 + for (const [key, value] of this.cacheSystem.messageCache) { + if (value.timestamp < now - 600000) { // 10分钟过期 + this.cacheSystem.messageCache.delete(key); + } + } + } + + /** + * 记录网络事件 + */ + private logNetworkEvent(eventType: string, data: any): void { + this.networkStats.errorLog.push({ + timestamp: Date.now(), + errorType: eventType, + message: JSON.stringify(data) + }); + + // 限制日志大小 + if (this.networkStats.errorLog.length > 1000) { + this.networkStats.errorLog = this.networkStats.errorLog.slice(-500); + } + } + + /** + * 重置组件 + */ + public reset(): void { + // 清理ID列表(不再需要处理循环引用) + this.connectedPlayerIds.clear(); + this.groupMemberIds = []; + this.groupLeaderId = null; + this.connectionState = 'disconnected'; + + this.syncData.dirtyFlags.clear(); + this.syncData.syncHistory = []; + this.syncData.queuedUpdates = []; + + this.messageQueue.incoming = []; + this.messageQueue.outgoing = []; + this.messageQueue.processing.clear(); + + this.cacheSystem.playerCache.clear(); + this.cacheSystem.messageCache.clear(); + this.cacheSystem.syncCache.clear(); + + this.networkStats.errorLog = []; + this.networkStats.latencyHistory = []; + } +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NetworkComponent.ts.meta b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NetworkComponent.ts.meta new file mode 100644 index 00000000..30ab9b64 --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NetworkComponent.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "d9263549-7b26-4b4f-9a15-b82e7af5fbd5", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NodeComponent.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NodeComponent.ts new file mode 100644 index 00000000..0ec49c31 --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NodeComponent.ts @@ -0,0 +1,346 @@ +import { Component } from '@esengine/ecs-framework'; +import { Node, Vec3, Color, Sprite, Label } from 'cc'; + +/** + * Node组件 - 包含Cocos Creator节点引用(已移除循环引用) + */ +export class NodeComponent extends Component { + /** Cocos Creator节点引用 */ + public node: Node | null = null; + + /** 子节点列表 */ + public children: Node[] = []; + + /** 节点配置信息 */ + public nodeConfig: { + name: string; + layer: number; + tag: string; + userData: Record; + transformData: { + position: Vec3; + rotation: Vec3; + scale: Vec3; + }; + renderData: { + color: Color; + opacity: number; + visible: boolean; + }; + parentId: number | null; // 避免循环引用:使用父节点实体ID + childIds: number[]; // 避免循环引用:使用子节点实体ID列表 + }; + + /** 渲染组件引用 */ + public sprite: Sprite | null = null; + public label: Label | null = null; + + /** 复杂嵌套对象 */ + public complexData: { + statistics: { + frameCount: number; + lastUpdateTime: number; + performance: { + avgRenderTime: number; + maxRenderTime: number; + renderHistory: number[]; + }; + }; + cache: { + textureCache: Map; + materialCache: Map; + shaderCache: Map; + }; + hierarchy: { + parentId: number | null; // 避免循环引用:使用ID + rootId: number | null; // 避免循环引用:使用ID + depth: number; + siblingIndex: number; + }; + animation: { + isPlaying: boolean; + currentFrame: number; + totalFrames: number; + loopCount: number; + animationQueue: Array<{ + name: string; + duration: number; + delay: number; + easing: string; + }>; + }; + interaction: { + isInteractable: boolean; + touchEnabled: boolean; + hitTestResults: Array<{ + position: Vec3; + timestamp: number; + result: boolean; + }>; + boundingBox: { + min: Vec3; + max: Vec3; + center: Vec3; + }; + }; + }; + + /** 复杂的渲染状态 */ + public renderState: { + layerInfo: { + currentLayer: number; + layerStack: number[]; + sortingOrder: number; + cullingMask: number; + }; + materials: Array<{ + materialId: string; + properties: Map; + textures: Map; + shaderParams: Record; + }>; + lightingData: { + ambientColor: Color; + diffuseColor: Color; + specularColor: Color; + lightDirection: Vec3; + shadowData: { + castShadows: boolean; + receiveShadows: boolean; + shadowQuality: 'low' | 'medium' | 'high'; + shadowDistance: number; + }; + }; + }; + + constructor(name: string = "DefaultNode") { + super(); + + this.nodeConfig = { + name: name, + layer: 0, + tag: "default", + userData: {}, + transformData: { + position: new Vec3(), + rotation: new Vec3(), + scale: new Vec3(1, 1, 1) + }, + renderData: { + color: new Color(255, 255, 255, 255), + opacity: 1.0, + visible: true + }, + parentId: null, + childIds: [] + }; + + this.complexData = { + statistics: { + frameCount: 0, + lastUpdateTime: 0, + performance: { + avgRenderTime: 0, + maxRenderTime: 0, + renderHistory: [] + } + }, + cache: { + textureCache: new Map(), + materialCache: new Map(), + shaderCache: new Map() + }, + hierarchy: { + parentId: null, + rootId: null, + depth: 0, + siblingIndex: 0 + }, + animation: { + isPlaying: false, + currentFrame: 0, + totalFrames: 60, + loopCount: 0, + animationQueue: [] + }, + interaction: { + isInteractable: true, + touchEnabled: true, + hitTestResults: [], + boundingBox: { + min: new Vec3(-1, -1, -1), + max: new Vec3(1, 1, 1), + center: new Vec3() + } + } + }; + + this.renderState = { + layerInfo: { + currentLayer: 0, + layerStack: [0], + sortingOrder: 0, + cullingMask: 0xFFFFFFFF + }, + materials: [], + lightingData: { + ambientColor: new Color(128, 128, 128, 255), + diffuseColor: new Color(255, 255, 255, 255), + specularColor: new Color(255, 255, 255, 255), + lightDirection: new Vec3(0, -1, 0), + shadowData: { + castShadows: true, + receiveShadows: true, + shadowQuality: 'medium', + shadowDistance: 100 + } + } + }; + } + + /** + * 设置父节点组件(避免循环引用) + */ + public setParent(parentEntityId: number): void { + this.nodeConfig.parentId = parentEntityId; + this.complexData.hierarchy.parentId = parentEntityId; + // 深度需要通过其他方式计算,避免引用 + } + + /** + * 添加子节点 + */ + public addChild(childEntityId: number): void { + if (!this.nodeConfig.childIds.includes(childEntityId)) { + this.nodeConfig.childIds.push(childEntityId); + } + } + + /** + * 更新性能统计 + */ + public updatePerformance(renderTime: number): void { + this.complexData.statistics.frameCount++; + this.complexData.statistics.lastUpdateTime = Date.now(); + + const perf = this.complexData.statistics.performance; + perf.renderHistory.push(renderTime); + + // 保持历史记录在合理范围内 + if (perf.renderHistory.length > 100) { + perf.renderHistory.shift(); + } + + // 计算平均值和最大值 + perf.avgRenderTime = perf.renderHistory.reduce((a, b) => a + b, 0) / perf.renderHistory.length; + perf.maxRenderTime = Math.max(perf.maxRenderTime, renderTime); + } + + /** + * 更新动画状态 + */ + public updateAnimation(deltaTime: number): void { + if (this.complexData.animation.isPlaying) { + this.complexData.animation.currentFrame++; + + if (this.complexData.animation.currentFrame >= this.complexData.animation.totalFrames) { + this.complexData.animation.currentFrame = 0; + this.complexData.animation.loopCount++; + + // 处理动画队列 + if (this.complexData.animation.animationQueue.length > 0) { + const nextAnim = this.complexData.animation.animationQueue.shift(); + if (nextAnim) { + this.complexData.animation.totalFrames = Math.floor(nextAnim.duration * 60); // 假设60FPS + } + } + } + } + } + + /** + * 添加材质 + */ + public addMaterial(materialId: string, properties: Record): void { + this.renderState.materials.push({ + materialId, + properties: new Map(Object.entries(properties)), + textures: new Map(), + shaderParams: {} + }); + } + + /** + * 更新包围盒 + */ + public updateBoundingBox(): void { + if (this.node) { + const worldPos = this.node.getWorldPosition(); + const scale = this.node.getScale(); + + this.complexData.interaction.boundingBox.center = new Vec3(worldPos.x, worldPos.y, worldPos.z); + this.complexData.interaction.boundingBox.min = new Vec3( + worldPos.x - scale.x * 0.5, + worldPos.y - scale.y * 0.5, + worldPos.z - scale.z * 0.5 + ); + this.complexData.interaction.boundingBox.max = new Vec3( + worldPos.x + scale.x * 0.5, + worldPos.y + scale.y * 0.5, + worldPos.z + scale.z * 0.5 + ); + } + } + + /** + * 执行点击测试 + */ + public hitTest(point: Vec3): boolean { + const bbox = this.complexData.interaction.boundingBox; + const result = point.x >= bbox.min.x && point.x <= bbox.max.x && + point.y >= bbox.min.y && point.y <= bbox.max.y && + point.z >= bbox.min.z && point.z <= bbox.max.z; + + // 记录测试结果 + this.complexData.interaction.hitTestResults.push({ + position: new Vec3(point.x, point.y, point.z), + timestamp: Date.now(), + result + }); + + // 限制历史记录大小 + if (this.complexData.interaction.hitTestResults.length > 50) { + this.complexData.interaction.hitTestResults.shift(); + } + + return result; + } + + /** + * 重置组件 + */ + public reset(): void { + this.node = null; + this.children = []; + this.sprite = null; + this.label = null; + + // 清理ID列表(不再需要处理循环引用) + this.nodeConfig.parentId = null; + this.nodeConfig.childIds = []; + this.complexData.hierarchy.parentId = null; + this.complexData.hierarchy.rootId = null; + this.complexData.hierarchy.depth = 0; + + this.complexData.cache.textureCache.clear(); + this.complexData.cache.materialCache.clear(); + this.complexData.cache.shaderCache.clear(); + + this.complexData.animation.isPlaying = false; + this.complexData.animation.currentFrame = 0; + this.complexData.animation.animationQueue = []; + + this.complexData.interaction.hitTestResults = []; + this.renderState.materials = []; + } +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NodeComponent.ts.meta b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NodeComponent.ts.meta new file mode 100644 index 00000000..ecd695dc --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/NodeComponent.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "28e7e8cd-591e-4fde-bb14-d668724a6201", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/index.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/index.ts index 701b64df..f53f9631 100644 --- a/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/index.ts +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/components/index.ts @@ -2,4 +2,7 @@ export { Transform } from './Transform'; export { Health } from './Health'; export { Velocity } from './Velocity'; -export { Renderer } from './Renderer'; \ No newline at end of file +export { Renderer } from './Renderer'; +export { NodeComponent } from './NodeComponent'; +export { AIComponent } from './AIComponent'; +export { NetworkComponent } from './NetworkComponent'; \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/scenes/GameScene.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/scenes/GameScene.ts index fa4f6c19..9e5079ea 100644 --- a/extensions/cocos/cocos-ecs/assets/scripts/ecs/scenes/GameScene.ts +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/scenes/GameScene.ts @@ -1,7 +1,7 @@ import { Scene } from '@esengine/ecs-framework'; -import { Color } from 'cc'; -import { MovementSystem, HealthSystem, RandomMovementSystem } from '../systems'; -import { Transform, Health, Velocity, Renderer } from '../components'; +import { Color, Node } from 'cc'; +import { MovementSystem, HealthSystem, RandomMovementSystem, AISystem, NetworkSystem, NodeRenderSystem } from '../systems'; +import { Transform, Health, Velocity, Renderer, NodeComponent, AIComponent, NetworkComponent } from '../components'; /** * 游戏场景 @@ -29,77 +29,268 @@ export class GameScene extends Scene { this.addEntityProcessor(new MovementSystem()); this.addEntityProcessor(new HealthSystem()); this.addEntityProcessor(new RandomMovementSystem()); + // this.addEntityProcessor(new AISystem()); + // this.addEntityProcessor(new NetworkSystem()); + // this.addEntityProcessor(new NodeRenderSystem()); - // 创建测试实体 - this.createTestEntities(); + // 创建大量复杂的测试实体 + this.createComplexTestEntities(); } /** - * 创建测试实体 + * 创建复杂的测试实体(1000+个) */ - private createTestEntities(): void { - console.log('🚀 开始创建测试实体...'); + private createComplexTestEntities(): void { + console.log('🚀 开始创建大量复杂测试实体...'); - // 创建玩家实体 - const player = this.createEntity("Player"); - player.addComponent(new Transform()); - player.addComponent(new Health(150)); - player.addComponent(new Velocity()); - player.addComponent(new Renderer("player", new Color(0, 255, 0, 255))); + // 存储创建的AI和网络组件用于建立循环引用 + const aiComponents: AIComponent[] = []; + const networkComponents: NetworkComponent[] = []; + const nodeComponents: NodeComponent[] = []; - const playerTransform = player.getComponent(Transform); - const playerHealth = player.getComponent(Health); - if (playerTransform) { - playerTransform.setPosition(0, 0); - playerTransform.speed = 120; - } - if (playerHealth) { - playerHealth.regenRate = 5; // 每秒回复5点生命值 - } - - // 创建一些敌人实体 - for (let i = 0; i < 10; i++) { - const enemy = this.createEntity(`Enemy_${i}`); - enemy.addComponent(new Transform()); - enemy.addComponent(new Health(80)); - enemy.addComponent(new Velocity()); - enemy.addComponent(new Renderer("enemy", new Color(255, 0, 0, 255))); + // 1. 创建玩家实体(具有所有组件类型) + console.log('创建玩家实体...'); + const player = this.createComplexEntity("Player", "player", new Color(0, 255, 0, 255), 0, 0, true, true, true); + if (player) { + const playerAI = player.getComponent(AIComponent); + const playerNetwork = player.getComponent(NetworkComponent); + const playerNode = player.getComponent(NodeComponent); - const enemyTransform = enemy.getComponent(Transform); - const enemyVelocity = enemy.getComponent(Velocity); - if (enemyTransform) { - // 随机位置 - const x = (Math.random() - 0.5) * 800; - const y = (Math.random() - 0.5) * 600; - enemyTransform.setPosition(x, y); - enemyTransform.speed = 80; - } - if (enemyVelocity) { - enemyVelocity.maxSpeed = 120; - enemyVelocity.friction = 0.95; + if (playerAI) aiComponents.push(playerAI); + if (playerNetwork) networkComponents.push(playerNetwork); + if (playerNode) nodeComponents.push(playerNode); + } + + // 2. 创建AI智能体(500个) + console.log('创建AI智能体...'); + for (let i = 0; i < 500; i++) { + const entityName = `AI_Agent_${i}`; + const x = (Math.random() - 0.5) * 2000; + const y = (Math.random() - 0.5) * 2000; + const color = new Color( + Math.floor(Math.random() * 255), + Math.floor(Math.random() * 255), + Math.floor(Math.random() * 255), + 255 + ); + + const entity = this.createComplexEntity(entityName, "ai_agent", color, x, y, true, true, Math.random() > 0.5); + + if (entity) { + const ai = entity.getComponent(AIComponent); + const network = entity.getComponent(NetworkComponent); + const node = entity.getComponent(NodeComponent); + + if (ai) { + aiComponents.push(ai); + // 设置随机AI个性 + ai.config.personality.aggression = Math.random(); + ai.config.personality.curiosity = Math.random(); + ai.config.personality.loyalty = Math.random(); + ai.config.personality.intelligence = Math.random(); + } + + if (network) { + networkComponents.push(network); + // 随机设置网络状态 + if (Math.random() > 0.2) { + network.connectionState = 'connected'; + } + } + + if (node) { + nodeComponents.push(node); + // 设置随机节点属性 + node.nodeConfig.layer = Math.floor(Math.random() * 10); + node.nodeConfig.tag = `layer_${node.nodeConfig.layer}`; + } } } - // 创建一些中性实体(只移动,无生命值) - for (let i = 0; i < 5; i++) { - const neutral = this.createEntity(`Neutral_${i}`); - neutral.addComponent(new Transform()); - neutral.addComponent(new Velocity()); - neutral.addComponent(new Renderer("neutral", new Color(255, 255, 0, 255))); + // 3. 创建网络节点(300个) + console.log('创建网络节点...'); + for (let i = 0; i < 300; i++) { + const entityName = `Network_Node_${i}`; + const x = (Math.random() - 0.5) * 1500; + const y = (Math.random() - 0.5) * 1500; + const color = new Color(0, 150, 255, 200); - const neutralTransform = neutral.getComponent(Transform); - const neutralVelocity = neutral.getComponent(Velocity); - if (neutralTransform) { - const x = (Math.random() - 0.5) * 600; - const y = (Math.random() - 0.5) * 400; - neutralTransform.setPosition(x, y); - neutralTransform.speed = 60; - } - if (neutralVelocity) { - neutralVelocity.maxSpeed = 80; - neutralVelocity.friction = 0.99; + const entity = this.createComplexEntity(entityName, "network_node", color, x, y, false, true, true); + + if (entity) { + const network = entity.getComponent(NetworkComponent); + const node = entity.getComponent(NodeComponent); + + if (network) { + networkComponents.push(network); + network.connectionState = 'connected'; + network.config.syncFrequency = 30 + Math.random() * 30; // 30-60Hz + } + + if (node) { + nodeComponents.push(node); + // 创建复杂的层次结构 + node.nodeConfig.layer = Math.floor(i / 10); // 每10个一层 + } } } + + // 4. 创建简单移动实体(200个) + console.log('创建简单移动实体...'); + for (let i = 0; i < 200; i++) { + const entityName = `Simple_Mover_${i}`; + const x = (Math.random() - 0.5) * 1000; + const y = (Math.random() - 0.5) * 1000; + const color = new Color(255, 255, 255, 150); + + this.createComplexEntity(entityName, "simple_mover", color, x, y, false, false, false); + } + + // 5. 建立循环引用和复杂关系 + console.log('建立实体间的复杂关系...'); + this.establishComplexRelationships(aiComponents, networkComponents, nodeComponents); + + const totalEntities = this.entities.count; + console.log(`✅ 创建完成!总共创建了 ${totalEntities} 个实体`); + console.log(` - AI组件: ${aiComponents.length} 个`); + console.log(` - 网络组件: ${networkComponents.length} 个`); + console.log(` - 节点组件: ${nodeComponents.length} 个`); + } + + /** + * 创建复杂实体的辅助方法 + */ + private createComplexEntity( + name: string, + type: string, + color: Color, + x: number, + y: number, + hasAI: boolean, + hasNetwork: boolean, + hasNode: boolean + ): any { + const entity = this.createEntity(name); + + // 基础组件 + const transform = new Transform(); + transform.setPosition(x, y); + transform.speed = 50 + Math.random() * 100; + entity.addComponent(transform); + + const health = new Health(50 + Math.random() * 100); + health.regenRate = Math.random() * 10; + entity.addComponent(health); + + const velocity = new Velocity(); + velocity.maxSpeed = 80 + Math.random() * 120; + velocity.friction = 0.95 + Math.random() * 0.04; + entity.addComponent(velocity); + + const renderer = new Renderer(type, color.clone()); + renderer.alpha = 0.5 + Math.random() * 0.5; + renderer.layer = Math.floor(Math.random() * 5); + entity.addComponent(renderer); + + // 复杂组件 + if (hasAI) { + const ai = new AIComponent(); + entity.addComponent(ai); + } + + if (hasNetwork) { + const network = new NetworkComponent(`${type}_${name}`); + entity.addComponent(network); + } + + if (hasNode) { + const node = new NodeComponent(name); + entity.addComponent(node); + } + + return entity; + } + + /** + * 建立实体间的复杂关系 + */ + private establishComplexRelationships( + aiComponents: AIComponent[], + networkComponents: NetworkComponent[], + nodeComponents: NodeComponent[] + ): void { + // 建立AI之间的盟友/敌人关系(避免循环引用) + for (let i = 0; i < Math.min(aiComponents.length, 100); i++) { + const ai = aiComponents[i]; + + // 随机添加盟友(使用实体ID) + const allyCount = Math.floor(Math.random() * 5); + for (let j = 0; j < allyCount; j++) { + const randomIndex = Math.floor(Math.random() * aiComponents.length); + const ally = aiComponents[randomIndex]; + if (ally !== ai && !ai.allyIds.includes(ally.entity.id)) { + ai.addAlly(ally.entity.id); + } + } + + // 随机设置目标(使用实体ID) + if (Math.random() > 0.7) { + const randomIndex = Math.floor(Math.random() * aiComponents.length); + const target = aiComponents[randomIndex]; + if (target !== ai) { + ai.setTarget(target.entity.id); + } + } + } + + // 建立网络连接(避免循环引用) + for (let i = 0; i < Math.min(networkComponents.length, 50); i++) { + const network = networkComponents[i]; + + // 连接到其他网络组件(使用网络ID) + const connectionCount = Math.floor(Math.random() * 8); + for (let j = 0; j < connectionCount; j++) { + const randomIndex = Math.floor(Math.random() * networkComponents.length); + const other = networkComponents[randomIndex]; + if (other !== network) { + network.connectToPlayer(other.networkId); + } + } + + // 创建群组(使用网络ID) + if (Math.random() > 0.8) { + const groupSize = Math.floor(Math.random() * 10) + 2; + const groupMemberIds: string[] = [network.networkId]; + + for (let k = 0; k < groupSize - 1; k++) { + const randomIndex = Math.floor(Math.random() * networkComponents.length); + const member = networkComponents[randomIndex]; + if (!groupMemberIds.includes(member.networkId)) { + groupMemberIds.push(member.networkId); + } + } + + network.joinGroup(groupMemberIds, network.networkId); + } + } + + // 建立节点层次结构(避免循环引用) + for (let i = 0; i < Math.min(nodeComponents.length, 30); i++) { + const parent = nodeComponents[i]; + + // 添加一些子节点(使用实体ID) + const childCount = Math.floor(Math.random() * 5); + for (let j = 0; j < childCount; j++) { + const childIndex = Math.floor(Math.random() * nodeComponents.length); + const child = nodeComponents[childIndex]; + + if (child !== parent && !parent.nodeConfig.childIds.includes(child.entity.id)) { + parent.addChild(child.entity.id); + } + } + } + + console.log('🔗 复杂关系建立完成!'); } /** diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/AISystem.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/AISystem.ts new file mode 100644 index 00000000..885b0fd0 --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/AISystem.ts @@ -0,0 +1,317 @@ +import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework'; +import { AIComponent, Transform, Health } from '../components'; + +/** + * AI系统 - 处理AI行为和状态机 + */ +export class AISystem extends EntitySystem { + + /** 系统处理的实体计数器 */ + private processedEntityCount: number = 0; + + /** 状态转换计数器 */ + private stateTransitionCount: number = 0; + + constructor() { + // 处理具有AI组件的实体 + super(Matcher.empty().all(AIComponent)); + } + + /** + * 处理所有实体 + */ + protected process(entities: Entity[]): void { + const deltaTime = Time.deltaTime; + const currentTime = Time.totalTime; + + this.processedEntityCount = entities.length; + + for (const entity of entities) { + this.processEntity(entity, deltaTime, currentTime); + } + + // 批量处理AI间的交互 + this.processAIInteractions(entities); + } + + /** + * 处理单个实体 + */ + private processEntity(entity: Entity, deltaTime: number, currentTime: number): void { + const ai = entity.getComponent(AIComponent); + const transform = entity.getComponent(Transform); + const health = entity.getComponent(Health); + + if (!ai) return; + + // 更新感知系统 + ai.updatePerception(deltaTime); + + // 处理状态机 + this.updateStateMachine(ai, deltaTime); + + // 更新行为树 + this.updateBehaviorTree(ai, deltaTime); + + // 处理AI能力(如果有Transform和Health组件) + if (transform && health) { + this.updateAICapabilities(ai, transform, health, deltaTime); + } + + // 处理记忆衰减 + this.updateMemory(ai, deltaTime); + } + + /** + * 更新状态机 + */ + private updateStateMachine(ai: AIComponent, deltaTime: number): void { + const currentStateName = ai.currentState; + const transitions = ai.stateMachine.transitions.get(currentStateName); + + if (transitions) { + // 按优先级排序转换条件 + const sortedTransitions = transitions.sort((a, b) => b.priority - a.priority); + + for (const transition of sortedTransitions) { + if (transition.condition()) { + // 执行状态转换 + const currentState = ai.stateMachine.states.get(currentStateName); + const newState = ai.stateMachine.states.get(transition.targetState); + + if (currentState && newState) { + currentState.exit(); + ai.currentState = transition.targetState as any; + newState.enter(); + this.stateTransitionCount++; + break; + } + } + } + } + } + + /** + * 更新行为树 + */ + private updateBehaviorTree(ai: AIComponent, deltaTime: number): void { + const behaviorTree = ai.config.behaviorTree; + const blackboard = behaviorTree.blackboard; + + // 更新黑板数据 + blackboard.set('deltaTime', deltaTime); + blackboard.set('currentTime', Date.now()); + + // 模拟行为树执行 + const executionResult = this.executeBehaviorNode(behaviorTree.rootNode, ai); + + // 记录执行历史 + behaviorTree.executionHistory.push({ + nodeName: behaviorTree.rootNode.name, + startTime: Date.now(), + endTime: Date.now() + Math.random() * 10, + result: executionResult, + data: { deltaTime } + }); + + // 保持历史记录在合理范围内 + if (behaviorTree.executionHistory.length > 50) { + behaviorTree.executionHistory.shift(); + } + } + + /** + * 执行行为树节点(模拟) + */ + private executeBehaviorNode(node: any, ai: AIComponent): 'success' | 'failure' | 'running' { + // 简单的行为树执行模拟 + switch (node.name) { + case 'root': + case 'selector': + // 选择器节点:尝试执行子节点直到一个成功 + for (const child of node.children) { + const result = this.executeBehaviorNode(child, ai); + if (result === 'success' || result === 'running') { + return result; + } + } + return 'failure'; + + case 'sequence': + // 序列节点:按顺序执行所有子节点 + for (const child of node.children) { + const result = this.executeBehaviorNode(child, ai); + if (result === 'failure' || result === 'running') { + return result; + } + } + return 'success'; + + case 'condition': + // 条件节点:检查AI状态 + return ai.config.personality.intelligence > 0.5 ? 'success' : 'failure'; + + case 'action': + // 动作节点:执行AI行为 + return Math.random() > 0.3 ? 'success' : 'running'; + + default: + return 'failure'; + } + } + + /** + * 更新AI能力 + */ + private updateAICapabilities(ai: AIComponent, transform: Transform, health: Health, deltaTime: number): void { + const capabilities = ai.config.capabilities; + + // 根据健康状况调整能力 + const healthRatio = health.currentHealth / health.maxHealth; + const effectiveSpeed = capabilities.movementSpeed * healthRatio; + + // 更新移动速度 + transform.speed = effectiveSpeed; + + // 根据个性调整行为 + if (ai.config.personality.aggression > 0.7 && healthRatio > 0.5) { + // 高攻击性且健康状况良好时更主动 + ai.currentState = 'chase'; + } else if (healthRatio < 0.3) { + // 生命值低时逃跑 + ai.currentState = 'flee'; + } + } + + /** + * 更新记忆系统 + */ + private updateMemory(ai: AIComponent, deltaTime: number): void { + const memory = ai.config.memory; + const currentTime = Date.now(); + + // 衰减已知位置的可信度 + memory.knownLocations.forEach(location => { + const timeSinceVisit = currentTime - location.lastVisited; + const decayFactor = Math.exp(-timeSinceVisit / 30000); // 30秒衰减率 + location.confidence *= decayFactor; + }); + + // 移除可信度过低的位置 + memory.knownLocations = memory.knownLocations.filter(location => location.confidence > 0.1); + + // 衰减关系信任度 + memory.relationships.forEach(relation => { + const timeSinceInteraction = currentTime - relation.lastInteraction; + if (timeSinceInteraction > 60000) { // 60秒没有交互 + relation.trustLevel *= 0.99; // 缓慢衰减 + } + }); + } + + /** + * 处理AI间的交互 + */ + private processAIInteractions(entities: Entity[]): void { + const aiEntities = entities.filter(e => e.getComponent(AIComponent)); + + for (let i = 0; i < aiEntities.length; i++) { + for (let j = i + 1; j < aiEntities.length; j++) { + this.processAIPair(aiEntities[i], aiEntities[j]); + } + } + } + + /** + * 处理两个AI实体间的交互 + */ + private processAIPair(entity1: Entity, entity2: Entity): void { + const ai1 = entity1.getComponent(AIComponent); + const ai2 = entity2.getComponent(AIComponent); + const transform1 = entity1.getComponent(Transform); + const transform2 = entity2.getComponent(Transform); + + if (!ai1 || !ai2 || !transform1 || !transform2) return; + + // 计算距离 + const distance = Math.sqrt( + Math.pow(transform1.position.x - transform2.position.x, 2) + + Math.pow(transform1.position.y - transform2.position.y, 2) + ); + + // 视线范围内的交互 + if (distance <= ai1.config.capabilities.sightRange) { + this.handleVisualContact(ai1, ai2, entity1, entity2, distance); + } + + // 听力范围内的交互 + if (distance <= ai1.config.capabilities.hearingRange) { + this.handleAudioContact(ai1, ai2, distance); + } + + // 创建盟友关系(随机) + if (Math.random() < 0.001 && !ai1.allyIds.includes(entity2.id)) { // 0.1%概率每帧 + ai1.addAlly(entity2.id); + } + } + + /** + * 处理视觉接触 + */ + private handleVisualContact(ai1: AIComponent, ai2: AIComponent, entity1: Entity, entity2: Entity, distance: number): void { + const currentTime = Date.now(); + + // 添加到可见实体列表 + const existingEntry = ai1.perception.visibleEntities.find(e => e.entityId === entity2.id); + if (existingEntry) { + existingEntry.distance = distance; + existingEntry.lastSeen = currentTime; + existingEntry.componentId = ai2.id; // 使用组件ID避免循环引用 + } else { + ai1.perception.visibleEntities.push({ + entityId: entity2.id, + position: entity2.getComponent(Transform)!.position.clone(), + distance: distance, + angle: Math.atan2( + entity2.getComponent(Transform)!.position.y - entity1.getComponent(Transform)!.position.y, + entity2.getComponent(Transform)!.position.x - entity1.getComponent(Transform)!.position.x + ), + lastSeen: currentTime, + componentId: ai2.id + }); + } + } + + /** + * 处理音频接触 + */ + private handleAudioContact(ai1: AIComponent, ai2: AIComponent, distance: number): void { + const soundVolume = 1.0 - (distance / ai1.config.capabilities.hearingRange); + + ai1.perception.audibleSounds.push({ + source: ai2.entity.getComponent(Transform)!.position.clone(), + volume: soundVolume, + type: 'movement', + timestamp: Date.now() + }); + } + + /** + * 系统初始化时调用 + */ + public initialize(): void { + super.initialize(); + console.log('🤖 AI系统已启动'); + } + + /** + * 获取系统统计信息 + */ + public getSystemStats(): any { + return { + processedEntities: this.processedEntityCount, + stateTransitions: this.stateTransitionCount, + systemName: 'AISystem' + }; + } +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/AISystem.ts.meta b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/AISystem.ts.meta new file mode 100644 index 00000000..d3d129b6 --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/AISystem.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "449fa887-eece-424d-ae1d-7082454fac3f", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NetworkSystem.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NetworkSystem.ts new file mode 100644 index 00000000..0974fb3a --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NetworkSystem.ts @@ -0,0 +1,448 @@ +import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework'; +import { NetworkComponent } from '../components'; + +/** + * 网络系统 - 处理网络同步和连接管理 + */ +export class NetworkSystem extends EntitySystem { + + /** 网络统计 */ + private networkStats = { + totalEntities: 0, + connectedEntities: 0, + totalMessagesSent: 0, + totalMessagesReceived: 0, + averagePing: 0, + networkTraffic: 0 + }; + + /** 消息处理队列 */ + private globalMessageQueue: Array<{ + from: string; + to: string; + messageType: string; + data: any; + timestamp: number; + priority: number; + }> = []; + + constructor() { + // 处理具有网络组件的实体 + super(Matcher.empty().all(NetworkComponent)); + } + + /** + * 处理所有实体 + */ + protected process(entities: Entity[]): void { + const deltaTime = Time.deltaTime; + + this.networkStats.totalEntities = entities.length; + this.networkStats.connectedEntities = entities.filter(e => + e.getComponent(NetworkComponent)?.connectionState === 'connected' + ).length; + + for (const entity of entities) { + this.processEntity(entity, deltaTime); + } + + // 处理全局消息队列 + this.processGlobalMessages(); + + // 更新网络统计 + this.updateGlobalNetworkStats(entities); + } + + /** + * 处理单个实体 + */ + private processEntity(entity: Entity, deltaTime: number): void { + const network = entity.getComponent(NetworkComponent); + + if (!network) return; + + // 更新网络统计 + network.updateNetworkStats(deltaTime); + + // 处理连接状态 + this.updateConnectionState(network, deltaTime); + + // 处理消息队列 + this.processEntityMessages(network, entity); + + // 处理数据同步 + this.processSynchronization(network, deltaTime); + + // 处理群组通信 + this.processGroupCommunication(network); + } + + /** + * 更新连接状态 + */ + private updateConnectionState(network: NetworkComponent, deltaTime: number): void { + const currentTime = Date.now(); + + switch (network.connectionState) { + case 'disconnected': + // 尝试连接 + if (network.config.autoReconnect && + network.networkStats.reconnectCount < network.config.maxReconnectAttempts) { + network.connectionState = 'connecting'; + network.connection.lastHeartbeat = currentTime; + } + break; + + case 'connecting': + // 模拟连接过程 + if (Math.random() > 0.1) { // 90% 成功率 + network.connectionState = 'connected'; + network.connection.sessionId = this.generateSessionId(); + network.connection.serverId = 'server_001'; + network.connection.lastHeartbeat = currentTime; + } else if (currentTime - network.connection.lastHeartbeat > 5000) { + // 连接超时 + network.connectionState = 'error'; + network.networkStats.errorCount++; + } + break; + + case 'connected': + // 维持连接心跳 + if (currentTime - network.connection.lastHeartbeat > network.config.heartbeatInterval) { + this.sendHeartbeat(network); + network.connection.lastHeartbeat = currentTime; + } + + // 模拟网络质量变化 + network.connection.ping = Math.random() * 100 + 20; // 20-120ms + network.connection.packetLoss = Math.random() * 0.05; // 0-5% + network.connection.bandwidth = 1000 + Math.random() * 500; // 1000-1500 Kbps + break; + + case 'error': + // 错误状态,尝试重连 + if (network.config.autoReconnect && + network.networkStats.reconnectCount < network.config.maxReconnectAttempts) { + network.connectionState = 'disconnected'; + network.networkStats.reconnectCount++; + } + break; + } + } + + /** + * 处理实体消息 + */ + private processEntityMessages(network: NetworkComponent, entity: Entity): void { + // 处理传出消息 + const outgoingMessages = network.messageQueue.outgoing.slice(); + network.messageQueue.outgoing = []; + + for (const message of outgoingMessages) { + if (this.sendMessage(network, message)) { + this.networkStats.totalMessagesSent++; + network.networkStats.totalBytesSent += this.estimateMessageSize(message); + } else { + // 发送失败,重新加入队列 + message.attempts++; + if (message.attempts < message.maxAttempts) { + network.messageQueue.outgoing.push(message); + } + } + } + + // 处理传入消息 + this.processIncomingMessages(network, entity); + } + + /** + * 发送消息 + */ + private sendMessage(network: NetworkComponent, message: any): boolean { + if (network.connectionState !== 'connected') { + return false; + } + + // 模拟网络延迟和丢包 + const shouldDelay = Math.random() < 0.3; // 30% 概率有延迟 + const shouldDrop = Math.random() < network.connection.packetLoss; + + if (shouldDrop) { + network.networkStats.errorCount++; + return false; + } + + // 添加到全局消息队列 + this.globalMessageQueue.push({ + from: network.networkId, + to: message.targetId, + messageType: message.messageType, + data: message.data, + timestamp: Date.now() + (shouldDelay ? Math.random() * 200 : 0), + priority: message.priority + }); + + return true; + } + + /** + * 处理传入消息 + */ + private processIncomingMessages(network: NetworkComponent, entity: Entity): void { + // 从全局队列中获取发给此实体的消息 + const incomingMessages = this.globalMessageQueue.filter(msg => + msg.to === network.networkId && msg.timestamp <= Date.now() + ); + + // 从全局队列中移除这些消息 + this.globalMessageQueue = this.globalMessageQueue.filter(msg => + !(msg.to === network.networkId && msg.timestamp <= Date.now()) + ); + + // 处理消息 + for (const message of incomingMessages) { + network.messageQueue.incoming.push({ + senderId: message.from, + messageType: message.messageType, + data: message.data, + timestamp: message.timestamp, + processed: false + }); + + this.networkStats.totalMessagesReceived++; + network.networkStats.totalBytesReceived += this.estimateMessageSize(message); + + // 立即处理某些类型的消息 + this.handleSpecialMessages(network, message); + } + } + + /** + * 处理特殊消息类型 + */ + private handleSpecialMessages(network: NetworkComponent, message: any): void { + switch (message.messageType) { + case 'player_join_group': + // 处理加入群组消息 + const groupData = message.data; + if (groupData.members && Array.isArray(groupData.members)) { + // 查找对应的网络组件并建立连接 + groupData.members.forEach((memberId: string) => { + // 直接使用成员ID建立连接 + network.connectToPlayer(memberId); + }); + } + break; + + case 'heartbeat': + // 心跳响应 + network.connection.ping = Date.now() - message.data.timestamp; + break; + + case 'sync_request': + // 同步请求 + this.handleSyncRequest(network, message); + break; + } + } + + /** + * 处理数据同步 + */ + private processSynchronization(network: NetworkComponent, deltaTime: number): void { + const currentTime = Date.now(); + const syncInterval = 1000 / network.config.syncFrequency; // 转换为毫秒 + + if (currentTime - network.syncData.lastSyncTime >= syncInterval) { + this.performSynchronization(network); + network.syncData.lastSyncTime = currentTime; + } + + // 处理排队的更新 + this.processQueuedUpdates(network); + } + + /** + * 执行同步 + */ + private performSynchronization(network: NetworkComponent): void { + if (network.syncData.dirtyFlags.size === 0) { + return; // 没有需要同步的数据 + } + + const syncData = { + networkId: network.networkId, + timestamp: Date.now(), + properties: Array.from(network.syncData.dirtyFlags), + checksum: this.calculateChecksum(network) + }; + + // 发送同步数据给连接的玩家 + network.connectedPlayerIds.forEach(playerId => { + network.sendMessage(playerId, 'sync_data', syncData, 7); + }); + + // 记录同步历史 + network.syncData.syncHistory.push({ + timestamp: syncData.timestamp, + dataSize: this.estimateMessageSize(syncData), + properties: syncData.properties, + success: true + }); + + // 清理脏标记 + network.syncData.dirtyFlags.clear(); + } + + /** + * 处理排队的更新 + */ + private processQueuedUpdates(network: NetworkComponent): void { + // 按优先级和时间戳排序 + network.syncData.queuedUpdates.sort((a, b) => { + if (a.priority !== b.priority) { + return b.priority - a.priority; // 高优先级优先 + } + return a.timestamp - b.timestamp; // 时间戳早的优先 + }); + + // 处理前10个更新 + const updatesToProcess = network.syncData.queuedUpdates.splice(0, 10); + for (const update of updatesToProcess) { + network.markDirty(update.property); + } + } + + /** + * 处理群组通信 + */ + private processGroupCommunication(network: NetworkComponent): void { + if (network.groupMemberIds.length === 0) { + return; + } + + // 群组消息广播 + if (Math.random() < 0.01) { // 1% 概率发送群组消息 + const groupMessage = { + type: 'group_update', + data: { + sender: network.networkId, + timestamp: Date.now(), + groupSize: network.groupMemberIds.length, + status: network.connectionState + } + }; + + network.groupMemberIds.forEach(memberId => { + if (memberId !== network.networkId) { + network.sendMessage(memberId, 'group_message', groupMessage, 5); + } + }); + } + } + + /** + * 处理全局消息 + */ + private processGlobalMessages(): void { + // 移除过期消息 + const currentTime = Date.now(); + this.globalMessageQueue = this.globalMessageQueue.filter(msg => + currentTime - msg.timestamp < 30000 // 30秒过期 + ); + + // 按优先级排序 + this.globalMessageQueue.sort((a, b) => b.priority - a.priority); + } + + /** + * 更新全局网络统计 + */ + private updateGlobalNetworkStats(entities: Entity[]): void { + let totalPing = 0; + let connectedCount = 0; + let totalTraffic = 0; + + for (const entity of entities) { + const network = entity.getComponent(NetworkComponent); + if (network && network.connectionState === 'connected') { + totalPing += network.connection.ping; + connectedCount++; + totalTraffic += network.networkStats.totalBytesSent + network.networkStats.totalBytesReceived; + } + } + + this.networkStats.averagePing = connectedCount > 0 ? totalPing / connectedCount : 0; + this.networkStats.networkTraffic = totalTraffic; + } + + /** + * 辅助方法 + */ + private generateSessionId(): string { + return 'session_' + Math.random().toString(36).substring(2, 15); + } + + private estimateMessageSize(message: any): number { + return JSON.stringify(message).length; + } + + private calculateChecksum(network: NetworkComponent): string { + // 简单的校验和计算 + const data = JSON.stringify({ + networkId: network.networkId, + connectionState: network.connectionState + }); + return btoa(data).substring(0, 8); + } + + private sendHeartbeat(network: NetworkComponent): void { + network.sendMessage('server', 'heartbeat', { timestamp: Date.now() }, 10); + } + + private findNetworkComponentById(networkId: string): NetworkComponent | null { + // 这里应该有一个全局的网络组件注册表 + // 为了简化,我们返回null + return null; + } + + private handleSyncRequest(network: NetworkComponent, message: any): void { + // 处理同步请求 + const response = { + requestId: message.data.requestId, + data: this.gatherSyncData(network), + timestamp: Date.now() + }; + + network.sendMessage(message.from, 'sync_response', response, 8); + } + + private gatherSyncData(network: NetworkComponent): any { + return { + networkId: network.networkId, + connectionState: network.connectionState, + ping: network.connection.ping, + groupSize: network.groupMemberIds.length + }; + } + + /** + * 系统初始化时调用 + */ + public initialize(): void { + super.initialize(); + console.log('🌐 网络系统已启动'); + } + + /** + * 获取系统统计信息 + */ + public getSystemStats(): any { + return { + ...this.networkStats, + globalMessageQueueSize: this.globalMessageQueue.length, + systemName: 'NetworkSystem' + }; + } +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NetworkSystem.ts.meta b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NetworkSystem.ts.meta new file mode 100644 index 00000000..5d87f043 --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NetworkSystem.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "09cb67c9-12ef-48e0-949d-c8edf2c7ae22", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NodeRenderSystem.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NodeRenderSystem.ts new file mode 100644 index 00000000..cffa755f --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NodeRenderSystem.ts @@ -0,0 +1,475 @@ +import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework'; +import { NodeComponent, Transform, Renderer } from '../components'; +import { Node, Vec3, Color } from 'cc'; + +/** + * 节点渲染系统 - 处理NodeComponent和Cocos Creator节点的同步 + */ +export class NodeRenderSystem extends EntitySystem { + + /** 渲染统计 */ + private renderStats = { + totalNodes: 0, + visibleNodes: 0, + renderCalls: 0, + averageRenderTime: 0, + totalRenderTime: 0, + frameCount: 0 + }; + + /** 节点池 */ + private nodePool: Node[] = []; + + /** 性能监控 */ + private performanceMonitor = { + frameStartTime: 0, + renderTimeHistory: [] as number[], + cullCount: 0, + frustumCullCount: 0 + }; + + constructor() { + // 处理具有NodeComponent的实体 + super(Matcher.empty().all(NodeComponent)); + } + + /** + * 处理所有实体 + */ + protected process(entities: Entity[]): void { + this.performanceMonitor.frameStartTime = performance.now(); + + this.renderStats.totalNodes = entities.length; + this.renderStats.visibleNodes = 0; + this.renderStats.renderCalls = 0; + + for (const entity of entities) { + this.processEntity(entity); + } + + // 处理节点层次结构 + this.updateNodeHierarchy(entities); + + // 更新性能统计 + this.updatePerformanceStats(); + + // 清理过期的性能缓存 + this.cleanupPerformanceCache(entities); + } + + /** + * 处理单个实体 + */ + private processEntity(entity: Entity): void { + const nodeComponent = entity.getComponent(NodeComponent); + const transform = entity.getComponent(Transform); + const renderer = entity.getComponent(Renderer); + + if (!nodeComponent) return; + + const renderStartTime = performance.now(); + + // 确保有对应的Cocos Creator节点 + this.ensureNode(nodeComponent, entity); + + // 同步Transform数据 + if (transform && nodeComponent.node) { + this.syncTransform(nodeComponent, transform); + } + + // 同步渲染数据 + if (renderer && nodeComponent.node) { + this.syncRenderer(nodeComponent, renderer); + } + + // 更新节点配置 + this.updateNodeConfig(nodeComponent); + + // 执行视锥体剔除 + const isVisible = this.performCulling(nodeComponent); + if (isVisible) { + this.renderStats.visibleNodes++; + this.performRender(nodeComponent); + } + + // 更新性能统计 + const renderTime = performance.now() - renderStartTime; + nodeComponent.updatePerformance(renderTime); + + this.renderStats.renderCalls++; + this.renderStats.totalRenderTime += renderTime; + } + + /** + * 确保节点存在 + */ + private ensureNode(nodeComponent: NodeComponent, entity: Entity): void { + if (!nodeComponent.node) { + // 从对象池中获取节点或创建新节点 + nodeComponent.node = this.getNodeFromPool() || new Node(nodeComponent.nodeConfig.name); + + // 初始化节点 + this.initializeNode(nodeComponent.node, nodeComponent, entity); + } + } + + /** + * 从对象池获取节点 + */ + private getNodeFromPool(): Node | null { + return this.nodePool.pop() || null; + } + + /** + * 初始化节点 + */ + private initializeNode(node: Node, nodeComponent: NodeComponent, entity: Entity): void { + const config = nodeComponent.nodeConfig; + + // 设置基本属性 + node.name = config.name; + node.layer = config.layer; + node.active = config.renderData.visible; + + // 设置变换 + node.setPosition(config.transformData.position); + node.setRotationFromEuler(config.transformData.rotation); + node.setScale(config.transformData.scale); + + // 设置渲染属性 + const opacity = Math.floor(config.renderData.opacity * 255); + // 这里可以设置更多Cocos Creator特定的属性 + + // 添加用户数据 + config.userData.entityId = entity.id; + config.userData.componentId = nodeComponent.id; + } + + /** + * 同步Transform数据 + */ + private syncTransform(nodeComponent: NodeComponent, transform: Transform): void { + const node = nodeComponent.node!; + const config = nodeComponent.nodeConfig; + + // 更新配置中的变换数据 + config.transformData.position.set(transform.position); + config.transformData.rotation.set(transform.rotation); + config.transformData.scale.set(transform.scale); + + // 同步到Cocos Creator节点 + node.setPosition(transform.position); + node.setRotationFromEuler(transform.rotation); + node.setScale(transform.scale); + + // 更新缓存数据 + nodeComponent.complexData.cache.textureCache.set('lastPosition', transform.position.clone()); + } + + /** + * 同步渲染数据 + */ + private syncRenderer(nodeComponent: NodeComponent, renderer: Renderer): void { + const node = nodeComponent.node!; + const config = nodeComponent.nodeConfig; + + // 更新配置中的渲染数据 + config.renderData.color.set(renderer.color); + config.renderData.opacity = renderer.alpha; + config.renderData.visible = renderer.visible && renderer.alpha > 0; + + // 同步到Cocos Creator节点 + node.active = config.renderData.visible; + + // 更新材质缓存 + nodeComponent.complexData.cache.materialCache.set('currentColor', renderer.color.clone()); + nodeComponent.complexData.cache.materialCache.set('alpha', renderer.alpha); + } + + /** + * 更新节点配置 + */ + private updateNodeConfig(nodeComponent: NodeComponent): void { + const config = nodeComponent.nodeConfig; + const currentTime = Date.now(); + + // 更新统计信息 + nodeComponent.complexData.statistics.frameCount++; + nodeComponent.complexData.statistics.lastUpdateTime = currentTime; + + // 更新用户数据 + config.userData.lastFrameUpdate = currentTime; + config.userData.frameCount = nodeComponent.complexData.statistics.frameCount; + + // 动态调整配置 + if (Math.random() < 0.01) { // 1% 概率调整 + config.renderData.opacity *= (0.95 + Math.random() * 0.1); // 轻微透明度变化 + config.renderData.opacity = Math.max(0.1, Math.min(1.0, config.renderData.opacity)); + } + } + + /** + * 执行视锥体剔除 + */ + private performCulling(nodeComponent: NodeComponent): boolean { + if (!nodeComponent.node) { + return false; + } + + const config = nodeComponent.nodeConfig; + + // 简单的可见性检查 + if (!config.renderData.visible || config.renderData.opacity <= 0) { + this.performanceMonitor.cullCount++; + return false; + } + + // 距离剔除 + const position = config.transformData.position; + const distance = position.length(); + if (distance > 1000) { // 超过1000单位距离的对象被剔除 + this.performanceMonitor.frustumCullCount++; + return false; + } + + // 层级剔除 + if (config.layer < 0) { + this.performanceMonitor.cullCount++; + return false; + } + + return true; + } + + /** + * 执行渲染 + */ + private performRender(nodeComponent: NodeComponent): void { + if (!nodeComponent.node) return; + + const renderStartTime = performance.now(); + + // 模拟复杂的渲染过程 + this.simulateRenderingWork(nodeComponent); + + // 更新子节点 + this.updateChildNodes(nodeComponent); + + // 更新着色器缓存 + this.updateShaderCache(nodeComponent); + + const renderTime = performance.now() - renderStartTime; + + // 更新性能统计 + const perf = nodeComponent.complexData.statistics.performance; + perf.renderHistory.push(renderTime); + + if (perf.renderHistory.length > 100) { + perf.renderHistory.shift(); + } + + perf.avgRenderTime = perf.renderHistory.reduce((a, b) => a + b, 0) / perf.renderHistory.length; + perf.maxRenderTime = Math.max(perf.maxRenderTime, renderTime); + } + + /** + * 模拟渲染工作 + */ + private simulateRenderingWork(nodeComponent: NodeComponent): void { + const complexity = nodeComponent.complexData.cache.materialCache.size + + nodeComponent.complexData.cache.textureCache.size; + + // 模拟基于复杂度的计算工作 + let iterations = Math.min(complexity * 10, 1000); + let result = 0; + for (let i = 0; i < iterations; i++) { + result += Math.sin(i * 0.001) * Math.cos(i * 0.002); + } + + // 存储计算结果到缓存 + nodeComponent.complexData.cache.shaderCache.set('computeResult', result); + } + + /** + * 更新子节点 + */ + private updateChildNodes(nodeComponent: NodeComponent): void { + if (nodeComponent.children.length === 0) return; + + const parentNode = nodeComponent.node!; + + // 同步子节点 + for (let i = 0; i < nodeComponent.children.length; i++) { + const childNode = nodeComponent.children[i]; + if (childNode && childNode.parent !== parentNode) { + parentNode.addChild(childNode); + } + } + + // 更新层次结构数据 + nodeComponent.complexData.hierarchy.siblingIndex = parentNode.getSiblingIndex(); + + // 更新子组件的层次深度(需要通过实体管理器查找) + // 这里省略了复杂的查找逻辑,避免循环引用 + if (nodeComponent.nodeConfig.childIds.length > 0) { + // 实际项目中应该通过实体管理器查找子实体并更新深度 + // 为了示例简化,我们只更新自己的深度 + nodeComponent.complexData.hierarchy.depth = Math.max(0, nodeComponent.complexData.hierarchy.depth); + } + } + + /** + * 更新着色器缓存 + */ + private updateShaderCache(nodeComponent: NodeComponent): void { + const shaderCache = nodeComponent.complexData.cache.shaderCache; + + // 模拟着色器参数更新 + const currentTime = Date.now(); + shaderCache.set('time', currentTime); + shaderCache.set('frameCount', nodeComponent.complexData.statistics.frameCount); + + // 清理过期的着色器缓存 + if (shaderCache.size > 50) { + const keys = Array.from(shaderCache.keys()); + const oldestKey = keys[0]; + shaderCache.delete(oldestKey); + } + } + + /** + * 更新节点层次结构 + */ + private updateNodeHierarchy(entities: Entity[]): void { + // 构建层次结构映射 + const nodeMap = new Map(); + + entities.forEach(entity => { + const nodeComponent = entity.getComponent(NodeComponent); + if (nodeComponent) { + nodeMap.set(entity.id, nodeComponent); + } + }); + + // 更新层次关系(使用ID避免循环引用) + nodeMap.forEach((nodeComponent, entityId) => { + // 更新根节点ID + if (!nodeComponent.complexData.hierarchy.parentId) { + nodeComponent.complexData.hierarchy.rootId = entityId; + } else { + // 查找根节点ID(简化版本,避免深度遍历) + let currentParentId = nodeComponent.complexData.hierarchy.parentId; + let depth = 0; + + // 限制深度以避免无限循环 + while (currentParentId && depth < 10) { + const parentNode = nodeMap.get(currentParentId); + if (parentNode && parentNode.complexData.hierarchy.parentId) { + currentParentId = parentNode.complexData.hierarchy.parentId; + depth++; + } else { + break; + } + } + + nodeComponent.complexData.hierarchy.rootId = currentParentId || entityId; + } + }); + } + + /** + * 更新性能统计 + */ + private updatePerformanceStats(): void { + const frameTime = performance.now() - this.performanceMonitor.frameStartTime; + + this.performanceMonitor.renderTimeHistory.push(frameTime); + if (this.performanceMonitor.renderTimeHistory.length > 60) { + this.performanceMonitor.renderTimeHistory.shift(); + } + + this.renderStats.frameCount++; + if (this.renderStats.renderCalls > 0) { + this.renderStats.averageRenderTime = this.renderStats.totalRenderTime / this.renderStats.renderCalls; + } + } + + /** + * 清理性能缓存 + */ + private cleanupPerformanceCache(entities: Entity[]): void { + entities.forEach(entity => { + const nodeComponent = entity.getComponent(NodeComponent); + if (nodeComponent) { + const caches = nodeComponent.complexData.cache; + + // 清理纹理缓存 + if (caches.textureCache.size > 100) { + const keys = Array.from(caches.textureCache.keys()); + const toDelete = keys.slice(0, 20); // 删除最旧的20个 + toDelete.forEach(key => caches.textureCache.delete(key)); + } + + // 清理材质缓存 + if (caches.materialCache.size > 50) { + const keys = Array.from(caches.materialCache.keys()); + const toDelete = keys.slice(0, 10); // 删除最旧的10个 + toDelete.forEach(key => caches.materialCache.delete(key)); + } + } + }); + } + + /** + * 回收节点到对象池 + */ + public recycleNode(node: Node): void { + if (this.nodePool.length < 100) { // 限制对象池大小 + node.removeFromParent(); + node.destroyAllChildren(); + this.nodePool.push(node); + } else { + node.destroy(); + } + } + + /** + * 系统初始化时调用 + */ + public initialize(): void { + super.initialize(); + console.log('🎨 节点渲染系统已启动'); + + // 预热对象池 + for (let i = 0; i < 10; i++) { + this.nodePool.push(new Node(`PooledNode_${i}`)); + } + } + + /** + * 当实体被移除时 + */ + protected onRemoved(entity: Entity): void { + const nodeComponent = entity.getComponent(NodeComponent); + if (nodeComponent && nodeComponent.node) { + this.recycleNode(nodeComponent.node); + nodeComponent.node = null; + } + } + + /** + * 获取系统统计信息 + */ + public getSystemStats(): any { + return { + ...this.renderStats, + cullCount: this.performanceMonitor.cullCount, + frustumCullCount: this.performanceMonitor.frustumCullCount, + nodePoolSize: this.nodePool.length, + averageFrameTime: this.performanceMonitor.renderTimeHistory.length > 0 + ? this.performanceMonitor.renderTimeHistory.reduce((a, b) => a + b, 0) / this.performanceMonitor.renderTimeHistory.length + : 0, + systemName: 'NodeRenderSystem' + }; + } +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NodeRenderSystem.ts.meta b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NodeRenderSystem.ts.meta new file mode 100644 index 00000000..25ad5e55 --- /dev/null +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/NodeRenderSystem.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "b4593488-685b-4e52-800b-b2a2990305d6", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/index.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/index.ts index 1da25ed1..d6ba54e3 100644 --- a/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/index.ts +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/systems/index.ts @@ -1,4 +1,7 @@ // 导出所有系统 export { MovementSystem } from './MovementSystem'; export { HealthSystem } from './HealthSystem'; -export { RandomMovementSystem } from './RandomMovementSystem'; \ No newline at end of file +export { RandomMovementSystem } from './RandomMovementSystem'; +export { AISystem } from './AISystem'; +export { NetworkSystem } from './NetworkSystem'; +export { NodeRenderSystem } from './NodeRenderSystem'; \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension index 60e25ffa..e222e9e9 160000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension @@ -1 +1 @@ -Subproject commit 60e25ffa1c6d31a7d992b0c82ca7f4312229db6e +Subproject commit e222e9e9d86e3704ff5c2c9aff929f8dd38d5384 diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-terrain-gen b/extensions/cocos/cocos-ecs/extensions/cocos-terrain-gen index 691005f1..8ebcce72 160000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-terrain-gen +++ b/extensions/cocos/cocos-ecs/extensions/cocos-terrain-gen @@ -1 +1 @@ -Subproject commit 691005f1f1bb169b4e49f62134fe2f78ac95c4ec +Subproject commit 8ebcce722a0889197fe006012d214178dd2a4e49 diff --git a/extensions/cocos/cocos-ecs/package-lock.json b/extensions/cocos/cocos-ecs/package-lock.json index 725cd385..cfb44a98 100644 --- a/extensions/cocos/cocos-ecs/package-lock.json +++ b/extensions/cocos/cocos-ecs/package-lock.json @@ -7,7 +7,7 @@ "name": "cocos-ecs", "dependencies": { "@esengine/ai": "^2.0.15", - "@esengine/ecs-framework": "^2.1.22" + "@esengine/ecs-framework": "^2.1.23" } }, "node_modules/@esengine/ai": { @@ -22,9 +22,10 @@ } }, "node_modules/@esengine/ecs-framework": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/@esengine/ecs-framework/-/ecs-framework-2.1.22.tgz", - "integrity": "sha512-U5zQJJC7eQeiIUMV1Y784rOJoVK9x8ZLDI65+8Bbt3PcBWzn+qspLe4FBlJU4XBZcMbmEo5vJltvRNkN7iVPZA==", + "version": "2.1.23", + "resolved": "https://registry.npmjs.org/@esengine/ecs-framework/-/ecs-framework-2.1.23.tgz", + "integrity": "sha512-Em4w+AQ2c/eXN6FqTwySwu869yPjG9kHZp74vEDVBLb7uQOw+g8aMrSALS4tYHTj0PxNofcNplS38xjGsFTekA==", + "license": "MIT", "engines": { "node": ">=16.0.0" } diff --git a/extensions/cocos/cocos-ecs/package.json b/extensions/cocos/cocos-ecs/package.json index e151e872..c5470b8c 100644 --- a/extensions/cocos/cocos-ecs/package.json +++ b/extensions/cocos/cocos-ecs/package.json @@ -5,7 +5,7 @@ "version": "3.8.6" }, "dependencies": { - "@esengine/ecs-framework": "^2.1.22", - "@esengine/ai": "^2.0.15" + "@esengine/ai": "^2.0.15", + "@esengine/ecs-framework": "^2.1.23" } -} \ No newline at end of file +} diff --git a/src/Utils/Debug/DebugManager.ts b/src/Utils/Debug/DebugManager.ts index 9c32b29c..6c9cd87e 100644 --- a/src/Utils/Debug/DebugManager.ts +++ b/src/Utils/Debug/DebugManager.ts @@ -136,6 +136,22 @@ export class DebugManager { } 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', @@ -149,9 +165,139 @@ export class DebugManager { } } catch (error) { // console.error('[ECS Debug] 处理消息失败:', error); + if (message.requestId) { + this.webSocketManager.send({ + type: 'error_response', + requestId: message.requestId, + error: error instanceof Error ? error.message : String(error) + }); + } } } + /** + * 处理展开懒加载对象请求 + */ + private handleExpandLazyObjectRequest(message: any): void { + try { + const { entityId, componentIndex, propertyPath, requestId } = message; + + if (entityId === undefined || componentIndex === undefined || !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) + }); + } + } + + /** + * 处理获取组件属性请求 + */ + private handleGetComponentPropertiesRequest(message: any): void { + try { + const { entityId, componentIndex, requestId } = message; + + if (entityId === undefined || componentIndex === undefined) { + 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) + }); + } + } + + /** + * 处理获取原始实体列表请求 + */ + private handleGetRawEntityListRequest(message: any): void { + 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) + }); + } + } + + /** + * 处理获取实体详情请求 + */ + private handleGetEntityDetailsRequest(message: any): void { + try { + const { entityId, requestId } = message; + + if (entityId === undefined) { + 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) + }); + } + } + + + + + /** * 处理内存快照请求 */ @@ -174,60 +320,46 @@ export class DebugManager { * 捕获内存快照 */ private captureMemorySnapshot(): any { - const scene = Core.scene; - if (!scene) { - throw new Error('没有活跃的场景'); - } - - const entityList = (scene as any).entities; - if (!entityList?.buffer) { - throw new Error('无法访问实体列表'); - } - const timestamp = Date.now(); - // 1. 收集基础内存信息 + // 使用专门的内存计算方法收集实体数据 + const entityData = this.entityCollector.collectEntityDataWithMemory(); + + // 收集其他内存统计 const baseMemoryInfo = this.collectBaseMemoryInfo(); - - // 2. 收集实体内存统计 - const entityMemoryStats = this.collectEntityMemoryStats(entityList); - - // 3. 收集组件内存统计 - const componentMemoryStats = this.collectComponentMemoryStats(entityList); - - // 4. 收集系统内存统计 + const componentMemoryStats = this.collectComponentMemoryStats((Core.scene as any)?.entities); const systemMemoryStats = this.collectSystemMemoryStats(); - - // 5. 收集对象池内存统计 const poolMemoryStats = this.collectPoolMemoryStats(); - - // 6. 收集性能监控器统计 const performanceStats = this.collectPerformanceStats(); + // 计算总内存使用量 + const totalEntityMemory = entityData.entitiesPerArchetype.reduce((sum, arch) => sum + arch.memory, 0); + return { timestamp, version: '2.0', summary: { - totalEntities: entityList.buffer.length, + totalEntities: entityData.totalEntities, totalMemoryUsage: baseMemoryInfo.usedMemory, totalMemoryLimit: baseMemoryInfo.totalMemory, memoryUtilization: (baseMemoryInfo.usedMemory / baseMemoryInfo.totalMemory * 100), gcCollections: baseMemoryInfo.gcCollections, - entityMemory: entityMemoryStats.totalMemory, + entityMemory: totalEntityMemory, componentMemory: componentMemoryStats.totalMemory, systemMemory: systemMemoryStats.totalMemory, poolMemory: poolMemoryStats.totalMemory }, baseMemory: baseMemoryInfo, - entities: entityMemoryStats, + entities: { + totalMemory: totalEntityMemory, + entityCount: entityData.totalEntities, + archetypes: entityData.entitiesPerArchetype, + largestEntities: entityData.topEntitiesByComponents + }, components: componentMemoryStats, systems: systemMemoryStats, pools: poolMemoryStats, - performance: performanceStats, - // 保持向后兼容 - totalEntities: entityList.buffer.length, - totalMemory: entityMemoryStats.totalMemory, - detailedArchetypes: entityMemoryStats.archetypes + performance: performanceStats }; } @@ -275,87 +407,7 @@ export class DebugManager { return memoryInfo; } - /** - * 收集实体内存统计 - */ - private collectEntityMemoryStats(entityList: any): any { - const archetypeStats = new Map(); - const entitySizeDistribution = new Map(); // 按大小范围分布 - let totalMemory = 0; - let maxEntityMemory = 0; - let minEntityMemory = Number.MAX_VALUE; - const largestEntities: any[] = []; - for (const entity of entityList.buffer) { - if (!entity || entity.destroyed) continue; - - // 生成组件签名 - const componentTypes = entity.components ? - entity.components.map((c: any) => c.constructor.name).sort() : []; - const signature = componentTypes.length > 0 ? componentTypes.join(',') : 'Empty'; - - // 计算实体内存使用 - const entityMemory = this.entityCollector.estimateEntityMemoryUsage(entity); - totalMemory += entityMemory; - maxEntityMemory = Math.max(maxEntityMemory, entityMemory); - minEntityMemory = Math.min(minEntityMemory, entityMemory); - - // 收集大实体信息 - largestEntities.push({ - id: entity.id, - name: entity.name || `Entity_${entity.id}`, - memory: entityMemory, - componentCount: componentTypes.length, - componentTypes: componentTypes - }); - - // 内存大小分布统计 - const sizeCategory = this.getMemorySizeCategory(entityMemory); - entitySizeDistribution.set(sizeCategory, (entitySizeDistribution.get(sizeCategory) || 0) + 1); - - // 更新原型统计 - if (!archetypeStats.has(signature)) { - archetypeStats.set(signature, { count: 0, memory: 0, entities: [] }); - } - const stats = archetypeStats.get(signature)!; - stats.count++; - stats.memory += entityMemory; - stats.entities.push({ - id: entity.id, - name: entity.name || `Entity_${entity.id}`, - memory: entityMemory, - componentCount: componentTypes.length - }); - } - - // 排序并限制返回的实体数量 - largestEntities.sort((a, b) => b.memory - a.memory); - - // 转换原型统计 - const archetypes = Array.from(archetypeStats.entries()).map(([signature, stats]) => ({ - signature, - count: stats.count, - memory: stats.memory, - averageMemory: stats.memory / stats.count, - percentage: totalMemory > 0 ? (stats.memory / totalMemory * 100) : 0, - entities: stats.entities.sort((a, b) => b.memory - a.memory).slice(0, 5) // 只返回前5个最大的 - })).sort((a, b) => b.memory - a.memory); - - return { - totalMemory, - entityCount: entityList.buffer.length, - averageEntityMemory: totalMemory / entityList.buffer.length, - maxEntityMemory, - minEntityMemory: minEntityMemory === Number.MAX_VALUE ? 0 : minEntityMemory, - archetypes, - largestEntities: largestEntities.slice(0, 10), - sizeDistribution: Array.from(entitySizeDistribution.entries()).map(([category, count]) => ({ - category, - count, - percentage: (count / entityList.buffer.length * 100) - })) - }; - } /** * 收集组件内存统计 diff --git a/src/Utils/Debug/EntityDataCollector.ts b/src/Utils/Debug/EntityDataCollector.ts index fac900d7..9adb44f1 100644 --- a/src/Utils/Debug/EntityDataCollector.ts +++ b/src/Utils/Debug/EntityDataCollector.ts @@ -7,9 +7,6 @@ import { Component } from '../../ECS/Component'; * 实体数据收集器 */ export class EntityDataCollector { - /** - * 收集实体数据 - */ public collectEntityData(): IEntityDebugData { const scene = Core.scene; if (!scene) { @@ -25,7 +22,6 @@ export class EntityDataCollector { try { stats = entityList.getStats ? entityList.getStats() : this.calculateFallbackEntityStats(entityList); } catch (error) { - // console.warn('[ECS Debug] 获取实体统计失败:', error); return { totalEntities: 0, activeEntities: 0, @@ -38,9 +34,122 @@ export class EntityDataCollector { }; } - // 利用ArchetypeSystem的缓存数据 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: {} + }; + } + + + public getRawEntityList(): Array<{ + id: number; + name: string; + active: boolean; + enabled: boolean; + activeInHierarchy: boolean; + componentCount: number; + componentTypes: string[]; + parentId: number | null; + childIds: number[]; + depth: number; + tag: number; + updateOrder: number; + }> { + const scene = Core.scene; + if (!scene) return []; + + const entityList = (scene as any).entities; + if (!entityList?.buffer) return []; + + return entityList.buffer.map((entity: 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: Component) => component.constructor.name), + parentId: entity.parent?.id || null, + childIds: entity.children?.map((child: Entity) => child.id) || [], + depth: entity.getDepth ? entity.getDepth() : 0, + tag: entity.tag || 0, + updateOrder: entity.updateOrder || 0 + })); + } + + + public getEntityDetails(entityId: number): any { + try { + const scene = Core.scene; + if (!scene) return null; + + const entityList = (scene as any).entities; + if (!entityList?.buffer) return null; + + const entity = entityList.buffer.find((e: any) => e.id === entityId); + if (!entity) return null; + + const baseDebugInfo = entity.getDebugInfo ? + entity.getDebugInfo() : + this.buildFallbackEntityInfo(entity); + + const componentDetails = this.extractComponentDetails(entity.components); + + return { + ...baseDebugInfo, + parentName: entity.parent?.name || null, + components: componentDetails || [], + componentCount: entity.components?.length || 0, + componentTypes: entity.components?.map((comp: any) => comp.constructor.name) || [] + }; + } catch (error) { + return { + error: `获取实体详情失败: ${error instanceof Error ? error.message : String(error)}`, + components: [], + componentCount: 0, + componentTypes: [] + }; + } + } + + + public collectEntityDataWithMemory(): IEntityDebugData { + const scene = Core.scene; + if (!scene) { + return this.getEmptyEntityDebugData(); + } + + const entityList = (scene as any).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, @@ -53,64 +162,82 @@ export class EntityDataCollector { }; } - /** - * 获取空的实体数据 - */ - private getEmptyEntityDebugData(): IEntityDebugData { - return { - totalEntities: 0, - activeEntities: 0, - pendingAdd: 0, - pendingRemove: 0, - entitiesPerArchetype: [], - topEntitiesByComponents: [], - entityHierarchy: [], - entityDetailsMap: {} - }; - } - /** - * 计算实体统计信息 - */ - private calculateFallbackEntityStats(entityList: any): any { - const allEntities = entityList.buffer || []; - const activeEntities = allEntities.filter((entity: any) => - entity.enabled && !entity._isDestroyed - ); - - return { - totalEntities: allEntities.length, - activeEntities: activeEntities.length, - pendingAdd: 0, - pendingRemove: 0, - averageComponentsPerEntity: activeEntities.length > 0 ? - allEntities.reduce((sum: number, e: any) => sum + (e.components?.length || 0), 0) / activeEntities.length : 0 - }; - } - - /** - * 收集原型数据 - */ private collectArchetypeData(scene: any): { distribution: Array<{ signature: string; count: number; memory: number }>; topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>; } { - // 利用ArchetypeSystem的缓存数据 if (scene && scene.archetypeSystem && typeof scene.archetypeSystem.getAllArchetypes === 'function') { return this.extractArchetypeStatistics(scene.archetypeSystem); } - // 回退到传统方法 const entityContainer = { entities: scene.entities?.buffer || [] }; return { - distribution: this.getArchetypeDistribution(entityContainer), - topEntities: this.getTopEntitiesByComponents(entityContainer) + distribution: this.getArchetypeDistributionFast(entityContainer), + topEntities: this.getTopEntitiesByComponentsFast(entityContainer) }; } - /** - * 提取原型统计信息 - */ + private getArchetypeDistributionFast(entityContainer: any): Array<{ signature: string; count: number; memory: number }> { + const distribution = new Map(); + + if (entityContainer && entityContainer.entities) { + entityContainer.entities.forEach((entity: any) => { + const componentTypes = entity.components?.map((comp: any) => comp.constructor.name) || []; + 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); + } + + private getTopEntitiesByComponentsFast(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> { + if (!entityContainer || !entityContainer.entities) { + return []; + } + + return entityContainer.entities + .map((entity: any) => ({ + id: entity.id.toString(), + name: entity.name || `Entity_${entity.id}`, + componentCount: entity.components?.length || 0, + memory: 0 + })) + .sort((a: any, b: any) => b.componentCount - a.componentCount) + .slice(0, 10); + } + + + private collectArchetypeDataWithMemory(scene: any): { + distribution: Array<{ signature: string; count: number; memory: number }>; + topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>; + } { + 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) + }; + } + + private extractArchetypeStatistics(archetypeSystem: any): { distribution: Array<{ signature: string; count: number; memory: number }>; topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>; @@ -123,7 +250,43 @@ export class EntityDataCollector { const signature = archetype.componentTypes?.map((type: any) => 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: any) => { + 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 }; + } + + + private extractArchetypeStatisticsWithMemory(archetypeSystem: any): { + distribution: Array<{ signature: string; count: number; memory: number }>; + topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>; + } { + const archetypes = archetypeSystem.getAllArchetypes(); + const distribution: Array<{ signature: string; count: number; memory: number }> = []; + const topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }> = []; + + archetypes.forEach((archetype: any) => { + const signature = archetype.componentTypes?.map((type: any) => 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); @@ -142,7 +305,6 @@ export class EntityDataCollector { memory: actualMemory }); - // 收集组件数量最多的实体 if (archetype.entities) { archetype.entities.slice(0, 5).forEach((entity: any) => { topEntities.push({ @@ -155,85 +317,199 @@ export class EntityDataCollector { } }); - // 按实体数量排序 distribution.sort((a, b) => b.count - a.count); topEntities.sort((a, b) => b.componentCount - a.componentCount); return { distribution, topEntities }; } - /** - * 计算实体内存使用量 - */ - public estimateEntityMemoryUsage(entity: any): number { - const startTime = performance.now(); - - let totalSize = 0; - - // 计算实体基础大小 - totalSize += this.calculateObjectSize(entity, ['components', 'children', 'parent']); - - // 计算组件大小 - if (entity.components) { - entity.components.forEach((component: any) => { - totalSize += this.calculateObjectSize(component, ['entity']); + + private getArchetypeDistribution(entityContainer: any): Array<{ signature: string; count: number; memory: number }> { + const distribution = new Map(); + + if (entityContainer && entityContainer.entities) { + entityContainer.entities.forEach((entity: any) => { + const signature = entity.componentMask?.toString() || '0'; + const existing = distribution.get(signature); + distribution.set(signature, (existing || 0) + 1); }); } - - // 记录计算耗时 - const executionTime = performance.now() - startTime; - if (executionTime > 1) { - // console.debug(`[ECS Debug] 实体${entity.id}内存计算耗时: ${executionTime.toFixed(2)}ms`); - } - - return totalSize; + + return Array.from(distribution.entries()) + .map(([signature, count]) => ({ signature, count, memory: 0 })) + .sort((a, b) => b.count - a.count); + } + + private getArchetypeDistributionWithMemory(entityContainer: any): Array<{ signature: string; count: number; memory: number }> { + const distribution = new Map(); + + if (entityContainer && entityContainer.entities) { + entityContainer.entities.forEach((entity: any) => { + const componentTypes = entity.components?.map((comp: any) => comp.constructor.name) || []; + 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); + } + + + private getTopEntitiesByComponents(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> { + if (!entityContainer || !entityContainer.entities) { + return []; + } + + return entityContainer.entities + .map((entity: any) => ({ + id: entity.id.toString(), + name: entity.name || `Entity_${entity.id}`, + componentCount: entity.components?.length || 0, + memory: 0 + })) + .sort((a: any, b: any) => b.componentCount - a.componentCount); + } + + + private getTopEntitiesByComponentsWithMemory(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> { + if (!entityContainer || !entityContainer.entities) { + return []; + } + + return entityContainer.entities + .map((entity: any) => ({ + id: entity.id.toString(), + name: entity.name || `Entity_${entity.id}`, + componentCount: entity.components?.length || 0, + memory: this.estimateEntityMemoryUsage(entity) + })) + .sort((a: any, b: any) => b.componentCount - a.componentCount); + } + + + private getEmptyEntityDebugData(): IEntityDebugData { + return { + totalEntities: 0, + activeEntities: 0, + pendingAdd: 0, + pendingRemove: 0, + entitiesPerArchetype: [], + topEntitiesByComponents: [], + entityHierarchy: [], + entityDetailsMap: {} + }; + } + + + private calculateFallbackEntityStats(entityList: any): any { + const allEntities = entityList.buffer || []; + const activeEntities = allEntities.filter((entity: any) => + entity.enabled && !entity._isDestroyed + ); + + return { + totalEntities: allEntities.length, + activeEntities: activeEntities.length, + pendingAdd: 0, + pendingRemove: 0, + averageComponentsPerEntity: activeEntities.length > 0 ? + allEntities.reduce((sum: number, e: any) => sum + (e.components?.length || 0), 0) / activeEntities.length : 0 + }; + } + + public estimateEntityMemoryUsage(entity: any): number { + 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: any) => { + const componentSize = this.calculateObjectSize(component, ['entity']); + if (!isNaN(componentSize) && componentSize > 0) { + totalSize += componentSize; + } + }); + } + + return isNaN(totalSize) || totalSize < 0 ? 0 : totalSize; + } catch (error) { + return 0; + } } - /** - * 计算对象大小 - */ public calculateObjectSize(obj: any, excludeKeys: string[] = []): number { if (!obj || typeof obj !== 'object') return 0; + try { let size = 0; const visited = new WeakSet(); - - const calculate = (item: any): number => { - if (!item || typeof item !== 'object' || visited.has(item)) return 0; + const maxDepth = 3; + + const calculate = (item: any, depth: number = 0): number => { + if (!item || typeof item !== 'object' || visited.has(item) || depth >= maxDepth) { + return 0; + } visited.add(item); let itemSize = 0; try { - for (const key in item) { + const keys = Object.keys(item); + for (let i = 0; i < Math.min(keys.length, 50); i++) { + const key = keys[i]; if (excludeKeys.includes(key)) continue; const value = item[key]; - itemSize += key.length * 2; // key size + itemSize += key.length * 2; if (typeof value === 'string') { - itemSize += value.length * 2; + itemSize += Math.min(value.length * 2, 1000); } else if (typeof value === 'number') { itemSize += 8; } else if (typeof value === 'boolean') { itemSize += 4; } else if (typeof value === 'object' && value !== null) { - itemSize += calculate(value); + itemSize += calculate(value, depth + 1); + } } + } catch (error) { + return 0; } - } catch (error) { - // 忽略无法访问的属性 - } - - return itemSize; - }; - - return calculate(obj); + + return isNaN(itemSize) ? 0 : itemSize; + }; + + size = calculate(obj); + return isNaN(size) || size < 0 ? 0 : size; + } catch (error) { + return 0; + } } - /** - * 构建实体层次结构树 - */ + private buildEntityHierarchyTree(entityList: { buffer?: Entity[] }): Array<{ id: number; name: string; @@ -252,7 +528,7 @@ export class EntityDataCollector { const rootEntities: any[] = []; - // 直接遍历实体缓冲区,只收集根实体 + entityList.buffer.forEach((entity: Entity) => { if (!entity.parent) { const hierarchyNode = this.buildEntityHierarchyNode(entity); @@ -318,34 +594,28 @@ export class EntityDataCollector { if (!entityList?.buffer) return {}; const entityDetailsMap: Record = {}; - - // 批量处理实体,减少函数调用开销 const entities = entityList.buffer; - const batchSize = 50; + const batchSize = 100; for (let i = 0; i < entities.length; i += batchSize) { const batch = entities.slice(i, i + batchSize); batch.forEach((entity: Entity) => { - // 优先使用Entity的getDebugInfo方法 const baseDebugInfo = entity.getDebugInfo ? entity.getDebugInfo() : this.buildFallbackEntityInfo(entity); - // 利用组件缓存统计信息 const componentCacheStats = (entity as any).getComponentCacheStats ? (entity as any).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, @@ -384,17 +654,14 @@ export class EntityDataCollector { /** * 提取组件详细信息 */ - private extractComponentDetails(components: Component[]): Array<{ + public extractComponentDetails(components: Component[]): Array<{ typeName: string; properties: Record; }> { return components.map((component: Component) => { - // 获取组件类型名称,优先使用constructor.name let typeName = component.constructor.name; - // 如果constructor.name为空或者是通用名称,尝试其他方法 if (!typeName || typeName === 'Object' || typeName === 'Function') { - // 尝试从类型管理器获取 try { const { ComponentTypeManager } = require('../../ECS/Utils/ComponentTypeManager'); const typeManager = ComponentTypeManager.instance; @@ -402,267 +669,277 @@ export class EntityDataCollector { const typeId = typeManager.getTypeId(componentType); typeName = typeManager.getTypeName(typeId); } catch (error) { - // 如果类型管理器不可用,使用默认名称 typeName = 'UnknownComponent'; } } - const componentDetail = { + return { typeName: typeName, - properties: {} as Record + properties: { + _componentId: component.constructor.name, + _propertyCount: Object.keys(component).filter(key => !key.startsWith('_') && key !== 'entity').length, + _lazyLoad: true + } }; + }); + } + + /** + * 获取组件的完整属性信息(仅在需要时调用) + */ + public getComponentProperties(entityId: number, componentIndex: number): Record { + try { + const scene = Core.scene; + if (!scene) return {}; + + const entityList = (scene as any).entities; + if (!entityList?.buffer) return {}; + + const entity = entityList.buffer.find((e: any) => e.id === entityId); + if (!entity || componentIndex >= entity.components.length) return {}; + + const component = entity.components[componentIndex]; + const properties: Record = {}; - // 安全地提取组件属性 - try { const propertyKeys = Object.keys(component); propertyKeys.forEach(propertyKey => { - // 跳过私有属性和实体引用,避免循环引用 if (!propertyKey.startsWith('_') && propertyKey !== 'entity') { const propertyValue = (component as any)[propertyKey]; if (propertyValue !== undefined && propertyValue !== null) { - componentDetail.properties[propertyKey] = this.formatPropertyValue(propertyValue); - } + properties[propertyKey] = this.formatPropertyValue(propertyValue); + } } }); - } catch (error) { - componentDetail.properties['_extractionError'] = '属性提取失败'; - } - return componentDetail; - }); + return properties; + } catch (error) { + return { _error: '属性提取失败' }; + } } /** * 格式化属性值 */ private formatPropertyValue(value: any, depth: number = 0): any { - // 防止无限递归,限制最大深度 - if (depth > 5) { - return value?.toString() || 'null'; + if (value === null || value === undefined) { + return value; } - if (typeof value === 'object' && value !== null) { - if (Array.isArray(value)) { - // 对于数组,总是返回完整数组,让前端决定如何显示 - return value.map(item => this.formatPropertyValue(item, depth + 1)); - } else { - // 通用对象处理:提取所有可枚举属性,不限制数量 + 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); + } + } + + /** + * 格式化对象第一层 + */ + private formatObjectFirstLevel(obj: any): any { + 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: any = {}; + 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 keys = Object.keys(value); - if (keys.length === 0) { - return {}; + const value = obj[key]; + if (value !== null && value !== undefined) { + result[key] = this.formatPropertyValue(value, 1); + processedCount++; } - - const result: any = {}; - keys.forEach(key => { - const propValue = value[key]; - // 避免循环引用和函数属性 - if (propValue !== value && typeof propValue !== 'function') { - try { - result[key] = this.formatPropertyValue(propValue, depth + 1); - } catch (error) { - // 如果属性访问失败,记录错误信息 - result[key] = `[访问失败: ${error instanceof Error ? error.message : String(error)}]`; - } - } - }); - return result; } catch (error) { - return `[对象解析失败: ${error instanceof Error ? error.message : String(error)}]`; + result[key] = `[访问失败: ${error instanceof Error ? error.message : String(error)}]`; + processedCount++; } } + + return result; + } catch (error) { + return `[对象解析失败: ${error instanceof Error ? error.message : String(error)}]`; } - return value; } /** - * 获取Archetype分布 + * 创建懒加载占位符 */ - private getArchetypeDistribution(entityContainer: any): Array<{ signature: string; count: number; memory: number }> { - const distribution = new Map(); - - if (entityContainer && entityContainer.entities) { - entityContainer.entities.forEach((entity: any) => { - const signature = entity.componentMask?.toString() || '0'; - const existing = distribution.get(signature); - const memory = this.estimateEntityMemoryUsage(entity); - - if (existing) { - existing.count++; - existing.memory += memory; - } else { - distribution.set(signature, { count: 1, memory }); - } - }); - } - - return Array.from(distribution.entries()) - .map(([signature, data]) => ({ signature, ...data })) - .sort((a, b) => b.count - a.count); - } - - /** - * 获取组件数量最多的实体 - */ - private getTopEntitiesByComponents(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> { - if (!entityContainer || !entityContainer.entities) { - return []; - } - - return entityContainer.entities - .map((entity: any) => ({ - id: entity.id.toString(), - name: entity.name || `Entity_${entity.id}`, - componentCount: entity.components?.length || 0, - memory: this.estimateEntityMemoryUsage(entity) - })) - .sort((a: any, b: any) => b.componentCount - a.componentCount); - } - - /** - * 提取实体详细信息 - */ - private extractEntityDetails(entity: any): any { - const components = entity.components || []; - const componentDetails = this.extractComponentDetails(components); - - // 格式化组件掩码为更可读的形式 - const componentMask = entity.componentMask || entity._componentMask || 0; - const componentMaskBinary = componentMask.toString(2).padStart(32, '0'); - const componentMaskHex = '0x' + componentMask.toString(16).toUpperCase().padStart(8, '0'); - - // 生成组件掩码的可读描述 - const maskDescription = this.generateComponentMaskDescription(componentMask, components); + private createLazyLoadPlaceholder(obj: any): any { + try { + const typeName = obj.constructor?.name || 'Object'; + const summary = this.getObjectSummary(obj, typeName); return { - id: entity.id, - name: entity.name || `Entity_${entity.id}`, - active: entity.active !== false, - enabled: entity.enabled !== false, - activeInHierarchy: entity.activeInHierarchy !== false, - updateOrder: entity.updateOrder || 0, - tag: entity.tag || '未分配', - depth: entity.depth || 0, - componentMask: componentMask, - componentMaskHex: componentMaskHex, - componentMaskBinary: componentMaskBinary, - componentMaskDescription: maskDescription, - componentCount: components.length, - components: componentDetails, - - // 层次关系信息 - parentId: entity.parent?.id || null, - parentName: entity.parent?.name || null, - childCount: entity.children?.length || 0, - childIds: entity.children?.map((child: any) => child.id).slice(0, 10) || [], // 最多显示10个子实体ID - - // 内存信息 - estimatedMemory: this.estimateEntityMemoryUsage(entity), - - // 额外的调试信息 - destroyed: entity.destroyed || false, - scene: entity.scene?.name || '未知场景', - transform: this.extractTransformInfo(entity), - - // 实体状态描述 - statusDescription: this.generateEntityStatusDescription(entity) - }; - } - - /** - * 生成组件掩码的可读描述 - */ - private generateComponentMaskDescription(mask: number, components: any[]): string { - if (mask === 0) { - return '无组件'; - } - - if (components.length === 0) { - return `掩码值: ${mask} (组件信息不可用)`; - } - - const componentNames = components.map(c => c.constructor.name); - if (componentNames.length <= 3) { - return `包含: ${componentNames.join(', ')}`; - } else { - return `包含: ${componentNames.slice(0, 3).join(', ')} 等${componentNames.length}个组件`; - } - } - - /** - * 生成实体状态描述 - */ - private generateEntityStatusDescription(entity: any): string { - const statuses = []; - - if (entity.destroyed) { - statuses.push('已销毁'); - } else { - if (entity.active === false) { - statuses.push('非活跃'); - } - if (entity.enabled === false) { - statuses.push('已禁用'); - } - if (entity.activeInHierarchy === false) { - statuses.push('层次非活跃'); - } - if (statuses.length === 0) { - statuses.push('正常运行'); - } - } - - return statuses.join(', '); - } - - /** - * 提取变换信息 - */ - private extractTransformInfo(entity: any): any { - try { - // 尝试获取Transform组件或position/rotation/scale信息 - const transform = entity.transform || entity.getComponent?.('Transform') || null; - - if (transform) { - return { - position: this.formatVector3(transform.position || transform.localPosition), - rotation: this.formatVector3(transform.rotation || transform.localRotation), - scale: this.formatVector3(transform.scale || transform.localScale), - worldPosition: this.formatVector3(transform.worldPosition), - }; - } - - // 如果没有Transform组件,检查是否有直接的位置信息 - if (entity.position || entity.x !== undefined) { - return { - position: this.formatVector3({ - x: entity.x || entity.position?.x || 0, - y: entity.y || entity.position?.y || 0, - z: entity.z || entity.position?.z || 0 - }) - }; - } - - return null; + _isLazyObject: true, + _typeName: typeName, + _summary: summary, + _objectId: this.generateObjectId(obj) + }; } catch (error) { - return null; + return { + _isLazyObject: true, + _typeName: 'Unknown', + _summary: `无法分析的对象: ${error instanceof Error ? error.message : String(error)}`, + _objectId: Math.random().toString(36).substr(2, 9) + }; } } /** - * 格式化Vector3对象 + * 获取对象摘要信息 */ - private formatVector3(vector: any): string | null { - if (!vector) return null; - + private getObjectSummary(obj: any, typeName: string): string { try { - const x = typeof vector.x === 'number' ? vector.x.toFixed(2) : '0.00'; - const y = typeof vector.y === 'number' ? vector.y.toFixed(2) : '0.00'; - const z = typeof vector.z === 'number' ? vector.z.toFixed(2) : '0.00'; - - return `(${x}, ${y}, ${z})`; + if (typeName.toLowerCase().includes('vec') || typeName.toLowerCase().includes('vector')) { + if (obj.x !== undefined && obj.y !== undefined) { + const z = obj.z !== undefined ? obj.z : ''; + return `${typeName}(${obj.x}, ${obj.y}${z ? ', ' + z : ''})`; + } + } + + if (typeName.toLowerCase().includes('color')) { + if (obj.r !== undefined && obj.g !== undefined && obj.b !== undefined) { + const a = obj.a !== undefined ? 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 vector.toString(); + return `${typeName} (无法分析)`; } } + + /** + * 生成对象ID + */ + private generateObjectId(obj: any): string { + try { + if (obj.id !== undefined) return `obj_${obj.id}`; + if (obj._id !== undefined) return `obj_${obj._id}`; + if (obj.uuid !== undefined) return `obj_${obj.uuid}`; + if (obj._uuid !== undefined) return `obj_${obj._uuid}`; + + return `obj_${Math.random().toString(36).substr(2, 9)}`; + } catch { + return `obj_${Math.random().toString(36).substr(2, 9)}`; + } + } + + /** + * 展开懒加载对象(供调试面板调用) + */ + public expandLazyObject(entityId: number, componentIndex: number, propertyPath: string): any { + try { + const scene = Core.scene; + if (!scene) return null; + + const entityList = (scene as any).entities; + if (!entityList?.buffer) return null; + + // 找到对应的实体 + const entity = entityList.buffer.find((e: any) => 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)}` + }; + } + } + + /** + * 根据路径获取对象 + */ + private getObjectByPath(root: any, path: string): any { + if (!path) return root; + + const parts = path.split('.'); + let current = root; + + for (const part of parts) { + if (current === null || current === undefined) 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; + } } \ No newline at end of file diff --git a/thirdparty/BehaviourTree-ai b/thirdparty/BehaviourTree-ai index f3e91b9f..7df0745b 160000 --- a/thirdparty/BehaviourTree-ai +++ b/thirdparty/BehaviourTree-ai @@ -1 +1 @@ -Subproject commit f3e91b9f34eaa41d38a5a284be01597e0376c3b3 +Subproject commit 7df0745b80d52b6b4b3e8befdca7f6193f80f1c2