优化性能结构/延迟加载
新增测试代码用于测试性能
This commit is contained in:
@@ -42,11 +42,12 @@ export class ECSManager extends Component {
|
||||
// 1. 创建Core实例,启用调试功能
|
||||
if (this.debugMode) {
|
||||
Core.create({
|
||||
debug: true,
|
||||
enableEntitySystems: true,
|
||||
debugConfig: {
|
||||
enabled: true,
|
||||
websocketUrl: 'ws://localhost:8080',
|
||||
autoReconnect: true,
|
||||
updateInterval: 100,
|
||||
debugFrameRate: 30,
|
||||
channels: {
|
||||
entities: true,
|
||||
@@ -57,9 +58,13 @@ export class ECSManager extends Component {
|
||||
}
|
||||
}
|
||||
});
|
||||
// ECS调试模式已启用
|
||||
console.log('✅ ECS调试模式已启用');
|
||||
} else {
|
||||
Core.create(false);
|
||||
Core.create({
|
||||
debug: false,
|
||||
enableEntitySystems: true
|
||||
});
|
||||
console.log('ℹ️ ECS调试模式已禁用');
|
||||
}
|
||||
|
||||
// 2. 创建游戏场景
|
||||
|
||||
@@ -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": {}
|
||||
}
|
||||
@@ -2,4 +2,7 @@
|
||||
export { Transform } from './Transform';
|
||||
export { Health } from './Health';
|
||||
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 { Color } from 'cc';
|
||||
import { MovementSystem, HealthSystem, RandomMovementSystem } from '../systems';
|
||||
import { Transform, Health, Velocity, Renderer } from '../components';
|
||||
import { Color, Node } from 'cc';
|
||||
import { MovementSystem, HealthSystem, RandomMovementSystem, AISystem, NetworkSystem, NodeRenderSystem } from '../systems';
|
||||
import { Transform, Health, Velocity, Renderer, NodeComponent, AIComponent, NetworkComponent } from '../components';
|
||||
|
||||
/**
|
||||
* 游戏场景
|
||||
@@ -29,77 +29,268 @@ export class GameScene extends Scene {
|
||||
this.addEntityProcessor(new MovementSystem());
|
||||
this.addEntityProcessor(new HealthSystem());
|
||||
this.addEntityProcessor(new RandomMovementSystem());
|
||||
// this.addEntityProcessor(new AISystem());
|
||||
// this.addEntityProcessor(new NetworkSystem());
|
||||
// this.addEntityProcessor(new NodeRenderSystem());
|
||||
|
||||
// 创建测试实体
|
||||
this.createTestEntities();
|
||||
// 创建大量复杂的测试实体
|
||||
this.createComplexTestEntities();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建测试实体
|
||||
* 创建复杂的测试实体(1000+个)
|
||||
*/
|
||||
private createTestEntities(): void {
|
||||
console.log('🚀 开始创建测试实体...');
|
||||
private createComplexTestEntities(): void {
|
||||
console.log('🚀 开始创建大量复杂测试实体...');
|
||||
|
||||
// 创建玩家实体
|
||||
const player = this.createEntity("Player");
|
||||
player.addComponent(new Transform());
|
||||
player.addComponent(new Health(150));
|
||||
player.addComponent(new Velocity());
|
||||
player.addComponent(new Renderer("player", new Color(0, 255, 0, 255)));
|
||||
// 存储创建的AI和网络组件用于建立循环引用
|
||||
const aiComponents: AIComponent[] = [];
|
||||
const networkComponents: NetworkComponent[] = [];
|
||||
const nodeComponents: NodeComponent[] = [];
|
||||
|
||||
const playerTransform = player.getComponent(Transform);
|
||||
const playerHealth = player.getComponent(Health);
|
||||
if (playerTransform) {
|
||||
playerTransform.setPosition(0, 0);
|
||||
playerTransform.speed = 120;
|
||||
}
|
||||
if (playerHealth) {
|
||||
playerHealth.regenRate = 5; // 每秒回复5点生命值
|
||||
}
|
||||
|
||||
// 创建一些敌人实体
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const enemy = this.createEntity(`Enemy_${i}`);
|
||||
enemy.addComponent(new Transform());
|
||||
enemy.addComponent(new Health(80));
|
||||
enemy.addComponent(new Velocity());
|
||||
enemy.addComponent(new Renderer("enemy", new Color(255, 0, 0, 255)));
|
||||
// 1. 创建玩家实体(具有所有组件类型)
|
||||
console.log('创建玩家实体...');
|
||||
const player = this.createComplexEntity("Player", "player", new Color(0, 255, 0, 255), 0, 0, true, true, true);
|
||||
if (player) {
|
||||
const playerAI = player.getComponent(AIComponent);
|
||||
const playerNetwork = player.getComponent(NetworkComponent);
|
||||
const playerNode = player.getComponent(NodeComponent);
|
||||
|
||||
const enemyTransform = enemy.getComponent(Transform);
|
||||
const enemyVelocity = enemy.getComponent(Velocity);
|
||||
if (enemyTransform) {
|
||||
// 随机位置
|
||||
const x = (Math.random() - 0.5) * 800;
|
||||
const y = (Math.random() - 0.5) * 600;
|
||||
enemyTransform.setPosition(x, y);
|
||||
enemyTransform.speed = 80;
|
||||
}
|
||||
if (enemyVelocity) {
|
||||
enemyVelocity.maxSpeed = 120;
|
||||
enemyVelocity.friction = 0.95;
|
||||
if (playerAI) aiComponents.push(playerAI);
|
||||
if (playerNetwork) networkComponents.push(playerNetwork);
|
||||
if (playerNode) nodeComponents.push(playerNode);
|
||||
}
|
||||
|
||||
// 2. 创建AI智能体(500个)
|
||||
console.log('创建AI智能体...');
|
||||
for (let i = 0; i < 500; i++) {
|
||||
const entityName = `AI_Agent_${i}`;
|
||||
const x = (Math.random() - 0.5) * 2000;
|
||||
const y = (Math.random() - 0.5) * 2000;
|
||||
const color = new Color(
|
||||
Math.floor(Math.random() * 255),
|
||||
Math.floor(Math.random() * 255),
|
||||
Math.floor(Math.random() * 255),
|
||||
255
|
||||
);
|
||||
|
||||
const entity = this.createComplexEntity(entityName, "ai_agent", color, x, y, true, true, Math.random() > 0.5);
|
||||
|
||||
if (entity) {
|
||||
const ai = entity.getComponent(AIComponent);
|
||||
const network = entity.getComponent(NetworkComponent);
|
||||
const node = entity.getComponent(NodeComponent);
|
||||
|
||||
if (ai) {
|
||||
aiComponents.push(ai);
|
||||
// 设置随机AI个性
|
||||
ai.config.personality.aggression = Math.random();
|
||||
ai.config.personality.curiosity = Math.random();
|
||||
ai.config.personality.loyalty = Math.random();
|
||||
ai.config.personality.intelligence = Math.random();
|
||||
}
|
||||
|
||||
if (network) {
|
||||
networkComponents.push(network);
|
||||
// 随机设置网络状态
|
||||
if (Math.random() > 0.2) {
|
||||
network.connectionState = 'connected';
|
||||
}
|
||||
}
|
||||
|
||||
if (node) {
|
||||
nodeComponents.push(node);
|
||||
// 设置随机节点属性
|
||||
node.nodeConfig.layer = Math.floor(Math.random() * 10);
|
||||
node.nodeConfig.tag = `layer_${node.nodeConfig.layer}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建一些中性实体(只移动,无生命值)
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const neutral = this.createEntity(`Neutral_${i}`);
|
||||
neutral.addComponent(new Transform());
|
||||
neutral.addComponent(new Velocity());
|
||||
neutral.addComponent(new Renderer("neutral", new Color(255, 255, 0, 255)));
|
||||
// 3. 创建网络节点(300个)
|
||||
console.log('创建网络节点...');
|
||||
for (let i = 0; i < 300; i++) {
|
||||
const entityName = `Network_Node_${i}`;
|
||||
const x = (Math.random() - 0.5) * 1500;
|
||||
const y = (Math.random() - 0.5) * 1500;
|
||||
const color = new Color(0, 150, 255, 200);
|
||||
|
||||
const neutralTransform = neutral.getComponent(Transform);
|
||||
const neutralVelocity = neutral.getComponent(Velocity);
|
||||
if (neutralTransform) {
|
||||
const x = (Math.random() - 0.5) * 600;
|
||||
const y = (Math.random() - 0.5) * 400;
|
||||
neutralTransform.setPosition(x, y);
|
||||
neutralTransform.speed = 60;
|
||||
}
|
||||
if (neutralVelocity) {
|
||||
neutralVelocity.maxSpeed = 80;
|
||||
neutralVelocity.friction = 0.99;
|
||||
const entity = this.createComplexEntity(entityName, "network_node", color, x, y, false, true, true);
|
||||
|
||||
if (entity) {
|
||||
const network = entity.getComponent(NetworkComponent);
|
||||
const node = entity.getComponent(NodeComponent);
|
||||
|
||||
if (network) {
|
||||
networkComponents.push(network);
|
||||
network.connectionState = 'connected';
|
||||
network.config.syncFrequency = 30 + Math.random() * 30; // 30-60Hz
|
||||
}
|
||||
|
||||
if (node) {
|
||||
nodeComponents.push(node);
|
||||
// 创建复杂的层次结构
|
||||
node.nodeConfig.layer = Math.floor(i / 10); // 每10个一层
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 创建简单移动实体(200个)
|
||||
console.log('创建简单移动实体...');
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const entityName = `Simple_Mover_${i}`;
|
||||
const x = (Math.random() - 0.5) * 1000;
|
||||
const y = (Math.random() - 0.5) * 1000;
|
||||
const color = new Color(255, 255, 255, 150);
|
||||
|
||||
this.createComplexEntity(entityName, "simple_mover", color, x, y, false, false, false);
|
||||
}
|
||||
|
||||
// 5. 建立循环引用和复杂关系
|
||||
console.log('建立实体间的复杂关系...');
|
||||
this.establishComplexRelationships(aiComponents, networkComponents, nodeComponents);
|
||||
|
||||
const totalEntities = this.entities.count;
|
||||
console.log(`✅ 创建完成!总共创建了 ${totalEntities} 个实体`);
|
||||
console.log(` - AI组件: ${aiComponents.length} 个`);
|
||||
console.log(` - 网络组件: ${networkComponents.length} 个`);
|
||||
console.log(` - 节点组件: ${nodeComponents.length} 个`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建复杂实体的辅助方法
|
||||
*/
|
||||
private createComplexEntity(
|
||||
name: string,
|
||||
type: string,
|
||||
color: Color,
|
||||
x: number,
|
||||
y: number,
|
||||
hasAI: boolean,
|
||||
hasNetwork: boolean,
|
||||
hasNode: boolean
|
||||
): any {
|
||||
const entity = this.createEntity(name);
|
||||
|
||||
// 基础组件
|
||||
const transform = new Transform();
|
||||
transform.setPosition(x, y);
|
||||
transform.speed = 50 + Math.random() * 100;
|
||||
entity.addComponent(transform);
|
||||
|
||||
const health = new Health(50 + Math.random() * 100);
|
||||
health.regenRate = Math.random() * 10;
|
||||
entity.addComponent(health);
|
||||
|
||||
const velocity = new Velocity();
|
||||
velocity.maxSpeed = 80 + Math.random() * 120;
|
||||
velocity.friction = 0.95 + Math.random() * 0.04;
|
||||
entity.addComponent(velocity);
|
||||
|
||||
const renderer = new Renderer(type, color.clone());
|
||||
renderer.alpha = 0.5 + Math.random() * 0.5;
|
||||
renderer.layer = Math.floor(Math.random() * 5);
|
||||
entity.addComponent(renderer);
|
||||
|
||||
// 复杂组件
|
||||
if (hasAI) {
|
||||
const ai = new AIComponent();
|
||||
entity.addComponent(ai);
|
||||
}
|
||||
|
||||
if (hasNetwork) {
|
||||
const network = new NetworkComponent(`${type}_${name}`);
|
||||
entity.addComponent(network);
|
||||
}
|
||||
|
||||
if (hasNode) {
|
||||
const node = new NodeComponent(name);
|
||||
entity.addComponent(node);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 建立实体间的复杂关系
|
||||
*/
|
||||
private establishComplexRelationships(
|
||||
aiComponents: AIComponent[],
|
||||
networkComponents: NetworkComponent[],
|
||||
nodeComponents: NodeComponent[]
|
||||
): void {
|
||||
// 建立AI之间的盟友/敌人关系(避免循环引用)
|
||||
for (let i = 0; i < Math.min(aiComponents.length, 100); i++) {
|
||||
const ai = aiComponents[i];
|
||||
|
||||
// 随机添加盟友(使用实体ID)
|
||||
const allyCount = Math.floor(Math.random() * 5);
|
||||
for (let j = 0; j < allyCount; j++) {
|
||||
const randomIndex = Math.floor(Math.random() * aiComponents.length);
|
||||
const ally = aiComponents[randomIndex];
|
||||
if (ally !== ai && !ai.allyIds.includes(ally.entity.id)) {
|
||||
ai.addAlly(ally.entity.id);
|
||||
}
|
||||
}
|
||||
|
||||
// 随机设置目标(使用实体ID)
|
||||
if (Math.random() > 0.7) {
|
||||
const randomIndex = Math.floor(Math.random() * aiComponents.length);
|
||||
const target = aiComponents[randomIndex];
|
||||
if (target !== ai) {
|
||||
ai.setTarget(target.entity.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 建立网络连接(避免循环引用)
|
||||
for (let i = 0; i < Math.min(networkComponents.length, 50); i++) {
|
||||
const network = networkComponents[i];
|
||||
|
||||
// 连接到其他网络组件(使用网络ID)
|
||||
const connectionCount = Math.floor(Math.random() * 8);
|
||||
for (let j = 0; j < connectionCount; j++) {
|
||||
const randomIndex = Math.floor(Math.random() * networkComponents.length);
|
||||
const other = networkComponents[randomIndex];
|
||||
if (other !== network) {
|
||||
network.connectToPlayer(other.networkId);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建群组(使用网络ID)
|
||||
if (Math.random() > 0.8) {
|
||||
const groupSize = Math.floor(Math.random() * 10) + 2;
|
||||
const groupMemberIds: string[] = [network.networkId];
|
||||
|
||||
for (let k = 0; k < groupSize - 1; k++) {
|
||||
const randomIndex = Math.floor(Math.random() * networkComponents.length);
|
||||
const member = networkComponents[randomIndex];
|
||||
if (!groupMemberIds.includes(member.networkId)) {
|
||||
groupMemberIds.push(member.networkId);
|
||||
}
|
||||
}
|
||||
|
||||
network.joinGroup(groupMemberIds, network.networkId);
|
||||
}
|
||||
}
|
||||
|
||||
// 建立节点层次结构(避免循环引用)
|
||||
for (let i = 0; i < Math.min(nodeComponents.length, 30); i++) {
|
||||
const parent = nodeComponents[i];
|
||||
|
||||
// 添加一些子节点(使用实体ID)
|
||||
const childCount = Math.floor(Math.random() * 5);
|
||||
for (let j = 0; j < childCount; j++) {
|
||||
const childIndex = Math.floor(Math.random() * nodeComponents.length);
|
||||
const child = nodeComponents[childIndex];
|
||||
|
||||
if (child !== parent && !parent.nodeConfig.childIds.includes(child.entity.id)) {
|
||||
parent.addChild(child.entity.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('🔗 复杂关系建立完成!');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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": {}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
// 导出所有系统
|
||||
export { MovementSystem } from './MovementSystem';
|
||||
export { HealthSystem } from './HealthSystem';
|
||||
export { RandomMovementSystem } from './RandomMovementSystem';
|
||||
export { RandomMovementSystem } from './RandomMovementSystem';
|
||||
export { AISystem } from './AISystem';
|
||||
export { NetworkSystem } from './NetworkSystem';
|
||||
export { NodeRenderSystem } from './NodeRenderSystem';
|
||||
Reference in New Issue
Block a user