diff --git a/docs/UPDATE_LOG.md b/docs/UPDATE_LOG.md index efcaa39..2cd61de 100644 --- a/docs/UPDATE_LOG.md +++ b/docs/UPDATE_LOG.md @@ -295,3 +295,13 @@ - **问题**: `instantiate-prefab` 中查找父节点直接调用 `cc.engine.getInstanceById(parentId)`,绕过了 `findNode` 函数的压缩 UUID 解压与兼容逻辑。 - **修复**: 统一改用 `findNode(parentId)`,确保所有场景操作对压缩和非压缩 UUID 格式的兼容性一致。 + +--- + +## 编辑器体验与容错增强 (2026-02-28) + +### 1. SpriteFrame 智能识别与自动转换 + +- **问题**: 当 AI 大模型尝试给 `cc.Sprite` 等组件的 `spriteFrame` 属性赋值时,常常会错误传递为其父级 `Texture2D` (原图) 的 UUID。Cocos 引擎由于类型不匹配会导致赋值无效,且静默失败(或陷入 IPC 死锁导致编辑器卡死)。 +- **优化**: 在 `scene-script.js` 中的 `applyProperties` 环节新增了类型容错机制。当识别到传入的 UUID 对应 `Texture2D` 但该属性(例如含 `sprite` 关键字)需要 `SpriteFrame` 时,脚本会利用 Node.js `fs` 直接读取对应的 `.meta` 文件,提取出实际子资源 (`SpriteFrame`) 的正确 UUID,从而实现自动转换与安全赋值。 +- **降级**: 若自动转换失败(如 `meta` 结构改变或读取失败),则会通过 `Editor.warn` 在控制台明确提示类型错误,拦截强制赋值操作,彻底消除潜在的隐性崩溃。 diff --git a/src/scene-script.js b/src/scene-script.js index 8562b4c..f66244c 100644 --- a/src/scene-script.js +++ b/src/scene-script.js @@ -437,43 +437,68 @@ module.exports = { return; } + const fs = require("fs"); + const path = require("path"); + uuids.forEach((uuid, idx) => { if (typeof uuid !== "string" || uuid.length < 10) { loadedCount++; return; } - // 从主进程查询是否这个 UUID 其实是一个纹理,而我们真正需要的是它的 SpriteFrame 子资源 - if (propertyType === cc.SpriteFrame || key.toLowerCase().includes("sprite")) { + // 尝试进行自动转换:如果这是原图,且需要 SpriteFrame,自动读取其 meta 获取子 UUID + const needsSpriteFrame = + propertyType === cc.SpriteFrame || key.toLowerCase().includes("sprite"); + + let targetUuid = uuid; + + if (needsSpriteFrame && Editor && Editor.assetdb && Editor.assetdb.remote) { try { - const sfUuid = Editor.Ipc.sendToMainSync( - "mcp-bridge:query-spriteframe-uuid", - uuid, - ); - if (sfUuid && sfUuid !== uuid) { - uuid = sfUuid; - Editor.log( - `[scene-script] 识别到材质类型为 SpriteFrame,已将纹理 UUID 自动纠正为 SpriteFrame UUID: ${uuid}`, - ); + const fspath = Editor.assetdb.remote.uuidToFspath(uuid); + if (fspath) { + const metaPath = fspath + ".meta"; + if (fs.existsSync(metaPath)) { + const metaContent = fs.readFileSync(metaPath, "utf-8"); + const metaObj = JSON.parse(metaContent); + // Creator 2.x 图片的 subMetas 里通常存储着以图片名命名的 spriteFrame + if (metaObj && metaObj.subMetas) { + const subKeys = Object.keys(metaObj.subMetas); + // 如果有子 spriteFrame,提取它的 uuid + for (const subKey of subKeys) { + const subMeta = metaObj.subMetas[subKey]; + if (subMeta && (subMeta.uuid || subMeta.rawTextureUuid)) { + targetUuid = subMeta.uuid; + Editor.log( + `[scene-script] 自动转换 UUID: ${uuid} (Texture2D) -> ${targetUuid} (SpriteFrame)`, + ); + break; + } + } + } + } } - } catch (e) { - // 忽略 + } catch (err) { + Editor.log(`[scene-script] 读取 meta 失败: ${err.message}`); } } - cc.AssetLibrary.loadAsset(uuid, (err, asset) => { + cc.AssetLibrary.loadAsset(targetUuid, (err, asset) => { loadedCount++; if (!err && asset) { - // 自动处理 Texture2D 到 SpriteFrame 的转换,防止由于传错了图片 UUID 导致赋值失效 - if ( - asset instanceof cc.Texture2D && - (propertyType === cc.SpriteFrame || key.toLowerCase().includes("sprite")) - ) { - asset = new cc.SpriteFrame(asset); + // 判断是否依然是 Texture2D,并且需要 SpriteFrame + const stillIsTexture = asset instanceof cc.Texture2D && needsSpriteFrame; + + if (stillIsTexture) { + Editor.warn( + `[scene-script] 拒绝为 ${key}[${idx}] 赋值:给 SpriteFrame 属性传递了 Texture2D (原图) 的 UUID ${targetUuid}。自动转换失败,请使用正确的 SpriteFrame UUID。`, + ); + } else { + loadedAssets[idx] = asset; } - loadedAssets[idx] = asset; } else { - Editor.warn(`[scene-script] 未能为 ${key}[${idx}] 加载资源 ${uuid}: ${err}`); + Editor.warn( + `[scene-script] 未能为 ${key}[${idx}] 加载资源 ${targetUuid}: ${err}`, + ); } if (loadedCount === uuids.length) {