import { NodeType, BlackboardValueType } from '../Types/TaskStatus'; /** * 行为树资产元数据 */ export interface AssetMetadata { name: string; description?: string; version: string; createdAt?: string; modifiedAt?: string; } /** * 黑板变量定义 */ export interface BlackboardVariableDefinition { name: string; type: BlackboardValueType; defaultValue: any; readonly?: boolean; description?: string; } /** * 行为树节点数据(运行时格式) */ export interface BehaviorTreeNodeData { id: string; name: string; nodeType: NodeType; // 节点类型特定数据 data: Record; // 子节点ID列表 children: string[]; } /** * 属性绑定定义 */ export interface PropertyBinding { nodeId: string; propertyName: string; variableName: string; } /** * 行为树资产(运行时格式) * * 这是用于游戏运行时的优化格式,不包含编辑器UI信息 */ export interface BehaviorTreeAsset { /** * 资产格式版本 */ version: string; /** * 元数据 */ metadata: AssetMetadata; /** * 根节点ID */ rootNodeId: string; /** * 所有节点数据(扁平化存储,通过children建立层级) */ nodes: BehaviorTreeNodeData[]; /** * 黑板变量定义 */ blackboard: BlackboardVariableDefinition[]; /** * 属性绑定 */ propertyBindings?: PropertyBinding[]; } /** * 资产验证结果 */ export interface AssetValidationResult { valid: boolean; errors?: string[]; warnings?: string[]; } /** * 资产验证器 */ export class BehaviorTreeAssetValidator { /** * 验证资产数据的完整性和正确性 */ static validate(asset: BehaviorTreeAsset): AssetValidationResult { const errors: string[] = []; const warnings: string[] = []; // 检查版本 if (!asset.version) { errors.push('Missing version field'); } // 检查元数据 if (!asset.metadata || !asset.metadata.name) { errors.push('Missing or invalid metadata'); } // 检查根节点 if (!asset.rootNodeId) { errors.push('Missing rootNodeId'); } // 检查节点列表 if (!asset.nodes || !Array.isArray(asset.nodes)) { errors.push('Missing or invalid nodes array'); } else { const nodeIds = new Set(); const rootNode = asset.nodes.find(n => n.id === asset.rootNodeId); if (!rootNode) { errors.push(`Root node '${asset.rootNodeId}' not found in nodes array`); } // 检查节点ID唯一性 for (const node of asset.nodes) { if (!node.id) { errors.push('Node missing id field'); continue; } if (nodeIds.has(node.id)) { errors.push(`Duplicate node id: ${node.id}`); } nodeIds.add(node.id); // 检查节点类型 if (!node.nodeType) { errors.push(`Node ${node.id} missing nodeType`); } // 检查子节点引用 if (node.children) { for (const childId of node.children) { if (!asset.nodes.find(n => n.id === childId)) { errors.push(`Node ${node.id} references non-existent child: ${childId}`); } } } } // 检查是否有孤立节点 const referencedNodes = new Set([asset.rootNodeId]); const collectReferencedNodes = (nodeId: string) => { const node = asset.nodes.find(n => n.id === nodeId); if (node && node.children) { for (const childId of node.children) { referencedNodes.add(childId); collectReferencedNodes(childId); } } }; collectReferencedNodes(asset.rootNodeId); for (const node of asset.nodes) { if (!referencedNodes.has(node.id)) { warnings.push(`Orphaned node detected: ${node.id} (${node.name})`); } } } // 检查黑板定义 if (asset.blackboard && Array.isArray(asset.blackboard)) { const varNames = new Set(); for (const variable of asset.blackboard) { if (!variable.name) { errors.push('Blackboard variable missing name'); continue; } if (varNames.has(variable.name)) { errors.push(`Duplicate blackboard variable: ${variable.name}`); } varNames.add(variable.name); if (!variable.type) { errors.push(`Blackboard variable ${variable.name} missing type`); } } } // 检查属性绑定 if (asset.propertyBindings && Array.isArray(asset.propertyBindings)) { const nodeIds = new Set(asset.nodes.map(n => n.id)); const varNames = new Set(asset.blackboard?.map(v => v.name) || []); for (const binding of asset.propertyBindings) { if (!nodeIds.has(binding.nodeId)) { errors.push(`Property binding references non-existent node: ${binding.nodeId}`); } if (!varNames.has(binding.variableName)) { errors.push(`Property binding references non-existent variable: ${binding.variableName}`); } if (!binding.propertyName) { errors.push('Property binding missing propertyName'); } } } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined, warnings: warnings.length > 0 ? warnings : undefined }; } /** * 获取资产统计信息 */ static getStats(asset: BehaviorTreeAsset): { nodeCount: number; actionCount: number; conditionCount: number; compositeCount: number; decoratorCount: number; blackboardVariableCount: number; propertyBindingCount: number; maxDepth: number; } { let actionCount = 0; let conditionCount = 0; let compositeCount = 0; let decoratorCount = 0; for (const node of asset.nodes) { switch (node.nodeType) { case NodeType.Action: actionCount++; break; case NodeType.Condition: conditionCount++; break; case NodeType.Composite: compositeCount++; break; case NodeType.Decorator: decoratorCount++; break; } } // 计算最大深度 const getDepth = (nodeId: string, currentDepth: number = 0): number => { const node = asset.nodes.find(n => n.id === nodeId); if (!node || !node.children || node.children.length === 0) { return currentDepth; } let maxChildDepth = currentDepth; for (const childId of node.children) { const childDepth = getDepth(childId, currentDepth + 1); maxChildDepth = Math.max(maxChildDepth, childDepth); } return maxChildDepth; }; return { nodeCount: asset.nodes.length, actionCount, conditionCount, compositeCount, decoratorCount, blackboardVariableCount: asset.blackboard?.length || 0, propertyBindingCount: asset.propertyBindings?.length || 0, maxDepth: getDepth(asset.rootNodeId) }; } }