import { ToolDefinition, ToolResponse, ToolExecutor, PrefabInfo } from '../types'; export class PrefabTools implements ToolExecutor { getTools(): ToolDefinition[] { return [ { name: 'get_prefab_list', description: 'Get all prefabs in the project', inputSchema: { type: 'object', properties: { folder: { type: 'string', description: 'Folder path to search (optional)', default: 'db://assets' } } } }, { name: 'load_prefab', description: 'Load a prefab by path', inputSchema: { type: 'object', properties: { prefabPath: { type: 'string', description: 'Prefab asset path' } }, required: ['prefabPath'] } }, { name: 'instantiate_prefab', description: 'Instantiate a prefab in the scene', inputSchema: { type: 'object', properties: { prefabPath: { type: 'string', description: 'Prefab asset path' }, parentUuid: { type: 'string', description: 'Parent node UUID (optional)' }, position: { type: 'object', description: 'Initial position', properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } } } }, required: ['prefabPath'] } }, { name: 'create_prefab', description: 'Create a prefab from a node', inputSchema: { type: 'object', properties: { nodeUuid: { type: 'string', description: 'Source node UUID' }, savePath: { type: 'string', description: 'Path to save the prefab' }, prefabName: { type: 'string', description: 'Prefab name' }, includeChildren: { type: 'boolean', description: 'Whether to include child nodes', default: true }, includeComponents: { type: 'boolean', description: 'Whether to include components', default: true } }, required: ['nodeUuid', 'savePath', 'prefabName'] } }, { name: 'create_prefab_from_node', description: 'Create a prefab from a node (alias for create_prefab)', inputSchema: { type: 'object', properties: { nodeUuid: { type: 'string', description: 'Source node UUID' }, prefabPath: { type: 'string', description: 'Path to save the prefab' } }, required: ['nodeUuid', 'prefabPath'] } }, { name: 'update_prefab', description: 'Update an existing prefab', inputSchema: { type: 'object', properties: { prefabPath: { type: 'string', description: 'Prefab asset path' }, nodeUuid: { type: 'string', description: 'Node UUID with changes' } }, required: ['prefabPath', 'nodeUuid'] } }, { name: 'revert_prefab', description: 'Revert prefab instance to original', inputSchema: { type: 'object', properties: { nodeUuid: { type: 'string', description: 'Prefab instance node UUID' } }, required: ['nodeUuid'] } }, { name: 'get_prefab_info', description: 'Get detailed prefab information', inputSchema: { type: 'object', properties: { prefabPath: { type: 'string', description: 'Prefab asset path' } }, required: ['prefabPath'] } }, { name: 'validate_prefab', description: 'Validate a prefab file format', inputSchema: { type: 'object', properties: { prefabPath: { type: 'string', description: 'Prefab asset path' } }, required: ['prefabPath'] } }, { name: 'duplicate_prefab', description: 'Duplicate an existing prefab', inputSchema: { type: 'object', properties: { sourcePrefabPath: { type: 'string', description: 'Source prefab path' }, targetPrefabPath: { type: 'string', description: 'Target prefab path' }, newPrefabName: { type: 'string', description: 'New prefab name' } }, required: ['sourcePrefabPath', 'targetPrefabPath'] } }, { name: 'restore_prefab_node', description: 'Restore prefab node using prefab asset (built-in undo record)', inputSchema: { type: 'object', properties: { nodeUuid: { type: 'string', description: 'Prefab instance node UUID' }, assetUuid: { type: 'string', description: 'Prefab asset UUID' } }, required: ['nodeUuid', 'assetUuid'] } } ]; } async execute(toolName: string, args: any): Promise { switch (toolName) { case 'get_prefab_list': return await this.getPrefabList(args.folder); case 'load_prefab': return await this.loadPrefab(args.prefabPath); case 'instantiate_prefab': return await this.instantiatePrefab(args); case 'create_prefab': return await this.createPrefab(args); case 'create_prefab_from_node': return await this.createPrefabFromNode(args); case 'update_prefab': return await this.updatePrefab(args.prefabPath, args.nodeUuid); case 'revert_prefab': return await this.revertPrefab(args.nodeUuid); case 'get_prefab_info': return await this.getPrefabInfo(args.prefabPath); case 'validate_prefab': return await this.validatePrefab(args.prefabPath); case 'duplicate_prefab': return await this.duplicatePrefab(args); case 'restore_prefab_node': return await this.restorePrefabNode(args.nodeUuid, args.assetUuid); default: throw new Error(`Unknown tool: ${toolName}`); } } private async getPrefabList(folder: string = 'db://assets'): Promise { return new Promise((resolve) => { const pattern = folder.endsWith('/') ? `${folder}**/*.prefab` : `${folder}/**/*.prefab`; Editor.Message.request('asset-db', 'query-assets', { pattern: pattern }).then((results: any[]) => { const prefabs: PrefabInfo[] = results.map(asset => ({ name: asset.name, path: asset.url, uuid: asset.uuid, folder: asset.url.substring(0, asset.url.lastIndexOf('/')) })); resolve({ success: true, data: prefabs }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async loadPrefab(prefabPath: string): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { if (!assetInfo) { throw new Error('Prefab not found'); } return Editor.Message.request('scene', 'load-asset', { uuid: assetInfo.uuid }); }).then((prefabData: any) => { resolve({ success: true, data: { uuid: prefabData.uuid, name: prefabData.name, message: 'Prefab loaded successfully' } }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async instantiatePrefab(args: any): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', args.prefabPath).then((assetInfo: any) => { if (!assetInfo) { throw new Error('预制体未找到'); } // 使用正确的 create-node API 从预制体资源实例化 const createNodeOptions: any = { assetUuid: assetInfo.uuid }; // 设置父节点 if (args.parentUuid) { createNodeOptions.parent = args.parentUuid; } // 设置节点名称 if (args.name) { createNodeOptions.name = args.name; } else if (assetInfo.name) { createNodeOptions.name = assetInfo.name; } // 设置初始属性(如位置) if (args.position) { createNodeOptions.dump = { position: { value: args.position } }; } return Editor.Message.request('scene', 'create-node', createNodeOptions); }).then((nodeUuid: string | string[]) => { // 获取实际的节点UUID const uuid = Array.isArray(nodeUuid) ? nodeUuid[0] : nodeUuid; resolve({ success: true, data: { nodeUuid: uuid, prefabPath: args.prefabPath, parentUuid: args.parentUuid, position: args.position, message: '预制体实例化成功' } }); }).catch((err: Error) => { resolve({ success: false, error: `预制体实例化失败: ${err.message}`, instruction: '请检查预制体路径是否正确,确保预制体文件格式正确' }); }); }); } private async tryCreateNodeWithPrefab(args: any): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', args.prefabPath).then((assetInfo: any) => { if (!assetInfo) { throw new Error('预制体未找到'); } // 方法2: 使用 create-node 指定预制体资源 const createNodeOptions: any = { assetUuid: assetInfo.uuid }; // 设置父节点 if (args.parentUuid) { createNodeOptions.parent = args.parentUuid; } return Editor.Message.request('scene', 'create-node', createNodeOptions); }).then((nodeUuid: string | string[]) => { const uuid = Array.isArray(nodeUuid) ? nodeUuid[0] : nodeUuid; // 如果指定了位置,设置节点位置 if (args.position && uuid) { Editor.Message.request('scene', 'set-property', { uuid: uuid, path: 'position', dump: { value: args.position } }).then(() => { resolve({ success: true, data: { nodeUuid: uuid, prefabPath: args.prefabPath, position: args.position, message: '预制体实例化成功(备用方法)并设置了位置' } }); }).catch(() => { resolve({ success: true, data: { nodeUuid: uuid, prefabPath: args.prefabPath, message: '预制体实例化成功(备用方法)但位置设置失败' } }); }); } else { resolve({ success: true, data: { nodeUuid: uuid, prefabPath: args.prefabPath, message: '预制体实例化成功(备用方法)' } }); } }).catch((err: Error) => { resolve({ success: false, error: `备用预制体实例化方法也失败: ${err.message}` }); }); }); } private async tryAlternativeInstantiateMethods(args: any): Promise { return new Promise(async (resolve) => { try { // 方法1: 尝试使用 create-node 然后设置预制体 const assetInfo = await this.getAssetInfo(args.prefabPath); if (!assetInfo) { resolve({ success: false, error: '无法获取预制体信息' }); return; } // 创建空节点 const createResult = await this.createNode(args.parentUuid, args.position); if (!createResult.success) { resolve(createResult); return; } // 尝试将预制体应用到节点 const applyResult = await this.applyPrefabToNode(createResult.data.nodeUuid, assetInfo.uuid); if (applyResult.success) { resolve({ success: true, data: { nodeUuid: createResult.data.nodeUuid, name: createResult.data.name, message: '预制体实例化成功(使用备选方法)' } }); } else { resolve({ success: false, error: '无法将预制体应用到节点', data: { nodeUuid: createResult.data.nodeUuid, message: '已创建节点,但无法应用预制体数据' } }); } } catch (error) { resolve({ success: false, error: `备选实例化方法失败: ${error}` }); } }); } private async getAssetInfo(prefabPath: string): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { resolve(assetInfo); }).catch(() => { resolve(null); }); }); } private async createNode(parentUuid?: string, position?: any): Promise { return new Promise((resolve) => { const createNodeOptions: any = { name: 'PrefabInstance' }; // 设置父节点 if (parentUuid) { createNodeOptions.parent = parentUuid; } // 设置位置 if (position) { createNodeOptions.dump = { position: position }; } Editor.Message.request('scene', 'create-node', createNodeOptions).then((nodeUuid: string | string[]) => { const uuid = Array.isArray(nodeUuid) ? nodeUuid[0] : nodeUuid; resolve({ success: true, data: { nodeUuid: uuid, name: 'PrefabInstance' } }); }).catch((error: any) => { resolve({ success: false, error: error.message || '创建节点失败' }); }); }); } private async applyPrefabToNode(nodeUuid: string, prefabUuid: string): Promise { return new Promise((resolve) => { // 尝试多种方法来应用预制体数据 const methods = [ () => Editor.Message.request('scene', 'apply-prefab', { node: nodeUuid, prefab: prefabUuid }), () => Editor.Message.request('scene', 'set-prefab', { node: nodeUuid, prefab: prefabUuid }), () => Editor.Message.request('scene', 'load-prefab-to-node', { node: nodeUuid, prefab: prefabUuid }) ]; const tryMethod = (index: number) => { if (index >= methods.length) { resolve({ success: false, error: '无法应用预制体数据' }); return; } methods[index]().then(() => { resolve({ success: true }); }).catch(() => { tryMethod(index + 1); }); }; tryMethod(0); }); } private async createPrefab(args: any): Promise { return new Promise(async (resolve) => { try { // 支持 prefabPath 和 savePath 两种参数名 const pathParam = args.prefabPath || args.savePath; if (!pathParam) { resolve({ success: false, error: '缺少预制体路径参数。请提供 prefabPath 或 savePath。' }); return; } const prefabName = args.prefabName || 'NewPrefab'; const fullPath = pathParam.endsWith('.prefab') ? pathParam : `${pathParam}/${prefabName}.prefab`; // 尝试使用Cocos Creator的原生预制体创建API const nativeResult = await this.createPrefabNative(args.nodeUuid, fullPath); if (nativeResult.success) { resolve(nativeResult); return; } // 如果原生API失败,使用自定义实现 const customResult = await this.createPrefabCustom(args.nodeUuid, fullPath, prefabName); resolve(customResult); } catch (error) { resolve({ success: false, error: `创建预制体时发生错误: ${error}` }); } }); } private async createPrefabNative(nodeUuid: string, prefabPath: string): Promise { return new Promise((resolve) => { // 根据官方API文档,不存在直接的预制体创建API // 预制体创建需要手动在编辑器中完成 resolve({ success: false, error: '原生预制体创建API不存在', instruction: '根据Cocos Creator官方API文档,预制体创建需要手动操作:\n1. 在场景中选择节点\n2. 将节点拖拽到资源管理器中\n3. 或右键节点选择"生成预制体"' }); }); } private async createPrefabCustom(nodeUuid: string, prefabPath: string, prefabName: string): Promise { return new Promise(async (resolve) => { try { // 1. 获取源节点的完整数据 const nodeData = await this.getNodeData(nodeUuid); if (!nodeData) { resolve({ success: false, error: `无法找到节点: ${nodeUuid}` }); return; } // 2. 生成预制体UUID const prefabUuid = this.generateUUID(); // 3. 创建预制体数据结构 const prefabData = this.createPrefabData(nodeData, prefabName, prefabUuid); // 4. 基于官方格式创建预制体数据结构 console.log('=== 开始创建预制体 ==='); console.log('节点名称:', nodeData.name?.value || '未知'); console.log('节点UUID:', nodeData.uuid?.value || '未知'); console.log('预制体保存路径:', prefabPath); console.log(`开始创建预制体,节点数据:`, nodeData); const prefabJsonData = await this.createStandardPrefabData(nodeData, prefabName, prefabUuid); // 5. 创建标准meta文件数据 const standardMetaData = this.createStandardMetaData(prefabName, prefabUuid); // 6. 保存预制体和meta文件 const saveResult = await this.savePrefabWithMeta(prefabPath, prefabJsonData, standardMetaData); if (saveResult.success) { resolve({ success: true, data: { prefabUuid: prefabUuid, prefabPath: prefabPath, nodeUuid: nodeUuid, prefabName: prefabName, message: '自定义预制体创建成功' } }); } else { resolve({ success: false, error: saveResult.error || '保存预制体文件失败' }); } } catch (error) { resolve({ success: false, error: `创建预制体时发生错误: ${error}` }); } }); } private async getNodeData(nodeUuid: string): Promise { return new Promise((resolve) => { Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeInfo: any) => { if (!nodeInfo) { resolve(null); return; } // 尝试获取节点的完整序列化数据 Editor.Message.request('scene', 'serialize-node', { node: nodeUuid, includeChildren: true }).then((serializedData: any) => { resolve(serializedData); }).catch(() => { // 如果序列化失败,尝试获取节点的详细信息 this.getNodeDetailedInfo(nodeUuid).then((detailedInfo: any) => { resolve(detailedInfo || nodeInfo); }).catch(() => { resolve(nodeInfo); }); }); }).catch(() => { resolve(null); }); }); } private async getNodeDetailedInfo(nodeUuid: string): Promise { return new Promise((resolve) => { // 获取节点的详细信息,包括组件和子节点 Editor.Message.request('scene', 'query-node-detail', nodeUuid).then((detailInfo: any) => { if (detailInfo) { resolve(detailInfo); } else { // 如果无法获取详细信息,构建基本节点信息 this.buildBasicNodeInfo(nodeUuid).then((basicInfo: any) => { resolve(basicInfo); }).catch(() => { resolve(null); }); } }).catch(() => { resolve(null); }); }); } private async buildBasicNodeInfo(nodeUuid: string): Promise { return new Promise((resolve) => { // 构建基本的节点信息 Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeInfo: any) => { if (!nodeInfo) { resolve(null); return; } // 简化版本:只返回基本节点信息,不获取子节点和组件 // 这些信息将在后续的预制体处理中根据需要添加 const basicInfo = { ...nodeInfo, children: [], components: [] }; resolve(basicInfo); }).catch(() => { resolve(null); }); }); } private generateUUID(): string { // 生成符合Cocos Creator格式的UUID const chars = '0123456789abcdef'; let uuid = ''; for (let i = 0; i < 32; i++) { if (i === 8 || i === 12 || i === 16 || i === 20) { uuid += '-'; } uuid += chars[Math.floor(Math.random() * chars.length)]; } return uuid; } private createPrefabData(nodeData: any, prefabName: string, prefabUuid: string): any[] { // 创建标准的预制体数据结构 const prefabAsset = { "__type__": "cc.Prefab", "_name": prefabName, "_objFlags": 0, "__editorExtras__": {}, "_native": "", "data": { "__id__": 1 }, "optimizationPolicy": 0, "persistent": false }; // 处理节点数据,确保符合预制体格式 const processedNodeData = this.processNodeForPrefab(nodeData, prefabUuid); return [prefabAsset, ...processedNodeData]; } private processNodeForPrefab(nodeData: any, prefabUuid: string): any[] { // 处理节点数据以符合预制体格式 const processedData: any[] = []; let idCounter = 1; // 递归处理节点和组件 const processNode = (node: any, parentId: number = 0): number => { const nodeId = idCounter++; // 创建节点对象 const processedNode = { "__type__": "cc.Node", "_name": node.name || "Node", "_objFlags": 0, "__editorExtras__": {}, "_parent": parentId > 0 ? { "__id__": parentId } : null, "_children": node.children ? node.children.map(() => ({ "__id__": idCounter++ })) : [], "_active": node.active !== false, "_components": node.components ? node.components.map(() => ({ "__id__": idCounter++ })) : [], "_prefab": { "__id__": idCounter++ }, "_lpos": { "__type__": "cc.Vec3", "x": node.position?.x || 0, "y": node.position?.y || 0, "z": node.position?.z || 0 }, "_lrot": { "__type__": "cc.Quat", "x": 0, "y": 0, "z": 0, "w": 1 }, "_lscale": { "__type__": "cc.Vec3", "x": node.scale?.x || 1, "y": node.scale?.y || 1, "z": node.scale?.z || 1 }, "_mobility": 0, "_layer": 1073741824, "_euler": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 }, "_id": "" }; processedData.push(processedNode); // 处理组件 if (node.components) { node.components.forEach((component: any) => { const componentId = idCounter++; const processedComponents = this.processComponentForPrefab(component, componentId); processedData.push(...processedComponents); }); } // 处理子节点 if (node.children) { node.children.forEach((child: any) => { processNode(child, nodeId); }); } return nodeId; }; processNode(nodeData); return processedData; } private processComponentForPrefab(component: any, componentId: number): any[] { // 处理组件数据以符合预制体格式 const processedComponent = { "__type__": component.type || "cc.Component", "_name": "", "_objFlags": 0, "__editorExtras__": {}, "node": { "__id__": componentId - 1 }, "_enabled": component.enabled !== false, "__prefab": { "__id__": componentId + 1 }, ...component.properties }; // 添加组件特定的预制体信息 const compPrefabInfo = { "__type__": "cc.CompPrefabInfo", "fileId": this.generateFileId() }; return [processedComponent, compPrefabInfo]; } private generateFileId(): string { // 生成文件ID(简化版本) const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/'; let fileId = ''; for (let i = 0; i < 22; i++) { fileId += chars[Math.floor(Math.random() * chars.length)]; } return fileId; } private createMetaData(prefabName: string, prefabUuid: string): any { return { "ver": "1.1.50", "importer": "prefab", "imported": true, "uuid": prefabUuid, "files": [ ".json" ], "subMetas": {}, "userData": { "syncNodeName": prefabName } }; } private async savePrefabFiles(prefabPath: string, prefabData: any[], metaData: any): Promise<{ success: boolean; error?: string }> { return new Promise((resolve) => { try { // 使用Editor API保存预制体文件 const prefabContent = JSON.stringify(prefabData, null, 2); const metaContent = JSON.stringify(metaData, null, 2); // 尝试使用更可靠的保存方法 this.saveAssetFile(prefabPath, prefabContent).then(() => { // 再创建meta文件 const metaPath = `${prefabPath}.meta`; return this.saveAssetFile(metaPath, metaContent); }).then(() => { resolve({ success: true }); }).catch((error: any) => { resolve({ success: false, error: error.message || '保存预制体文件失败' }); }); } catch (error) { resolve({ success: false, error: `保存文件时发生错误: ${error}` }); } }); } private async saveAssetFile(filePath: string, content: string): Promise { return new Promise((resolve, reject) => { // 尝试多种保存方法 const saveMethods = [ () => Editor.Message.request('asset-db', 'create-asset', filePath, content), () => Editor.Message.request('asset-db', 'save-asset', filePath, content), () => Editor.Message.request('asset-db', 'write-asset', filePath, content) ]; const trySave = (index: number) => { if (index >= saveMethods.length) { reject(new Error('所有保存方法都失败了')); return; } saveMethods[index]().then(() => { resolve(); }).catch(() => { trySave(index + 1); }); }; trySave(0); }); } private async updatePrefab(prefabPath: string, nodeUuid: string): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { if (!assetInfo) { throw new Error('Prefab not found'); } return Editor.Message.request('scene', 'apply-prefab', { node: nodeUuid, prefab: assetInfo.uuid }); }).then(() => { resolve({ success: true, message: 'Prefab updated successfully' }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async revertPrefab(nodeUuid: string): Promise { return new Promise((resolve) => { Editor.Message.request('scene', 'revert-prefab', { node: nodeUuid }).then(() => { resolve({ success: true, message: 'Prefab instance reverted successfully' }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async getPrefabInfo(prefabPath: string): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { if (!assetInfo) { throw new Error('Prefab not found'); } return Editor.Message.request('asset-db', 'query-asset-meta', assetInfo.uuid); }).then((metaInfo: any) => { const info: PrefabInfo = { name: metaInfo.name, uuid: metaInfo.uuid, path: prefabPath, folder: prefabPath.substring(0, prefabPath.lastIndexOf('/')), createTime: metaInfo.createTime, modifyTime: metaInfo.modifyTime, dependencies: metaInfo.depends || [] }; resolve({ success: true, data: info }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async createPrefabFromNode(args: any): Promise { // 从 prefabPath 提取名称 const prefabPath = args.prefabPath; const prefabName = prefabPath.split('/').pop()?.replace('.prefab', '') || 'NewPrefab'; // 调用原来的 createPrefab 方法 return await this.createPrefab({ nodeUuid: args.nodeUuid, savePath: prefabPath, prefabName: prefabName }); } private async validatePrefab(prefabPath: string): Promise { return new Promise((resolve) => { try { // 读取预制体文件内容 Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { if (!assetInfo) { resolve({ success: false, error: '预制体文件不存在' }); return; } // 验证预制体格式 Editor.Message.request('asset-db', 'read-asset', prefabPath).then((content: string) => { try { const prefabData = JSON.parse(content); const validationResult = this.validatePrefabFormat(prefabData); resolve({ success: true, data: { isValid: validationResult.isValid, issues: validationResult.issues, nodeCount: validationResult.nodeCount, componentCount: validationResult.componentCount, message: validationResult.isValid ? '预制体格式有效' : '预制体格式存在问题' } }); } catch (parseError) { resolve({ success: false, error: '预制体文件格式错误,无法解析JSON' }); } }).catch((error: any) => { resolve({ success: false, error: `读取预制体文件失败: ${error.message}` }); }); }).catch((error: any) => { resolve({ success: false, error: `查询预制体信息失败: ${error.message}` }); }); } catch (error) { resolve({ success: false, error: `验证预制体时发生错误: ${error}` }); } }); } private validatePrefabFormat(prefabData: any): { isValid: boolean; issues: string[]; nodeCount: number; componentCount: number } { const issues: string[] = []; let nodeCount = 0; let componentCount = 0; // 检查基本结构 if (!Array.isArray(prefabData)) { issues.push('预制体数据必须是数组格式'); return { isValid: false, issues, nodeCount, componentCount }; } if (prefabData.length === 0) { issues.push('预制体数据为空'); return { isValid: false, issues, nodeCount, componentCount }; } // 检查第一个元素是否为预制体资产 const firstElement = prefabData[0]; if (!firstElement || firstElement.__type__ !== 'cc.Prefab') { issues.push('第一个元素必须是cc.Prefab类型'); } // 统计节点和组件 prefabData.forEach((item: any, index: number) => { if (item.__type__ === 'cc.Node') { nodeCount++; } else if (item.__type__ && item.__type__.includes('cc.')) { componentCount++; } }); // 检查必要的字段 if (nodeCount === 0) { issues.push('预制体必须包含至少一个节点'); } return { isValid: issues.length === 0, issues, nodeCount, componentCount }; } private async duplicatePrefab(args: any): Promise { return new Promise(async (resolve) => { try { const { sourcePrefabPath, targetPrefabPath, newPrefabName } = args; // 读取源预制体 const sourceInfo = await this.getPrefabInfo(sourcePrefabPath); if (!sourceInfo.success) { resolve({ success: false, error: `无法读取源预制体: ${sourceInfo.error}` }); return; } // 读取源预制体内容 const sourceContent = await this.readPrefabContent(sourcePrefabPath); if (!sourceContent.success) { resolve({ success: false, error: `无法读取源预制体内容: ${sourceContent.error}` }); return; } // 生成新的UUID const newUuid = this.generateUUID(); // 修改预制体数据 const modifiedData = this.modifyPrefabForDuplication(sourceContent.data, newPrefabName, newUuid); // 创建新的meta数据 const newMetaData = this.createMetaData(newPrefabName || 'DuplicatedPrefab', newUuid); // 预制体复制功能暂时禁用,因为涉及复杂的序列化格式 resolve({ success: false, error: '预制体复制功能暂时不可用', instruction: '请在 Cocos Creator 编辑器中手动复制预制体:\n1. 在资源管理器中选择要复制的预制体\n2. 右键选择复制\n3. 在目标位置粘贴' }); } catch (error) { resolve({ success: false, error: `复制预制体时发生错误: ${error}` }); } }); } private async readPrefabContent(prefabPath: string): Promise<{ success: boolean; data?: any; error?: string }> { return new Promise((resolve) => { Editor.Message.request('asset-db', 'read-asset', prefabPath).then((content: string) => { try { const prefabData = JSON.parse(content); resolve({ success: true, data: prefabData }); } catch (parseError) { resolve({ success: false, error: '预制体文件格式错误' }); } }).catch((error: any) => { resolve({ success: false, error: error.message || '读取预制体文件失败' }); }); }); } private modifyPrefabForDuplication(prefabData: any[], newName: string, newUuid: string): any[] { // 修改预制体数据以创建副本 const modifiedData = [...prefabData]; // 修改第一个元素(预制体资产) if (modifiedData[0] && modifiedData[0].__type__ === 'cc.Prefab') { modifiedData[0]._name = newName || 'DuplicatedPrefab'; } // 更新所有UUID引用(简化版本) // 在实际应用中,可能需要更复杂的UUID映射处理 return modifiedData; } private async restorePrefabNode(nodeUuid: string, assetUuid: string): Promise { return new Promise((resolve) => { // 使用官方API restore-prefab 还原预制体节点 (Editor.Message.request as any)('scene', 'restore-prefab', nodeUuid, assetUuid).then(() => { resolve({ success: true, data: { nodeUuid: nodeUuid, assetUuid: assetUuid, message: '预制体节点还原成功' } }); }).catch((error: any) => { resolve({ success: false, error: `预制体节点还原失败: ${error.message}` }); }); }); } // 基于官方预制体格式的新实现方法 private async getNodeDataForPrefab(nodeUuid: string): Promise<{ success: boolean; data?: any; error?: string }> { return new Promise((resolve) => { Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeData: any) => { if (!nodeData) { resolve({ success: false, error: '节点不存在' }); return; } resolve({ success: true, data: nodeData }); }).catch((error: any) => { resolve({ success: false, error: error.message }); }); }); } private async createStandardPrefabData(nodeData: any, prefabName: string, prefabUuid: string): Promise { // 基于官方Canvas.prefab格式创建预制体数据结构 const prefabData: any[] = []; let currentId = 0; // 第一个元素:cc.Prefab 资源对象 const prefabAsset = { "__type__": "cc.Prefab", "_name": prefabName, "_objFlags": 0, "__editorExtras__": {}, "_native": "", "data": { "__id__": 1 }, "optimizationPolicy": 0, "persistent": false }; prefabData.push(prefabAsset); currentId++; // 第二个元素:根节点 const rootNode = await this.createNodeObject(nodeData, null, prefabData, currentId); prefabData.push(rootNode.node); currentId = rootNode.nextId; // 添加根节点的 PrefabInfo const rootPrefabInfo = { "__type__": "cc.PrefabInfo", "root": { "__id__": 1 }, "asset": { "__id__": 0 }, "fileId": this.generateFileId(), "targetOverrides": null }; prefabData.push(rootPrefabInfo); return prefabData; } private async createNodeObject(nodeData: any, parentId: number | null, prefabData: any[], currentId: number): Promise<{ node: any; nextId: number }> { const nodeId = currentId++; // 提取节点的基本属性 const position = nodeData.position?.value || { x: 0, y: 0, z: 0 }; const rotation = nodeData.rotation?.value || { x: 0, y: 0, z: 0, w: 1 }; const scale = nodeData.scale?.value || { x: 1, y: 1, z: 1 }; const active = nodeData.active?.value !== undefined ? nodeData.active.value : true; const name = nodeData.name?.value || 'Node'; const layer = nodeData.layer?.value || 33554432; const node: any = { "__type__": "cc.Node", "_name": name, "_objFlags": 0, "__editorExtras__": {}, "_parent": parentId !== null ? { "__id__": parentId } : null, "_children": [], "_active": active, "_components": [], "_prefab": { "__id__": currentId++ }, "_lpos": { "__type__": "cc.Vec3", "x": position.x, "y": position.y, "z": position.z }, "_lrot": { "__type__": "cc.Quat", "x": rotation.x, "y": rotation.y, "z": rotation.z, "w": rotation.w }, "_lscale": { "__type__": "cc.Vec3", "x": scale.x, "y": scale.y, "z": scale.z }, "_mobility": 0, "_layer": layer, "_euler": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 }, "_id": "" }; // 添加组件 if (nodeData.__comps__ && nodeData.__comps__.length > 0) { for (const comp of nodeData.__comps__) { const componentId = currentId++; node._components.push({ "__id__": componentId }); // 创建组件对象 const componentObj = this.createComponentObject(comp, nodeId, currentId++); prefabData.push(componentObj); // 添加组件的 CompPrefabInfo const compPrefabInfo = { "__type__": "cc.CompPrefabInfo", "fileId": this.generateFileId() }; prefabData.push(compPrefabInfo); currentId++; } } // 处理子节点 if (nodeData.children && Array.isArray(nodeData.children) && nodeData.children.length > 0) { console.log(`=== 处理子节点 ===`); console.log(`节点 ${name} 包含 ${nodeData.children.length} 个子节点`); console.log('完整子节点数据:', JSON.stringify(nodeData.children, null, 2)); for (let i = 0; i < nodeData.children.length; i++) { const childRef = nodeData.children[i]; console.log(`第${i}个子节点的完整数据:`, JSON.stringify(childRef, null, 2)); // 尝试多种可能的UUID提取方式 let childUuid = null; if (typeof childRef === 'string') { childUuid = childRef; console.log(`方法1 - 直接字符串: ${childUuid}`); } else if (childRef && childRef.value) { if (typeof childRef.value === 'string') { childUuid = childRef.value; console.log(`方法2 - value是字符串: ${childUuid}`); } else if (childRef.value.uuid) { childUuid = childRef.value.uuid; console.log(`方法3 - value.uuid: ${childUuid}`); } else if (childRef.value.__id__) { console.log(`方法4 - 发现__id__引用: ${childRef.value.__id__}`); // 这可能是一个内部引用,我们需要不同的处理方式 continue; } else { console.log('方法4 - value不是字符串,内容:', JSON.stringify(childRef.value)); } } else if (childRef && childRef.uuid) { childUuid = childRef.uuid; console.log(`方法5 - 直接uuid属性: ${childUuid}`); } else { console.log('方法6 - 无法识别的子节点格式:', JSON.stringify(childRef)); } if (childUuid && childUuid !== '[object Object]') { try { // 获取子节点数据 const childNodeData = await this.getNodeData(childUuid); if (childNodeData) { const childId = currentId; node._children.push({ "__id__": childId }); // 递归创建子节点 const childResult = await this.createNodeObject(childNodeData, nodeId, prefabData, currentId); prefabData.push(childResult.node); currentId = childResult.nextId; // 为子节点添加PrefabInfo const childPrefabInfo = { "__type__": "cc.PrefabInfo", "root": { "__id__": 1 // 指向根节点 }, "asset": { "__id__": 0 // 指向Prefab资源 }, "fileId": this.generateFileId(), "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null }; prefabData.push(childPrefabInfo); currentId++; console.log(`✅ 已添加子节点: ${childNodeData.name?.value || '未知'}`); console.log(`子节点在预制体数组中的索引: ${currentId}`); } else { console.warn(`无法获取子节点数据: ${childUuid}`); } } catch (error) { console.error(`处理子节点 ${childUuid} 时出错:`, error); } } } } return { node, nextId: currentId }; } private createComponentObject(componentData: any, nodeId: number, prefabInfoId: number): any { const componentType = componentData.__type__ || 'cc.Component'; const component: any = { "__type__": componentType, "_name": "", "_objFlags": 0, "__editorExtras__": {}, "node": { "__id__": nodeId }, "_enabled": componentData.enabled !== undefined ? componentData.enabled : true, "__prefab": { "__id__": prefabInfoId }, "_id": "" }; // 复制组件的其他属性 for (const key in componentData) { if (!key.startsWith('_') && key !== '__type__' && key !== 'enabled' && key !== 'node') { component[key] = componentData[key]; } } return component; } private createStandardMetaData(prefabName: string, prefabUuid: string): any { return { "ver": "1.1.50", "importer": "prefab", "imported": true, "uuid": prefabUuid, "files": [ ".json" ], "subMetas": {}, "userData": { "syncNodeName": prefabName } }; } private async savePrefabWithMeta(prefabPath: string, prefabData: any[], metaData: any): Promise<{ success: boolean; error?: string }> { try { const prefabContent = JSON.stringify(prefabData, null, 2); const metaContent = JSON.stringify(metaData, null, 2); // 确保路径以.prefab结尾 const finalPrefabPath = prefabPath.endsWith('.prefab') ? prefabPath : `${prefabPath}.prefab`; const metaPath = `${finalPrefabPath}.meta`; // 使用asset-db API创建预制体文件 await new Promise((resolve, reject) => { Editor.Message.request('asset-db', 'create-asset', finalPrefabPath, prefabContent).then(() => { resolve(true); }).catch((error: any) => { reject(error); }); }); // 创建meta文件 await new Promise((resolve, reject) => { Editor.Message.request('asset-db', 'create-asset', metaPath, metaContent).then(() => { resolve(true); }).catch((error: any) => { reject(error); }); }); console.log(`=== 预制体保存完成 ===`); console.log(`预制体文件已保存: ${finalPrefabPath}`); console.log(`Meta文件已保存: ${metaPath}`); console.log(`预制体数组总长度: ${prefabData.length}`); console.log(`预制体根节点索引: ${prefabData.length - 1}`); return { success: true }; } catch (error: any) { console.error('保存预制体文件时出错:', error); return { success: false, error: error.message }; } } }