Skip to main content
Glama
DaxianLee

Cocos Creator MCP Server Plugin

by DaxianLee
prefab-tools.ts115 kB
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 with all children and components', inputSchema: { type: 'object', properties: { nodeUuid: { type: 'string', description: 'Source node UUID' }, savePath: { type: 'string', description: 'Path to save the prefab (e.g., db://assets/prefabs/MyPrefab.prefab)' }, prefabName: { type: 'string', description: 'Prefab name' } }, required: ['nodeUuid', 'savePath', 'prefabName'] } }, { 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<ToolResponse> { 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 '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<ToolResponse> { 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<ToolResponse> { 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<ToolResponse> { return new Promise(async (resolve) => { try { // 获取预制体资源信息 const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', args.prefabPath); 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 } }; } // 创建节点 const nodeUuid = await Editor.Message.request('scene', 'create-node', createNodeOptions); const uuid = Array.isArray(nodeUuid) ? nodeUuid[0] : nodeUuid; // 注意:create-node API从预制体资源创建时应该自动建立预制体关联 console.log('预制体节点创建成功:', { nodeUuid: uuid, prefabUuid: assetInfo.uuid, prefabPath: args.prefabPath }); resolve({ success: true, data: { nodeUuid: uuid, prefabPath: args.prefabPath, parentUuid: args.parentUuid, position: args.position, message: '预制体实例化成功,已建立预制体关联' } }); } catch (err: any) { resolve({ success: false, error: `预制体实例化失败: ${err.message}`, instruction: '请检查预制体路径是否正确,确保预制体文件格式正确' }); } }); } /** * 建立节点与预制体的关联关系 * 这个方法创建必要的PrefabInfo和PrefabInstance结构 */ private async establishPrefabConnection(nodeUuid: string, prefabUuid: string, prefabPath: string): Promise<void> { try { // 读取预制体文件获取根节点的fileId const prefabContent = await this.readPrefabFile(prefabPath); if (!prefabContent || !prefabContent.data || !prefabContent.data.length) { throw new Error('无法读取预制体文件内容'); } // 找到预制体根节点的fileId (通常是第二个对象,即索引1) const rootNode = prefabContent.data.find((item: any) => item.__type === 'cc.Node' && item._parent === null); if (!rootNode || !rootNode._prefab) { throw new Error('无法找到预制体根节点或其预制体信息'); } // 获取根节点的PrefabInfo const rootPrefabInfo = prefabContent.data[rootNode._prefab.__id__]; if (!rootPrefabInfo || rootPrefabInfo.__type !== 'cc.PrefabInfo') { throw new Error('无法找到预制体根节点的PrefabInfo'); } const rootFileId = rootPrefabInfo.fileId; // 使用scene API建立预制体连接 const prefabConnectionData = { node: nodeUuid, prefab: prefabUuid, fileId: rootFileId }; // 尝试使用多种API方法建立预制体连接 const connectionMethods = [ () => Editor.Message.request('scene', 'connect-prefab-instance', prefabConnectionData), () => Editor.Message.request('scene', 'set-prefab-connection', prefabConnectionData), () => Editor.Message.request('scene', 'apply-prefab-link', prefabConnectionData) ]; let connected = false; for (const method of connectionMethods) { try { await method(); connected = true; break; } catch (error) { console.warn('预制体连接方法失败,尝试下一个方法:', error); } } if (!connected) { // 如果所有API方法都失败,尝试手动修改场景数据 console.warn('所有预制体连接API都失败,尝试手动建立连接'); await this.manuallyEstablishPrefabConnection(nodeUuid, prefabUuid, rootFileId); } } catch (error) { console.error('建立预制体连接失败:', error); throw error; } } /** * 手动建立预制体连接(当API方法失败时的备用方案) */ private async manuallyEstablishPrefabConnection(nodeUuid: string, prefabUuid: string, rootFileId: string): Promise<void> { try { // 尝试使用dump API修改节点的_prefab属性 const prefabConnectionData = { [nodeUuid]: { '_prefab': { '__uuid__': prefabUuid, '__expectedType__': 'cc.Prefab', 'fileId': rootFileId } } }; await Editor.Message.request('scene', 'set-property', { uuid: nodeUuid, path: '_prefab', dump: { value: { '__uuid__': prefabUuid, '__expectedType__': 'cc.Prefab' } } }); } catch (error) { console.error('手动建立预制体连接也失败:', error); // 不抛出错误,因为基本的节点创建已经成功 } } /** * 读取预制体文件内容 */ private async readPrefabFile(prefabPath: string): Promise<any> { try { // 尝试使用asset-db API读取文件内容 let assetContent: any; try { assetContent = await Editor.Message.request('asset-db', 'query-asset-info', prefabPath); if (assetContent && assetContent.source) { // 如果有source路径,直接读取文件 const fs = require('fs'); const path = require('path'); const fullPath = path.resolve(assetContent.source); const fileContent = fs.readFileSync(fullPath, 'utf8'); return JSON.parse(fileContent); } } catch (error) { console.warn('使用asset-db读取失败,尝试其他方法:', error); } // 备用方法:转换db://路径为实际文件路径 const fsPath = prefabPath.replace('db://assets/', 'assets/').replace('db://assets', 'assets'); const fs = require('fs'); const path = require('path'); // 尝试多个可能的项目根路径 const possiblePaths = [ path.resolve(process.cwd(), '../../NewProject_3', fsPath), path.resolve('/Users/lizhiyong/NewProject_3', fsPath), path.resolve(fsPath), // 如果是根目录下的文件,也尝试直接路径 path.resolve('/Users/lizhiyong/NewProject_3/assets', path.basename(fsPath)) ]; console.log('尝试读取预制体文件,路径转换:', { originalPath: prefabPath, fsPath: fsPath, possiblePaths: possiblePaths }); for (const fullPath of possiblePaths) { try { console.log(`检查路径: ${fullPath}`); if (fs.existsSync(fullPath)) { console.log(`找到文件: ${fullPath}`); const fileContent = fs.readFileSync(fullPath, 'utf8'); const parsed = JSON.parse(fileContent); console.log('文件解析成功,数据结构:', { hasData: !!parsed.data, dataLength: parsed.data ? parsed.data.length : 0 }); return parsed; } else { console.log(`文件不存在: ${fullPath}`); } } catch (readError) { console.warn(`读取文件失败 ${fullPath}:`, readError); } } throw new Error('无法找到或读取预制体文件'); } catch (error) { console.error('读取预制体文件失败:', error); throw error; } } private async tryCreateNodeWithPrefab(args: any): Promise<ToolResponse> { 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<ToolResponse> { 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<any> { 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<ToolResponse> { 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<ToolResponse> { 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); }); } /** * 使用 asset-db API 创建预制体的新方法 * 深度整合引擎的资源管理系统,实现完整的预制体创建流程 */ private async createPrefabWithAssetDB(nodeUuid: string, savePath: string, prefabName: string, includeChildren: boolean, includeComponents: boolean): Promise<ToolResponse> { return new Promise(async (resolve) => { try { console.log('=== 使用 Asset-DB API 创建预制体 ==='); console.log(`节点UUID: ${nodeUuid}`); console.log(`保存路径: ${savePath}`); console.log(`预制体名称: ${prefabName}`); // 第一步:获取节点数据(包括变换属性) const nodeData = await this.getNodeData(nodeUuid); if (!nodeData) { resolve({ success: false, error: '无法获取节点数据' }); return; } console.log('获取到节点数据,子节点数量:', nodeData.children ? nodeData.children.length : 0); // 第二步:先创建资源文件以获取引擎分配的UUID console.log('创建预制体资源文件...'); const tempPrefabContent = JSON.stringify([{"__type__": "cc.Prefab", "_name": prefabName}], null, 2); const createResult = await this.createAssetWithAssetDB(savePath, tempPrefabContent); if (!createResult.success) { resolve(createResult); return; } // 获取引擎分配的实际UUID const actualPrefabUuid = createResult.data?.uuid; if (!actualPrefabUuid) { resolve({ success: false, error: '无法获取引擎分配的预制体UUID' }); return; } console.log('引擎分配的UUID:', actualPrefabUuid); // 第三步:使用实际UUID重新生成预制体内容 const prefabContent = await this.createStandardPrefabContent(nodeData, prefabName, actualPrefabUuid, includeChildren, includeComponents); const prefabContentString = JSON.stringify(prefabContent, null, 2); // 第四步:更新预制体文件内容 console.log('更新预制体文件内容...'); const updateResult = await this.updateAssetWithAssetDB(savePath, prefabContentString); // 第五步:创建对应的meta文件(使用实际UUID) console.log('创建预制体meta文件...'); const metaContent = this.createStandardMetaContent(prefabName, actualPrefabUuid); const metaResult = await this.createMetaWithAssetDB(savePath, metaContent); // 第六步:重新导入资源以更新引用 console.log('重新导入预制体资源...'); const reimportResult = await this.reimportAssetWithAssetDB(savePath); // 第七步:尝试将原始节点转换为预制体实例 console.log('尝试将原始节点转换为预制体实例...'); const convertResult = await this.convertNodeToPrefabInstance(nodeUuid, actualPrefabUuid, savePath); resolve({ success: true, data: { prefabUuid: actualPrefabUuid, prefabPath: savePath, nodeUuid: nodeUuid, prefabName: prefabName, convertedToPrefabInstance: convertResult.success, createAssetResult: createResult, updateResult: updateResult, metaResult: metaResult, reimportResult: reimportResult, convertResult: convertResult, message: convertResult.success ? '预制体创建并成功转换原始节点' : '预制体创建成功,但节点转换失败' } }); } catch (error) { console.error('创建预制体时发生错误:', error); resolve({ success: false, error: `创建预制体失败: ${error}` }); } }); } private async createPrefab(args: any): Promise<ToolResponse> { 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`; const includeChildren = args.includeChildren !== false; // 默认为 true const includeComponents = args.includeComponents !== false; // 默认为 true // 优先使用新的 asset-db 方法创建预制体 console.log('使用新的 asset-db 方法创建预制体...'); const assetDbResult = await this.createPrefabWithAssetDB( args.nodeUuid, fullPath, prefabName, includeChildren, includeComponents ); if (assetDbResult.success) { resolve(assetDbResult); return; } // 如果 asset-db 方法失败,尝试使用Cocos Creator的原生预制体创建API console.log('asset-db 方法失败,尝试原生API...'); const nativeResult = await this.createPrefabNative(args.nodeUuid, fullPath); if (nativeResult.success) { resolve(nativeResult); return; } // 如果原生API失败,使用自定义实现 console.log('原生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<ToolResponse> { 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<ToolResponse> { 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.createStandardPrefabContent(nodeData, prefabName, prefabUuid, true, true); // 5. 创建标准meta文件数据 const standardMetaData = this.createStandardMetaData(prefabName, prefabUuid); // 6. 保存预制体和meta文件 const saveResult = await this.savePrefabWithMeta(prefabPath, prefabJsonData, standardMetaData); if (saveResult.success) { // 保存成功后,将原始节点转换为预制体实例 const convertResult = await this.convertNodeToPrefabInstance(nodeUuid, prefabPath, prefabUuid); resolve({ success: true, data: { prefabUuid: prefabUuid, prefabPath: prefabPath, nodeUuid: nodeUuid, prefabName: prefabName, convertedToPrefabInstance: convertResult.success, message: convertResult.success ? '自定义预制体创建成功,原始节点已转换为预制体实例' : '预制体创建成功,但节点转换失败' } }); } else { resolve({ success: false, error: saveResult.error || '保存预制体文件失败' }); } } catch (error) { resolve({ success: false, error: `创建预制体时发生错误: ${error}` }); } }); } private async getNodeData(nodeUuid: string): Promise<any> { return new Promise(async (resolve) => { try { // 首先获取基本节点信息 const nodeInfo = await Editor.Message.request('scene', 'query-node', nodeUuid); if (!nodeInfo) { resolve(null); return; } console.log(`获取节点 ${nodeUuid} 的基本信息成功`); // 使用query-node-tree获取包含子节点的完整结构 const nodeTree = await this.getNodeWithChildren(nodeUuid); if (nodeTree) { console.log(`获取节点 ${nodeUuid} 的完整树结构成功`); resolve(nodeTree); } else { console.log(`使用基本节点信息`); resolve(nodeInfo); } } catch (error) { console.warn(`获取节点数据失败 ${nodeUuid}:`, error); resolve(null); } }); } // 使用query-node-tree获取包含子节点的完整节点结构 private async getNodeWithChildren(nodeUuid: string): Promise<any> { try { // 获取整个场景树 const tree = await Editor.Message.request('scene', 'query-node-tree'); if (!tree) { return null; } // 在树中查找指定的节点 const targetNode = this.findNodeInTree(tree, nodeUuid); if (targetNode) { console.log(`在场景树中找到节点 ${nodeUuid},子节点数量: ${targetNode.children ? targetNode.children.length : 0}`); // 增强节点树,获取每个节点的正确组件信息 const enhancedTree = await this.enhanceTreeWithMCPComponents(targetNode); return enhancedTree; } return null; } catch (error) { console.warn(`获取节点树结构失败 ${nodeUuid}:`, error); return null; } } // 在节点树中递归查找指定UUID的节点 private findNodeInTree(node: any, targetUuid: string): any { if (!node) return null; // 检查当前节点 if (node.uuid === targetUuid || node.value?.uuid === targetUuid) { return node; } // 递归检查子节点 if (node.children && Array.isArray(node.children)) { for (const child of node.children) { const found = this.findNodeInTree(child, targetUuid); if (found) { return found; } } } return null; } /** * 使用MCP接口增强节点树,获取正确的组件信息 */ private async enhanceTreeWithMCPComponents(node: any): Promise<any> { if (!node || !node.uuid) { return node; } try { // 使用MCP接口获取节点的组件信息 const response = await fetch('http://localhost:8585/mcp', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "component_get_components", "arguments": { "nodeUuid": node.uuid } }, "id": Date.now() }) }); const mcpResult = await response.json(); if (mcpResult.result?.content?.[0]?.text) { const componentData = JSON.parse(mcpResult.result.content[0].text); if (componentData.success && componentData.data.components) { // 更新节点的组件信息为MCP返回的正确数据 node.components = componentData.data.components; console.log(`节点 ${node.uuid} 获取到 ${componentData.data.components.length} 个组件,包含脚本组件的正确类型`); } } } catch (error) { console.warn(`获取节点 ${node.uuid} 的MCP组件信息失败:`, error); } // 递归处理子节点 if (node.children && Array.isArray(node.children)) { for (let i = 0; i < node.children.length; i++) { node.children[i] = await this.enhanceTreeWithMCPComponents(node.children[i]); } } return node; } private async buildBasicNodeInfo(nodeUuid: string): Promise<any> { 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 isValidNodeData(nodeData: any): boolean { if (!nodeData) return false; if (typeof nodeData !== 'object') return false; // 检查基本属性 - 适配query-node-tree的数据格式 return nodeData.hasOwnProperty('uuid') || nodeData.hasOwnProperty('name') || nodeData.hasOwnProperty('__type__') || (nodeData.value && ( nodeData.value.hasOwnProperty('uuid') || nodeData.value.hasOwnProperty('name') || nodeData.value.hasOwnProperty('__type__') )); } // 提取子节点UUID的统一方法 private extractChildUuid(childRef: any): string | null { if (!childRef) return null; // 方法1: 直接字符串 if (typeof childRef === 'string') { return childRef; } // 方法2: value属性包含字符串 if (childRef.value && typeof childRef.value === 'string') { return childRef.value; } // 方法3: value.uuid属性 if (childRef.value && childRef.value.uuid) { return childRef.value.uuid; } // 方法4: 直接uuid属性 if (childRef.uuid) { return childRef.uuid; } // 方法5: __id__引用 - 这种情况需要特殊处理 if (childRef.__id__ !== undefined) { console.log(`发现__id__引用: ${childRef.__id__},可能需要从数据结构中查找`); return null; // 暂时返回null,后续可以添加引用解析逻辑 } console.warn('无法提取子节点UUID:', JSON.stringify(childRef)); return null; } // 获取需要处理的子节点数据 private getChildrenToProcess(nodeData: any): any[] { const children: any[] = []; // 方法1: 直接从children数组获取(从query-node-tree返回的数据) if (nodeData.children && Array.isArray(nodeData.children)) { console.log(`从children数组获取子节点,数量: ${nodeData.children.length}`); for (const child of nodeData.children) { // query-node-tree返回的子节点通常已经是完整的数据结构 if (this.isValidNodeData(child)) { children.push(child); console.log(`添加子节点: ${child.name || child.value?.name || '未知'}`); } else { console.log('子节点数据无效:', JSON.stringify(child, null, 2)); } } } else { console.log('节点没有子节点或children数组为空'); } return children; } 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": 0, "y": 0, "z": 0 }, "_lrot": { "__type__": "cc.Quat", "x": 0, "y": 0, "z": 0, "w": 1 }, "_lscale": { "__type__": "cc.Vec3", "x": 1, "y": 1, "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<void> { 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<ToolResponse> { 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<ToolResponse> { 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<ToolResponse> { 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<ToolResponse> { // 从 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<ToolResponse> { 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<ToolResponse> { 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; } /** * 使用 asset-db API 创建资源文件 */ private async createAssetWithAssetDB(assetPath: string, content: string): Promise<{ success: boolean; data?: any; error?: string }> { return new Promise((resolve) => { Editor.Message.request('asset-db', 'create-asset', assetPath, content, { overwrite: true, rename: false }).then((assetInfo: any) => { console.log('创建资源文件成功:', assetInfo); resolve({ success: true, data: assetInfo }); }).catch((error: any) => { console.error('创建资源文件失败:', error); resolve({ success: false, error: error.message || '创建资源文件失败' }); }); }); } /** * 使用 asset-db API 创建 meta 文件 */ private async createMetaWithAssetDB(assetPath: string, metaContent: any): Promise<{ success: boolean; data?: any; error?: string }> { return new Promise((resolve) => { const metaContentString = JSON.stringify(metaContent, null, 2); Editor.Message.request('asset-db', 'save-asset-meta', assetPath, metaContentString).then((assetInfo: any) => { console.log('创建meta文件成功:', assetInfo); resolve({ success: true, data: assetInfo }); }).catch((error: any) => { console.error('创建meta文件失败:', error); resolve({ success: false, error: error.message || '创建meta文件失败' }); }); }); } /** * 使用 asset-db API 重新导入资源 */ private async reimportAssetWithAssetDB(assetPath: string): Promise<{ success: boolean; data?: any; error?: string }> { return new Promise((resolve) => { Editor.Message.request('asset-db', 'reimport-asset', assetPath).then((result: any) => { console.log('重新导入资源成功:', result); resolve({ success: true, data: result }); }).catch((error: any) => { console.error('重新导入资源失败:', error); resolve({ success: false, error: error.message || '重新导入资源失败' }); }); }); } /** * 使用 asset-db API 更新资源文件内容 */ private async updateAssetWithAssetDB(assetPath: string, content: string): Promise<{ success: boolean; data?: any; error?: string }> { return new Promise((resolve) => { Editor.Message.request('asset-db', 'save-asset', assetPath, content).then((result: any) => { console.log('更新资源文件成功:', result); resolve({ success: true, data: result }); }).catch((error: any) => { console.error('更新资源文件失败:', error); resolve({ success: false, error: error.message || '更新资源文件失败' }); }); }); } /** * 创建符合 Cocos Creator 标准的预制体内容 * 完整实现递归节点树处理,匹配引擎标准格式 */ private async createStandardPrefabContent(nodeData: any, prefabName: string, prefabUuid: string, includeChildren: boolean, includeComponents: boolean): Promise<any[]> { console.log('开始创建引擎标准预制体内容...'); const prefabData: any[] = []; let currentId = 0; // 1. 创建预制体资产对象 (index 0) const prefabAsset = { "__type__": "cc.Prefab", "_name": prefabName || "", // 确保预制体名称不为空 "_objFlags": 0, "__editorExtras__": {}, "_native": "", "data": { "__id__": 1 }, "optimizationPolicy": 0, "persistent": false }; prefabData.push(prefabAsset); currentId++; // 2. 递归创建完整的节点树结构 const context = { prefabData, currentId: currentId + 1, // 根节点占用索引1,子节点从索引2开始 prefabAssetIndex: 0, nodeFileIds: new Map<string, string>(), // 存储节点ID到fileId的映射 nodeUuidToIndex: new Map<string, number>(), // 存储节点UUID到索引的映射 componentUuidToIndex: new Map<string, number>() // 存储组件UUID到索引的映射 }; // 创建根节点和整个节点树 - 注意:根节点的父节点应该是null,不是预制体对象 await this.createCompleteNodeTree(nodeData, null, 1, context, includeChildren, includeComponents, prefabName); console.log(`预制体内容创建完成,总共 ${prefabData.length} 个对象`); console.log('节点fileId映射:', Array.from(context.nodeFileIds.entries())); return prefabData; } /** * 递归创建完整的节点树,包括所有子节点和对应的PrefabInfo */ private async createCompleteNodeTree( nodeData: any, parentNodeIndex: number | null, nodeIndex: number, context: { prefabData: any[], currentId: number, prefabAssetIndex: number, nodeFileIds: Map<string, string>, nodeUuidToIndex: Map<string, number>, componentUuidToIndex: Map<string, number> }, includeChildren: boolean, includeComponents: boolean, nodeName?: string ): Promise<void> { const { prefabData } = context; // 创建节点对象 const node = this.createEngineStandardNode(nodeData, parentNodeIndex, nodeName); // 确保节点在指定的索引位置 while (prefabData.length <= nodeIndex) { prefabData.push(null); } console.log(`设置节点到索引 ${nodeIndex}: ${node._name}, _parent:`, node._parent, `_children count: ${node._children.length}`); prefabData[nodeIndex] = node; // 为当前节点生成fileId并记录UUID到索引的映射 const nodeUuid = this.extractNodeUuid(nodeData); const fileId = nodeUuid || this.generateFileId(); context.nodeFileIds.set(nodeIndex.toString(), fileId); // 记录节点UUID到索引的映射 if (nodeUuid) { context.nodeUuidToIndex.set(nodeUuid, nodeIndex); console.log(`记录节点UUID映射: ${nodeUuid} -> ${nodeIndex}`); } // 先处理子节点(保持与手动创建的索引顺序一致) const childrenToProcess = this.getChildrenToProcess(nodeData); if (includeChildren && childrenToProcess.length > 0) { console.log(`处理节点 ${node._name} 的 ${childrenToProcess.length} 个子节点`); // 为每个子节点分配索引 const childIndices: number[] = []; console.log(`准备为 ${childrenToProcess.length} 个子节点分配索引,当前ID: ${context.currentId}`); for (let i = 0; i < childrenToProcess.length; i++) { console.log(`处理第 ${i+1} 个子节点,当前currentId: ${context.currentId}`); const childIndex = context.currentId++; childIndices.push(childIndex); node._children.push({ "__id__": childIndex }); console.log(`✅ 添加子节点引用到 ${node._name}: {__id__: ${childIndex}}`); } console.log(`✅ 节点 ${node._name} 最终的子节点数组:`, node._children); // 递归创建子节点 for (let i = 0; i < childrenToProcess.length; i++) { const childData = childrenToProcess[i]; const childIndex = childIndices[i]; await this.createCompleteNodeTree( childData, nodeIndex, childIndex, context, includeChildren, includeComponents, childData.name || `Child${i+1}` ); } } // 然后处理组件 if (includeComponents && nodeData.components && Array.isArray(nodeData.components)) { console.log(`处理节点 ${node._name} 的 ${nodeData.components.length} 个组件`); const componentIndices: number[] = []; for (const component of nodeData.components) { const componentIndex = context.currentId++; componentIndices.push(componentIndex); node._components.push({ "__id__": componentIndex }); // 记录组件UUID到索引的映射 const componentUuid = component.uuid || (component.value && component.value.uuid); if (componentUuid) { context.componentUuidToIndex.set(componentUuid, componentIndex); console.log(`记录组件UUID映射: ${componentUuid} -> ${componentIndex}`); } // 创建组件对象,传入context以处理引用 const componentObj = this.createComponentObject(component, nodeIndex, context); prefabData[componentIndex] = componentObj; // 为组件创建 CompPrefabInfo const compPrefabInfoIndex = context.currentId++; prefabData[compPrefabInfoIndex] = { "__type__": "cc.CompPrefabInfo", "fileId": this.generateFileId() }; // 如果组件对象有 __prefab 属性,设置引用 if (componentObj && typeof componentObj === 'object') { componentObj.__prefab = { "__id__": compPrefabInfoIndex }; } } console.log(`✅ 节点 ${node._name} 添加了 ${componentIndices.length} 个组件`); } // 为当前节点创建PrefabInfo const prefabInfoIndex = context.currentId++; node._prefab = { "__id__": prefabInfoIndex }; const prefabInfo: any = { "__type__": "cc.PrefabInfo", "root": { "__id__": 1 }, "asset": { "__id__": context.prefabAssetIndex }, "fileId": fileId, "targetOverrides": null, "nestedPrefabInstanceRoots": null }; // 根节点的特殊处理 if (nodeIndex === 1) { // 根节点没有instance,但可能有targetOverrides prefabInfo.instance = null; } else { // 子节点通常有instance为null prefabInfo.instance = null; } prefabData[prefabInfoIndex] = prefabInfo; context.currentId = prefabInfoIndex + 1; } /** * 将UUID转换为Cocos Creator的压缩格式 * 基于真实Cocos Creator编辑器的压缩算法实现 * 前5个hex字符保持不变,剩余27个字符压缩成18个字符 */ private uuidToCompressedId(uuid: string): string { const BASE64_KEYS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; // 移除连字符并转为小写 const cleanUuid = uuid.replace(/-/g, '').toLowerCase(); // 确保UUID有效 if (cleanUuid.length !== 32) { return uuid; // 如果不是有效的UUID,返回原始值 } // Cocos Creator的压缩算法:前5个字符保持不变,剩余27个字符压缩成18个字符 let result = cleanUuid.substring(0, 5); // 剩余27个字符需要压缩成18个字符 const remainder = cleanUuid.substring(5); // 每3个hex字符压缩成2个字符 for (let i = 0; i < remainder.length; i += 3) { const hex1 = remainder[i] || '0'; const hex2 = remainder[i + 1] || '0'; const hex3 = remainder[i + 2] || '0'; // 将3个hex字符(12位)转换为2个base64字符 const value = parseInt(hex1 + hex2 + hex3, 16); // 12位分成两个6位 const high6 = (value >> 6) & 63; const low6 = value & 63; result += BASE64_KEYS[high6] + BASE64_KEYS[low6]; } return result; } /** * 创建组件对象 */ private createComponentObject(componentData: any, nodeIndex: number, context?: { nodeUuidToIndex?: Map<string, number>, componentUuidToIndex?: Map<string, number> }): any { let componentType = componentData.type || componentData.__type__ || 'cc.Component'; const enabled = componentData.enabled !== undefined ? componentData.enabled : true; // console.log(`创建组件对象 - 原始类型: ${componentType}`); // console.log('组件完整数据:', JSON.stringify(componentData, null, 2)); // 处理脚本组件 - MCP接口已经返回正确的压缩UUID格式 if (componentType && !componentType.startsWith('cc.')) { console.log(`使用脚本组件压缩UUID类型: ${componentType}`); } // 基础组件结构 const component: any = { "__type__": componentType, "_name": "", "_objFlags": 0, "__editorExtras__": {}, "node": { "__id__": nodeIndex }, "_enabled": enabled }; // 提前设置 __prefab 属性占位符,后续会被正确设置 component.__prefab = null; // 根据组件类型添加特定属性 if (componentType === 'cc.UITransform') { const contentSize = componentData.properties?.contentSize?.value || { width: 100, height: 100 }; const anchorPoint = componentData.properties?.anchorPoint?.value || { x: 0.5, y: 0.5 }; component._contentSize = { "__type__": "cc.Size", "width": contentSize.width, "height": contentSize.height }; component._anchorPoint = { "__type__": "cc.Vec2", "x": anchorPoint.x, "y": anchorPoint.y }; } else if (componentType === 'cc.Sprite') { // 处理Sprite组件的spriteFrame引用 const spriteFrameProp = componentData.properties?._spriteFrame || componentData.properties?.spriteFrame; if (spriteFrameProp) { component._spriteFrame = this.processComponentProperty(spriteFrameProp, context); } else { component._spriteFrame = null; } component._type = componentData.properties?._type?.value ?? 0; component._fillType = componentData.properties?._fillType?.value ?? 0; component._sizeMode = componentData.properties?._sizeMode?.value ?? 1; component._fillCenter = { "__type__": "cc.Vec2", "x": 0, "y": 0 }; component._fillStart = componentData.properties?._fillStart?.value ?? 0; component._fillRange = componentData.properties?._fillRange?.value ?? 0; component._isTrimmedMode = componentData.properties?._isTrimmedMode?.value ?? true; component._useGrayscale = componentData.properties?._useGrayscale?.value ?? false; // 调试:打印Sprite组件的所有属性(已注释) // console.log('Sprite组件属性:', JSON.stringify(componentData.properties, null, 2)); component._atlas = null; component._id = ""; } else if (componentType === 'cc.Button') { component._interactable = true; component._transition = 3; component._normalColor = { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 }; component._hoverColor = { "__type__": "cc.Color", "r": 211, "g": 211, "b": 211, "a": 255 }; component._pressedColor = { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 }; component._disabledColor = { "__type__": "cc.Color", "r": 124, "g": 124, "b": 124, "a": 255 }; component._normalSprite = null; component._hoverSprite = null; component._pressedSprite = null; component._disabledSprite = null; component._duration = 0.1; component._zoomScale = 1.2; // 处理Button的target引用 const targetProp = componentData.properties?._target || componentData.properties?.target; if (targetProp) { component._target = this.processComponentProperty(targetProp, context); } else { component._target = { "__id__": nodeIndex }; // 默认指向自身节点 } component._clickEvents = []; component._id = ""; } else if (componentType === 'cc.Label') { component._string = componentData.properties?._string?.value || "Label"; component._horizontalAlign = 1; component._verticalAlign = 1; component._actualFontSize = 20; component._fontSize = 20; component._fontFamily = "Arial"; component._lineHeight = 25; component._overflow = 0; component._enableWrapText = true; component._font = null; component._isSystemFontUsed = true; component._spacingX = 0; component._isItalic = false; component._isBold = false; component._isUnderline = false; component._underlineHeight = 2; component._cacheMode = 0; component._id = ""; } else if (componentData.properties) { // 处理所有组件的属性(包括内置组件和自定义脚本组件) for (const [key, value] of Object.entries(componentData.properties)) { if (key === 'node' || key === 'enabled' || key === '__type__' || key === 'uuid' || key === 'name' || key === '__scriptAsset' || key === '_objFlags') { continue; // 跳过这些特殊属性,包括_objFlags } // 对于以下划线开头的属性,需要特殊处理 if (key.startsWith('_')) { // 确保属性名保持原样(包括下划线) const propValue = this.processComponentProperty(value, context); if (propValue !== undefined) { component[key] = propValue; } } else { // 非下划线开头的属性正常处理 const propValue = this.processComponentProperty(value, context); if (propValue !== undefined) { component[key] = propValue; } } } } // 确保 _id 在最后位置 const _id = component._id || ""; delete component._id; component._id = _id; return component; } /** * 处理组件属性值,确保格式与手动创建的预制体一致 */ private processComponentProperty(propData: any, context?: { nodeUuidToIndex?: Map<string, number>, componentUuidToIndex?: Map<string, number> }): any { if (!propData || typeof propData !== 'object') { return propData; } const value = propData.value; const type = propData.type; // 处理null值 if (value === null || value === undefined) { return null; } // 处理空UUID对象,转换为null if (value && typeof value === 'object' && value.uuid === '') { return null; } // 处理节点引用 if (type === 'cc.Node' && value?.uuid) { // 在预制体中,节点引用需要转换为 __id__ 形式 if (context?.nodeUuidToIndex && context.nodeUuidToIndex.has(value.uuid)) { // 内部引用:转换为__id__格式 return { "__id__": context.nodeUuidToIndex.get(value.uuid) }; } // 外部引用:设置为null,因为外部节点不属于预制体结构 console.warn(`Node reference UUID ${value.uuid} not found in prefab context, setting to null (external reference)`); return null; } // 处理资源引用(预制体、纹理、精灵帧等) if (value?.uuid && ( type === 'cc.Prefab' || type === 'cc.Texture2D' || type === 'cc.SpriteFrame' || type === 'cc.Material' || type === 'cc.AnimationClip' || type === 'cc.AudioClip' || type === 'cc.Font' || type === 'cc.Asset' )) { // 对于预制体引用,保持原始UUID格式 const uuidToUse = type === 'cc.Prefab' ? value.uuid : this.uuidToCompressedId(value.uuid); return { "__uuid__": uuidToUse, "__expectedType__": type }; } // 处理组件引用(包括具体的组件类型如cc.Label, cc.Button等) if (value?.uuid && (type === 'cc.Component' || type === 'cc.Label' || type === 'cc.Button' || type === 'cc.Sprite' || type === 'cc.UITransform' || type === 'cc.RigidBody2D' || type === 'cc.BoxCollider2D' || type === 'cc.Animation' || type === 'cc.AudioSource' || (type?.startsWith('cc.') && !type.includes('@')))) { // 在预制体中,组件引用也需要转换为 __id__ 形式 if (context?.componentUuidToIndex && context.componentUuidToIndex.has(value.uuid)) { // 内部引用:转换为__id__格式 console.log(`Component reference ${type} UUID ${value.uuid} found in prefab context, converting to __id__`); return { "__id__": context.componentUuidToIndex.get(value.uuid) }; } // 外部引用:设置为null,因为外部组件不属于预制体结构 console.warn(`Component reference ${type} UUID ${value.uuid} not found in prefab context, setting to null (external reference)`); return null; } // 处理复杂类型,添加__type__标记 if (value && typeof value === 'object') { if (type === 'cc.Color') { return { "__type__": "cc.Color", "r": Math.min(255, Math.max(0, Number(value.r) || 0)), "g": Math.min(255, Math.max(0, Number(value.g) || 0)), "b": Math.min(255, Math.max(0, Number(value.b) || 0)), "a": value.a !== undefined ? Math.min(255, Math.max(0, Number(value.a))) : 255 }; } else if (type === 'cc.Vec3') { return { "__type__": "cc.Vec3", "x": Number(value.x) || 0, "y": Number(value.y) || 0, "z": Number(value.z) || 0 }; } else if (type === 'cc.Vec2') { return { "__type__": "cc.Vec2", "x": Number(value.x) || 0, "y": Number(value.y) || 0 }; } else if (type === 'cc.Size') { return { "__type__": "cc.Size", "width": Number(value.width) || 0, "height": Number(value.height) || 0 }; } else if (type === 'cc.Quat') { return { "__type__": "cc.Quat", "x": Number(value.x) || 0, "y": Number(value.y) || 0, "z": Number(value.z) || 0, "w": value.w !== undefined ? Number(value.w) : 1 }; } } // 处理数组类型 if (Array.isArray(value)) { // 节点数组 if (propData.elementTypeData?.type === 'cc.Node') { return value.map(item => { if (item?.uuid && context?.nodeUuidToIndex?.has(item.uuid)) { return { "__id__": context.nodeUuidToIndex.get(item.uuid) }; } return null; }).filter(item => item !== null); } // 资源数组 if (propData.elementTypeData?.type && propData.elementTypeData.type.startsWith('cc.')) { return value.map(item => { if (item?.uuid) { return { "__uuid__": this.uuidToCompressedId(item.uuid), "__expectedType__": propData.elementTypeData.type }; } return null; }).filter(item => item !== null); } // 基础类型数组 return value.map(item => item?.value !== undefined ? item.value : item); } // 其他复杂对象类型,保持原样但确保有__type__标记 if (value && typeof value === 'object' && type && type.startsWith('cc.')) { return { "__type__": type, ...value }; } return value; } /** * 创建符合引擎标准的节点对象 */ private createEngineStandardNode(nodeData: any, parentNodeIndex: number | null, nodeName?: string): any { // 调试:打印原始节点数据(已注释) // console.log('原始节点数据:', JSON.stringify(nodeData, null, 2)); // 提取节点的基本属性 const getValue = (prop: any) => { if (prop?.value !== undefined) return prop.value; if (prop !== undefined) return prop; return null; }; const position = getValue(nodeData.position) || getValue(nodeData.value?.position) || { x: 0, y: 0, z: 0 }; const rotation = getValue(nodeData.rotation) || getValue(nodeData.value?.rotation) || { x: 0, y: 0, z: 0, w: 1 }; const scale = getValue(nodeData.scale) || getValue(nodeData.value?.scale) || { x: 1, y: 1, z: 1 }; const active = getValue(nodeData.active) ?? getValue(nodeData.value?.active) ?? true; const name = nodeName || getValue(nodeData.name) || getValue(nodeData.value?.name) || 'Node'; const layer = getValue(nodeData.layer) || getValue(nodeData.value?.layer) || 1073741824; // 调试输出 console.log(`创建节点: ${name}, parentNodeIndex: ${parentNodeIndex}`); const parentRef = parentNodeIndex !== null ? { "__id__": parentNodeIndex } : null; console.log(`节点 ${name} 的父节点引用:`, parentRef); return { "__type__": "cc.Node", "_name": name, "_objFlags": 0, "__editorExtras__": {}, "_parent": parentRef, "_children": [], // 子节点引用将在递归过程中动态添加 "_active": active, "_components": [], // 组件引用将在处理组件时动态添加 "_prefab": { "__id__": 0 }, // 临时值,后续会被正确设置 "_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": "" }; } /** * 从节点数据中提取UUID */ private extractNodeUuid(nodeData: any): string | null { if (!nodeData) return null; // 尝试多种方式获取UUID const sources = [ nodeData.uuid, nodeData.value?.uuid, nodeData.__uuid__, nodeData.value?.__uuid__, nodeData.id, nodeData.value?.id ]; for (const source of sources) { if (typeof source === 'string' && source.length > 0) { return source; } } return null; } /** * 创建最小化的节点对象,不包含任何组件以避免依赖问题 */ private createMinimalNode(nodeData: any, nodeName?: string): any { // 提取节点的基本属性 const getValue = (prop: any) => { if (prop?.value !== undefined) return prop.value; if (prop !== undefined) return prop; return null; }; const position = getValue(nodeData.position) || getValue(nodeData.value?.position) || { x: 0, y: 0, z: 0 }; const rotation = getValue(nodeData.rotation) || getValue(nodeData.value?.rotation) || { x: 0, y: 0, z: 0, w: 1 }; const scale = getValue(nodeData.scale) || getValue(nodeData.value?.scale) || { x: 1, y: 1, z: 1 }; const active = getValue(nodeData.active) ?? getValue(nodeData.value?.active) ?? true; const name = nodeName || getValue(nodeData.name) || getValue(nodeData.value?.name) || 'Node'; const layer = getValue(nodeData.layer) || getValue(nodeData.value?.layer) || 33554432; return { "__type__": "cc.Node", "_name": name, "_objFlags": 0, "_parent": null, "_children": [], "_active": active, "_components": [], // 空的组件数组,避免组件依赖问题 "_prefab": { "__id__": 2 }, "_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 }, "_layer": layer, "_euler": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 }, "_id": "" }; } /** * 创建标准的 meta 文件内容 */ private createStandardMetaContent(prefabName: string, prefabUuid: string): any { return { "ver": "2.0.3", "importer": "prefab", "imported": true, "uuid": prefabUuid, "files": [ ".json" ], "subMetas": {}, "userData": { "syncNodeName": prefabName, "hasIcon": false } }; } /** * 尝试将原始节点转换为预制体实例 */ private async convertNodeToPrefabInstance(nodeUuid: string, prefabUuid: string, prefabPath: string): Promise<{ success: boolean; error?: string }> { return new Promise((resolve) => { // 这个功能需要深入的场景编辑器集成,暂时返回失败 // 在实际的引擎中,这涉及到复杂的预制体实例化和节点替换逻辑 console.log('节点转换为预制体实例的功能需要更深入的引擎集成'); resolve({ success: false, error: '节点转换为预制体实例需要更深入的引擎集成支持' }); }); } private async restorePrefabNode(nodeUuid: string, assetUuid: string): Promise<ToolResponse> { 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<any[]> { // 基于官方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 - 修复asset引用使用UUID const rootPrefabInfo = { "__type__": "cc.PrefabInfo", "root": { "__id__": 1 }, "asset": { "__uuid__": prefabUuid }, "fileId": this.generateFileId(), "instance": null, "targetOverrides": [], "nestedPrefabInstanceRoots": [] }; 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++; // 提取节点的基本属性 - 适配query-node-tree的数据格式 const getValue = (prop: any) => { if (prop?.value !== undefined) return prop.value; if (prop !== undefined) return prop; return null; }; const position = getValue(nodeData.position) || getValue(nodeData.value?.position) || { x: 0, y: 0, z: 0 }; const rotation = getValue(nodeData.rotation) || getValue(nodeData.value?.rotation) || { x: 0, y: 0, z: 0, w: 1 }; const scale = getValue(nodeData.scale) || getValue(nodeData.value?.scale) || { x: 1, y: 1, z: 1 }; const active = getValue(nodeData.active) ?? getValue(nodeData.value?.active) ?? true; const name = getValue(nodeData.name) || getValue(nodeData.value?.name) || 'Node'; const layer = getValue(nodeData.layer) || getValue(nodeData.value?.layer) || 33554432; const node: any = { "__type__": "cc.Node", "_name": name, "_objFlags": 0, "__editorExtras__": {}, "_parent": parentId !== null ? { "__id__": parentId } : null, "_children": [], "_active": active, "_components": [], "_prefab": parentId === null ? { "__id__": currentId++ } : null, "_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": "" }; // 暂时跳过UITransform组件以避免_getDependComponent错误 // 后续通过Engine API动态添加 console.log(`节点 ${name} 暂时跳过UITransform组件,避免引擎依赖错误`); // 处理其他组件(暂时跳过,专注于修复UITransform问题) const components = this.extractComponentsFromNode(nodeData); if (components.length > 0) { console.log(`节点 ${name} 包含 ${components.length} 个其他组件,暂时跳过以专注于UITransform修复`); } // 处理子节点 - 使用query-node-tree获取的完整结构 const childrenToProcess = this.getChildrenToProcess(nodeData); if (childrenToProcess.length > 0) { console.log(`=== 处理子节点 ===`); console.log(`节点 ${name} 包含 ${childrenToProcess.length} 个子节点`); for (let i = 0; i < childrenToProcess.length; i++) { const childData = childrenToProcess[i]; const childName = childData.name || childData.value?.name || '未知'; console.log(`处理第${i + 1}个子节点: ${childName}`); try { const childId = currentId; node._children.push({ "__id__": childId }); // 递归创建子节点 const childResult = await this.createNodeObject(childData, nodeId, prefabData, currentId); prefabData.push(childResult.node); currentId = childResult.nextId; // 子节点不需要PrefabInfo,只有根节点需要 // 子节点的_prefab应该设置为null childResult.node._prefab = null; console.log(`✅ 成功添加子节点: ${childName}`); } catch (error) { console.error(`处理子节点 ${childName} 时出错:`, error); } } } return { node, nextId: currentId }; } // 从节点数据中提取组件信息 private extractComponentsFromNode(nodeData: any): any[] { const components: any[] = []; // 从不同位置尝试获取组件数据 const componentSources = [ nodeData.__comps__, nodeData.components, nodeData.value?.__comps__, nodeData.value?.components ]; for (const source of componentSources) { if (Array.isArray(source)) { components.push(...source.filter(comp => comp && (comp.__type__ || comp.type))); break; // 找到有效的组件数组就退出 } } return components; } // 创建标准的组件对象 private createStandardComponentObject(componentData: any, nodeId: number, prefabInfoId: number): any { const componentType = componentData.__type__ || componentData.type; if (!componentType) { console.warn('组件缺少类型信息:', componentData); return null; } // 基础组件结构 - 基于官方预制体格式 const component: any = { "__type__": componentType, "_name": "", "_objFlags": 0, "node": { "__id__": nodeId }, "_enabled": this.getComponentPropertyValue(componentData, 'enabled', true), "__prefab": { "__id__": prefabInfoId } }; // 根据组件类型添加特定属性 this.addComponentSpecificProperties(component, componentData, componentType); // 添加_id属性 component._id = ""; return component; } // 添加组件特定的属性 private addComponentSpecificProperties(component: any, componentData: any, componentType: string): void { switch (componentType) { case 'cc.UITransform': this.addUITransformProperties(component, componentData); break; case 'cc.Sprite': this.addSpriteProperties(component, componentData); break; case 'cc.Label': this.addLabelProperties(component, componentData); break; case 'cc.Button': this.addButtonProperties(component, componentData); break; default: // 对于未知类型的组件,复制所有安全的属性 this.addGenericProperties(component, componentData); break; } } // UITransform组件属性 private addUITransformProperties(component: any, componentData: any): void { component._contentSize = this.createSizeObject( this.getComponentPropertyValue(componentData, 'contentSize', { width: 100, height: 100 }) ); component._anchorPoint = this.createVec2Object( this.getComponentPropertyValue(componentData, 'anchorPoint', { x: 0.5, y: 0.5 }) ); } // Sprite组件属性 private addSpriteProperties(component: any, componentData: any): void { component._visFlags = 0; component._customMaterial = null; component._srcBlendFactor = 2; component._dstBlendFactor = 4; component._color = this.createColorObject( this.getComponentPropertyValue(componentData, 'color', { r: 255, g: 255, b: 255, a: 255 }) ); component._spriteFrame = this.getComponentPropertyValue(componentData, 'spriteFrame', null); component._type = this.getComponentPropertyValue(componentData, 'type', 0); component._fillType = 0; component._sizeMode = this.getComponentPropertyValue(componentData, 'sizeMode', 1); component._fillCenter = this.createVec2Object({ x: 0, y: 0 }); component._fillStart = 0; component._fillRange = 0; component._isTrimmedMode = true; component._useGrayscale = false; component._atlas = null; } // Label组件属性 private addLabelProperties(component: any, componentData: any): void { component._visFlags = 0; component._customMaterial = null; component._srcBlendFactor = 2; component._dstBlendFactor = 4; component._color = this.createColorObject( this.getComponentPropertyValue(componentData, 'color', { r: 0, g: 0, b: 0, a: 255 }) ); component._string = this.getComponentPropertyValue(componentData, 'string', 'Label'); component._horizontalAlign = 1; component._verticalAlign = 1; component._actualFontSize = 20; component._fontSize = this.getComponentPropertyValue(componentData, 'fontSize', 20); component._fontFamily = 'Arial'; component._lineHeight = 40; component._overflow = 1; component._enableWrapText = false; component._font = null; component._isSystemFontUsed = true; component._isItalic = false; component._isBold = false; component._isUnderline = false; component._underlineHeight = 2; component._cacheMode = 0; } // Button组件属性 private addButtonProperties(component: any, componentData: any): void { component.clickEvents = []; component._interactable = true; component._transition = 2; component._normalColor = this.createColorObject({ r: 214, g: 214, b: 214, a: 255 }); component._hoverColor = this.createColorObject({ r: 211, g: 211, b: 211, a: 255 }); component._pressedColor = this.createColorObject({ r: 255, g: 255, b: 255, a: 255 }); component._disabledColor = this.createColorObject({ r: 124, g: 124, b: 124, a: 255 }); component._duration = 0.1; component._zoomScale = 1.2; } // 添加通用属性 private addGenericProperties(component: any, componentData: any): void { // 只复制安全的、已知的属性 const safeProperties = ['enabled', 'color', 'string', 'fontSize', 'spriteFrame', 'type', 'sizeMode']; for (const prop of safeProperties) { if (componentData.hasOwnProperty(prop)) { const value = this.getComponentPropertyValue(componentData, prop); if (value !== undefined) { component[`_${prop}`] = value; } } } } // 创建Vec2对象 private createVec2Object(data: any): any { return { "__type__": "cc.Vec2", "x": data?.x || 0, "y": data?.y || 0 }; } // 创建Vec3对象 private createVec3Object(data: any): any { return { "__type__": "cc.Vec3", "x": data?.x || 0, "y": data?.y || 0, "z": data?.z || 0 }; } // 创建Size对象 private createSizeObject(data: any): any { return { "__type__": "cc.Size", "width": data?.width || 100, "height": data?.height || 100 }; } // 创建Color对象 private createColorObject(data: any): any { return { "__type__": "cc.Color", "r": data?.r ?? 255, "g": data?.g ?? 255, "b": data?.b ?? 255, "a": data?.a ?? 255 }; } // 判断是否应该复制组件属性 private shouldCopyComponentProperty(key: string, value: any): boolean { // 跳过内部属性和已处理的属性 if (key.startsWith('__') || key === '_enabled' || key === 'node' || key === 'enabled') { return false; } // 跳过函数和undefined值 if (typeof value === 'function' || value === undefined) { return false; } return true; } // 获取组件属性值 - 重命名以避免冲突 private getComponentPropertyValue(componentData: any, propertyName: string, defaultValue?: any): any { // 尝试直接获取属性 if (componentData[propertyName] !== undefined) { return this.extractValue(componentData[propertyName]); } // 尝试从value属性中获取 if (componentData.value && componentData.value[propertyName] !== undefined) { return this.extractValue(componentData.value[propertyName]); } // 尝试带下划线前缀的属性名 const prefixedName = `_${propertyName}`; if (componentData[prefixedName] !== undefined) { return this.extractValue(componentData[prefixedName]); } return defaultValue; } // 提取属性值 private extractValue(data: any): any { if (data === null || data === undefined) { return data; } // 如果有value属性,优先使用value if (typeof data === 'object' && data.hasOwnProperty('value')) { return data.value; } // 如果是引用对象,保持原样 if (typeof data === 'object' && (data.__id__ !== undefined || data.__uuid__ !== undefined)) { return data; } return data; } 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 }; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DaxianLee/cocos-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server