优化性能结构/延迟加载
新增测试代码用于测试性能
This commit is contained in:
@@ -42,11 +42,12 @@ export class ECSManager extends Component {
|
|||||||
// 1. 创建Core实例,启用调试功能
|
// 1. 创建Core实例,启用调试功能
|
||||||
if (this.debugMode) {
|
if (this.debugMode) {
|
||||||
Core.create({
|
Core.create({
|
||||||
|
debug: true,
|
||||||
|
enableEntitySystems: true,
|
||||||
debugConfig: {
|
debugConfig: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
websocketUrl: 'ws://localhost:8080',
|
websocketUrl: 'ws://localhost:8080',
|
||||||
autoReconnect: true,
|
autoReconnect: true,
|
||||||
updateInterval: 100,
|
|
||||||
debugFrameRate: 30,
|
debugFrameRate: 30,
|
||||||
channels: {
|
channels: {
|
||||||
entities: true,
|
entities: true,
|
||||||
@@ -57,9 +58,13 @@ export class ECSManager extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// ECS调试模式已启用
|
console.log('✅ ECS调试模式已启用');
|
||||||
} else {
|
} else {
|
||||||
Core.create(false);
|
Core.create({
|
||||||
|
debug: false,
|
||||||
|
enableEntitySystems: true
|
||||||
|
});
|
||||||
|
console.log('ℹ️ ECS调试模式已禁用');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 创建游戏场景
|
// 2. 创建游戏场景
|
||||||
|
|||||||
@@ -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<string, any>;
|
||||||
|
executionHistory: BehaviorExecution[];
|
||||||
|
};
|
||||||
|
memory: {
|
||||||
|
lastSeenEnemyPosition: Vec3 | null;
|
||||||
|
lastSeenEnemyTime: number;
|
||||||
|
knownLocations: Array<{
|
||||||
|
position: Vec3;
|
||||||
|
type: 'safe' | 'danger' | 'resource' | 'patrol';
|
||||||
|
confidence: number;
|
||||||
|
lastVisited: number;
|
||||||
|
}>;
|
||||||
|
relationships: Map<number, {
|
||||||
|
entityId: number;
|
||||||
|
relationship: 'ally' | 'enemy' | 'neutral';
|
||||||
|
trustLevel: number;
|
||||||
|
lastInteraction: number;
|
||||||
|
interactionHistory: Array<{
|
||||||
|
type: 'friendly' | 'hostile' | 'neutral';
|
||||||
|
timestamp: number;
|
||||||
|
impact: number;
|
||||||
|
}>;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 状态机 */
|
||||||
|
public stateMachine: {
|
||||||
|
states: Map<string, AIState>;
|
||||||
|
transitions: Map<string, Array<{
|
||||||
|
targetState: string;
|
||||||
|
condition: () => 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<string, any> = 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<string, any> = 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)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "cc0d3d0d-0c12-4007-8568-11b2cafdfb8f",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -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<string>;
|
||||||
|
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<string> = 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<string, number>;
|
||||||
|
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<string, {
|
||||||
|
messageId: string;
|
||||||
|
startTime: number;
|
||||||
|
expectedDuration: number;
|
||||||
|
status: 'processing' | 'completed' | 'failed';
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 复杂的网络缓存系统 */
|
||||||
|
public cacheSystem: {
|
||||||
|
playerCache: Map<string, {
|
||||||
|
playerId: string;
|
||||||
|
lastSeen: number;
|
||||||
|
cachedData: any;
|
||||||
|
cacheExpiry: number;
|
||||||
|
}>;
|
||||||
|
messageCache: Map<string, {
|
||||||
|
messageId: string;
|
||||||
|
content: any;
|
||||||
|
timestamp: number;
|
||||||
|
recipients: string[];
|
||||||
|
}>;
|
||||||
|
syncCache: Map<string, {
|
||||||
|
propertyPath: string;
|
||||||
|
value: any;
|
||||||
|
lastUpdated: number;
|
||||||
|
version: number;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "d9263549-7b26-4b4f-9a15-b82e7af5fbd5",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -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<string, any>;
|
||||||
|
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<string, any>;
|
||||||
|
materialCache: Map<string, any>;
|
||||||
|
shaderCache: Map<string, any>;
|
||||||
|
};
|
||||||
|
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<string, any>;
|
||||||
|
textures: Map<string, any>;
|
||||||
|
shaderParams: Record<string, any>;
|
||||||
|
}>;
|
||||||
|
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<string, any>): 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 = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "28e7e8cd-591e-4fde-bb14-d668724a6201",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -3,3 +3,6 @@ export { Transform } from './Transform';
|
|||||||
export { Health } from './Health';
|
export { Health } from './Health';
|
||||||
export { Velocity } from './Velocity';
|
export { Velocity } from './Velocity';
|
||||||
export { Renderer } from './Renderer';
|
export { Renderer } from './Renderer';
|
||||||
|
export { NodeComponent } from './NodeComponent';
|
||||||
|
export { AIComponent } from './AIComponent';
|
||||||
|
export { NetworkComponent } from './NetworkComponent';
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Scene } from '@esengine/ecs-framework';
|
import { Scene } from '@esengine/ecs-framework';
|
||||||
import { Color } from 'cc';
|
import { Color, Node } from 'cc';
|
||||||
import { MovementSystem, HealthSystem, RandomMovementSystem } from '../systems';
|
import { MovementSystem, HealthSystem, RandomMovementSystem, AISystem, NetworkSystem, NodeRenderSystem } from '../systems';
|
||||||
import { Transform, Health, Velocity, Renderer } from '../components';
|
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 MovementSystem());
|
||||||
this.addEntityProcessor(new HealthSystem());
|
this.addEntityProcessor(new HealthSystem());
|
||||||
this.addEntityProcessor(new RandomMovementSystem());
|
this.addEntityProcessor(new RandomMovementSystem());
|
||||||
|
// this.addEntityProcessor(new AISystem());
|
||||||
|
// this.addEntityProcessor(new NetworkSystem());
|
||||||
|
// this.addEntityProcessor(new NodeRenderSystem());
|
||||||
|
|
||||||
// 创建测试实体
|
// 创建大量复杂的测试实体
|
||||||
this.createTestEntities();
|
this.createComplexTestEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建测试实体
|
* 创建复杂的测试实体(1000+个)
|
||||||
*/
|
*/
|
||||||
private createTestEntities(): void {
|
private createComplexTestEntities(): void {
|
||||||
console.log('🚀 开始创建测试实体...');
|
console.log('🚀 开始创建大量复杂测试实体...');
|
||||||
|
|
||||||
// 创建玩家实体
|
// 存储创建的AI和网络组件用于建立循环引用
|
||||||
const player = this.createEntity("Player");
|
const aiComponents: AIComponent[] = [];
|
||||||
player.addComponent(new Transform());
|
const networkComponents: NetworkComponent[] = [];
|
||||||
player.addComponent(new Health(150));
|
const nodeComponents: NodeComponent[] = [];
|
||||||
player.addComponent(new Velocity());
|
|
||||||
player.addComponent(new Renderer("player", new Color(0, 255, 0, 255)));
|
|
||||||
|
|
||||||
const playerTransform = player.getComponent(Transform);
|
// 1. 创建玩家实体(具有所有组件类型)
|
||||||
const playerHealth = player.getComponent(Health);
|
console.log('创建玩家实体...');
|
||||||
if (playerTransform) {
|
const player = this.createComplexEntity("Player", "player", new Color(0, 255, 0, 255), 0, 0, true, true, true);
|
||||||
playerTransform.setPosition(0, 0);
|
if (player) {
|
||||||
playerTransform.speed = 120;
|
const playerAI = player.getComponent(AIComponent);
|
||||||
}
|
const playerNetwork = player.getComponent(NetworkComponent);
|
||||||
if (playerHealth) {
|
const playerNode = player.getComponent(NodeComponent);
|
||||||
playerHealth.regenRate = 5; // 每秒回复5点生命值
|
|
||||||
|
if (playerAI) aiComponents.push(playerAI);
|
||||||
|
if (playerNetwork) networkComponents.push(playerNetwork);
|
||||||
|
if (playerNode) nodeComponents.push(playerNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建一些敌人实体
|
// 2. 创建AI智能体(500个)
|
||||||
for (let i = 0; i < 10; i++) {
|
console.log('创建AI智能体...');
|
||||||
const enemy = this.createEntity(`Enemy_${i}`);
|
for (let i = 0; i < 500; i++) {
|
||||||
enemy.addComponent(new Transform());
|
const entityName = `AI_Agent_${i}`;
|
||||||
enemy.addComponent(new Health(80));
|
const x = (Math.random() - 0.5) * 2000;
|
||||||
enemy.addComponent(new Velocity());
|
const y = (Math.random() - 0.5) * 2000;
|
||||||
enemy.addComponent(new Renderer("enemy", new Color(255, 0, 0, 255)));
|
const color = new Color(
|
||||||
|
Math.floor(Math.random() * 255),
|
||||||
|
Math.floor(Math.random() * 255),
|
||||||
|
Math.floor(Math.random() * 255),
|
||||||
|
255
|
||||||
|
);
|
||||||
|
|
||||||
const enemyTransform = enemy.getComponent(Transform);
|
const entity = this.createComplexEntity(entityName, "ai_agent", color, x, y, true, true, Math.random() > 0.5);
|
||||||
const enemyVelocity = enemy.getComponent(Velocity);
|
|
||||||
if (enemyTransform) {
|
if (entity) {
|
||||||
// 随机位置
|
const ai = entity.getComponent(AIComponent);
|
||||||
const x = (Math.random() - 0.5) * 800;
|
const network = entity.getComponent(NetworkComponent);
|
||||||
const y = (Math.random() - 0.5) * 600;
|
const node = entity.getComponent(NodeComponent);
|
||||||
enemyTransform.setPosition(x, y);
|
|
||||||
enemyTransform.speed = 80;
|
if (ai) {
|
||||||
}
|
aiComponents.push(ai);
|
||||||
if (enemyVelocity) {
|
// 设置随机AI个性
|
||||||
enemyVelocity.maxSpeed = 120;
|
ai.config.personality.aggression = Math.random();
|
||||||
enemyVelocity.friction = 0.95;
|
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}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建一些中性实体(只移动,无生命值)
|
// 3. 创建网络节点(300个)
|
||||||
for (let i = 0; i < 5; i++) {
|
console.log('创建网络节点...');
|
||||||
const neutral = this.createEntity(`Neutral_${i}`);
|
for (let i = 0; i < 300; i++) {
|
||||||
neutral.addComponent(new Transform());
|
const entityName = `Network_Node_${i}`;
|
||||||
neutral.addComponent(new Velocity());
|
const x = (Math.random() - 0.5) * 1500;
|
||||||
neutral.addComponent(new Renderer("neutral", new Color(255, 255, 0, 255)));
|
const y = (Math.random() - 0.5) * 1500;
|
||||||
|
const color = new Color(0, 150, 255, 200);
|
||||||
|
|
||||||
const neutralTransform = neutral.getComponent(Transform);
|
const entity = this.createComplexEntity(entityName, "network_node", color, x, y, false, true, true);
|
||||||
const neutralVelocity = neutral.getComponent(Velocity);
|
|
||||||
if (neutralTransform) {
|
if (entity) {
|
||||||
const x = (Math.random() - 0.5) * 600;
|
const network = entity.getComponent(NetworkComponent);
|
||||||
const y = (Math.random() - 0.5) * 400;
|
const node = entity.getComponent(NodeComponent);
|
||||||
neutralTransform.setPosition(x, y);
|
|
||||||
neutralTransform.speed = 60;
|
if (network) {
|
||||||
}
|
networkComponents.push(network);
|
||||||
if (neutralVelocity) {
|
network.connectionState = 'connected';
|
||||||
neutralVelocity.maxSpeed = 80;
|
network.config.syncFrequency = 30 + Math.random() * 30; // 30-60Hz
|
||||||
neutralVelocity.friction = 0.99;
|
}
|
||||||
|
|
||||||
|
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('🔗 复杂关系建立完成!');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "449fa887-eece-424d-ae1d-7082454fac3f",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -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'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "09cb67c9-12ef-48e0-949d-c8edf2c7ae22",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -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<number, NodeComponent>();
|
||||||
|
|
||||||
|
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'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "b4593488-685b-4e52-800b-b2a2990305d6",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -2,3 +2,6 @@
|
|||||||
export { MovementSystem } from './MovementSystem';
|
export { MovementSystem } from './MovementSystem';
|
||||||
export { HealthSystem } from './HealthSystem';
|
export { HealthSystem } from './HealthSystem';
|
||||||
export { RandomMovementSystem } from './RandomMovementSystem';
|
export { RandomMovementSystem } from './RandomMovementSystem';
|
||||||
|
export { AISystem } from './AISystem';
|
||||||
|
export { NetworkSystem } from './NetworkSystem';
|
||||||
|
export { NodeRenderSystem } from './NodeRenderSystem';
|
||||||
Submodule extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension updated: 60e25ffa1c...e222e9e9d8
Submodule extensions/cocos/cocos-ecs/extensions/cocos-terrain-gen updated: 691005f1f1...8ebcce722a
9
extensions/cocos/cocos-ecs/package-lock.json
generated
9
extensions/cocos/cocos-ecs/package-lock.json
generated
@@ -7,7 +7,7 @@
|
|||||||
"name": "cocos-ecs",
|
"name": "cocos-ecs",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esengine/ai": "^2.0.15",
|
"@esengine/ai": "^2.0.15",
|
||||||
"@esengine/ecs-framework": "^2.1.22"
|
"@esengine/ecs-framework": "^2.1.23"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esengine/ai": {
|
"node_modules/@esengine/ai": {
|
||||||
@@ -22,9 +22,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esengine/ecs-framework": {
|
"node_modules/@esengine/ecs-framework": {
|
||||||
"version": "2.1.22",
|
"version": "2.1.23",
|
||||||
"resolved": "https://registry.npmjs.org/@esengine/ecs-framework/-/ecs-framework-2.1.22.tgz",
|
"resolved": "https://registry.npmjs.org/@esengine/ecs-framework/-/ecs-framework-2.1.23.tgz",
|
||||||
"integrity": "sha512-U5zQJJC7eQeiIUMV1Y784rOJoVK9x8ZLDI65+8Bbt3PcBWzn+qspLe4FBlJU4XBZcMbmEo5vJltvRNkN7iVPZA==",
|
"integrity": "sha512-Em4w+AQ2c/eXN6FqTwySwu869yPjG9kHZp74vEDVBLb7uQOw+g8aMrSALS4tYHTj0PxNofcNplS38xjGsFTekA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"version": "3.8.6"
|
"version": "3.8.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esengine/ecs-framework": "^2.1.22",
|
"@esengine/ai": "^2.0.15",
|
||||||
"@esengine/ai": "^2.0.15"
|
"@esengine/ecs-framework": "^2.1.23"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,6 +136,22 @@ export class DebugManager {
|
|||||||
}
|
}
|
||||||
break;
|
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':
|
case 'ping':
|
||||||
this.webSocketManager.send({
|
this.webSocketManager.send({
|
||||||
type: 'pong',
|
type: 'pong',
|
||||||
@@ -149,9 +165,139 @@ export class DebugManager {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error('[ECS Debug] 处理消息失败:', 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 {
|
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();
|
const timestamp = Date.now();
|
||||||
|
|
||||||
// 1. 收集基础内存信息
|
// 使用专门的内存计算方法收集实体数据
|
||||||
|
const entityData = this.entityCollector.collectEntityDataWithMemory();
|
||||||
|
|
||||||
|
// 收集其他内存统计
|
||||||
const baseMemoryInfo = this.collectBaseMemoryInfo();
|
const baseMemoryInfo = this.collectBaseMemoryInfo();
|
||||||
|
const componentMemoryStats = this.collectComponentMemoryStats((Core.scene as any)?.entities);
|
||||||
// 2. 收集实体内存统计
|
|
||||||
const entityMemoryStats = this.collectEntityMemoryStats(entityList);
|
|
||||||
|
|
||||||
// 3. 收集组件内存统计
|
|
||||||
const componentMemoryStats = this.collectComponentMemoryStats(entityList);
|
|
||||||
|
|
||||||
// 4. 收集系统内存统计
|
|
||||||
const systemMemoryStats = this.collectSystemMemoryStats();
|
const systemMemoryStats = this.collectSystemMemoryStats();
|
||||||
|
|
||||||
// 5. 收集对象池内存统计
|
|
||||||
const poolMemoryStats = this.collectPoolMemoryStats();
|
const poolMemoryStats = this.collectPoolMemoryStats();
|
||||||
|
|
||||||
// 6. 收集性能监控器统计
|
|
||||||
const performanceStats = this.collectPerformanceStats();
|
const performanceStats = this.collectPerformanceStats();
|
||||||
|
|
||||||
|
// 计算总内存使用量
|
||||||
|
const totalEntityMemory = entityData.entitiesPerArchetype.reduce((sum, arch) => sum + arch.memory, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
timestamp,
|
timestamp,
|
||||||
version: '2.0',
|
version: '2.0',
|
||||||
summary: {
|
summary: {
|
||||||
totalEntities: entityList.buffer.length,
|
totalEntities: entityData.totalEntities,
|
||||||
totalMemoryUsage: baseMemoryInfo.usedMemory,
|
totalMemoryUsage: baseMemoryInfo.usedMemory,
|
||||||
totalMemoryLimit: baseMemoryInfo.totalMemory,
|
totalMemoryLimit: baseMemoryInfo.totalMemory,
|
||||||
memoryUtilization: (baseMemoryInfo.usedMemory / baseMemoryInfo.totalMemory * 100),
|
memoryUtilization: (baseMemoryInfo.usedMemory / baseMemoryInfo.totalMemory * 100),
|
||||||
gcCollections: baseMemoryInfo.gcCollections,
|
gcCollections: baseMemoryInfo.gcCollections,
|
||||||
entityMemory: entityMemoryStats.totalMemory,
|
entityMemory: totalEntityMemory,
|
||||||
componentMemory: componentMemoryStats.totalMemory,
|
componentMemory: componentMemoryStats.totalMemory,
|
||||||
systemMemory: systemMemoryStats.totalMemory,
|
systemMemory: systemMemoryStats.totalMemory,
|
||||||
poolMemory: poolMemoryStats.totalMemory
|
poolMemory: poolMemoryStats.totalMemory
|
||||||
},
|
},
|
||||||
baseMemory: baseMemoryInfo,
|
baseMemory: baseMemoryInfo,
|
||||||
entities: entityMemoryStats,
|
entities: {
|
||||||
|
totalMemory: totalEntityMemory,
|
||||||
|
entityCount: entityData.totalEntities,
|
||||||
|
archetypes: entityData.entitiesPerArchetype,
|
||||||
|
largestEntities: entityData.topEntitiesByComponents
|
||||||
|
},
|
||||||
components: componentMemoryStats,
|
components: componentMemoryStats,
|
||||||
systems: systemMemoryStats,
|
systems: systemMemoryStats,
|
||||||
pools: poolMemoryStats,
|
pools: poolMemoryStats,
|
||||||
performance: performanceStats,
|
performance: performanceStats
|
||||||
// 保持向后兼容
|
|
||||||
totalEntities: entityList.buffer.length,
|
|
||||||
totalMemory: entityMemoryStats.totalMemory,
|
|
||||||
detailedArchetypes: entityMemoryStats.archetypes
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,87 +407,7 @@ export class DebugManager {
|
|||||||
return memoryInfo;
|
return memoryInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 收集实体内存统计
|
|
||||||
*/
|
|
||||||
private collectEntityMemoryStats(entityList: any): any {
|
|
||||||
const archetypeStats = new Map<string, { count: number; memory: number; entities: any[] }>();
|
|
||||||
const entitySizeDistribution = new Map<string, number>(); // 按大小范围分布
|
|
||||||
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)
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收集组件内存统计
|
* 收集组件内存统计
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
2
thirdparty/BehaviourTree-ai
vendored
2
thirdparty/BehaviourTree-ai
vendored
Submodule thirdparty/BehaviourTree-ai updated: f3e91b9f34...7df0745b80
Reference in New Issue
Block a user