feat: 增加 SpriteFrame 智能识别与自动转换容错机制,当传入 Texture2D UUID 时自动读取 meta 转为对应的子资源 SpriteFrame

This commit is contained in:
火焰库拉
2026-02-28 11:47:40 +08:00
parent 957bac5e0c
commit a2fbc3568f
2 changed files with 57 additions and 22 deletions

View File

@@ -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` 在控制台明确提示类型错误,拦截强制赋值操作,彻底消除潜在的隐性崩溃。

View File

@@ -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;
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] 识别到材质类型为 SpriteFrame已将纹理 UUID 自动纠正为 SpriteFrame UUID: ${uuid}`,
`[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);
}
loadedAssets[idx] = 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 {
Editor.warn(`[scene-script] 未能为 ${key}[${idx}] 加载资源 ${uuid}: ${err}`);
loadedAssets[idx] = asset;
}
} else {
Editor.warn(
`[scene-script] 未能为 ${key}[${idx}] 加载资源 ${targetUuid}: ${err}`,
);
}
if (loadedCount === uuids.length) {