优化性能结构/延迟加载
新增测试代码用于测试性能
This commit is contained in:
@@ -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';
|
||||
Reference in New Issue
Block a user