fix: 修复SpriteFrame赋值未获取真实UUID问题及面板日志被截断问题

This commit is contained in:
火焰库拉
2026-02-26 15:45:41 +08:00
parent 813cfa328e
commit d601ec8090
4 changed files with 85 additions and 6 deletions

View File

@@ -207,3 +207,17 @@
- **问题**: AI 偶尔会幻觉以为 `prefab_management`/`manage_script`/`manage_material`/`manage_texture`/`manage_shader` 的更新动作为 `save`,而不是标准定义的 `update``write`,导致抛出“未知的管理操作”报错。 - **问题**: AI 偶尔会幻觉以为 `prefab_management`/`manage_script`/`manage_material`/`manage_texture`/`manage_shader` 的更新动作为 `save`,而不是标准定义的 `update``write`,导致抛出“未知的管理操作”报错。
- **修复**: 在 `main.js` 所有这些管理工具的核心路由表中,为 `update``write` 操作均显式添加了 `case "save":` 作为后备兼容,极大地增强了不同大模型在不同提示词上下文环境下的操作容错率。 - **修复**: 在 `main.js` 所有这些管理工具的核心路由表中,为 `update``write` 操作均显式添加了 `case "save":` 作为后备兼容,极大地增强了不同大模型在不同提示词上下文环境下的操作容错率。
---
## 素材库与面板工具优化 (2026-02-26)
### 1. 材质与 SpriteFrame 赋值增强
- **问题**: 虽然支持了加载 Texture2D但如果 AI 传给 `Sprite.spriteFrame` 属性的是原生贴图 UUID`scene-script.js` 层面直接强转为 `new cc.SpriteFrame(asset)` 容易丢掉原图片配置信息,例如九宫格参数。
- **重构**: 新增底层 IPC 消息 `query-spriteframe-uuid`。现在当场景脚本识别到目标属性为精灵材质时,会通过跨进程向主进程查询目标 UUID 关联物理文件所属的 `.meta`。提取真实的 `subMetas` 内 SpriteFrame 的长 UUID 并返回。之后再基于该长 UUID 调用标准的 `loadAsset`。彻底保证资源结构的稳定性。
### 2. 测试面板输出全量解析
- **问题**: 为了防止大体积结构传递给 AI 时引发 OOM 截断崩溃,`main.js` 后台强行限制了所有日志向 Webview 输出的边界值 (默认不超过 100~500 字符),导致人类开发者从面板查看时无法追溯长内容如 Base64 和完整序列化返回值。
- **修复**: 拆分了拦截逻辑。剔除 `argsPreview``preview` 针对主面板渲染视图输出 `addLog` 时的预备阶段的 `substring` 剪裁。如今编辑器 UI 内将能看到完整、原生的调用参数和回调结果,而对于通过 HTTP 接口返还给 AI 的载荷依然安全拦截。

39
main.js
View File

@@ -695,6 +695,37 @@ const getToolsList = () => {
module.exports = { module.exports = {
"scene-script": "scene-script.js", "scene-script": "scene-script.js",
openTestPanel() {
Editor.Panel.open("mcp-bridge");
},
querySpriteFrameUuid(event, uuid) {
const fs = require("fs");
try {
const url = Editor.assetdb.uuidToUrl(uuid);
if (!url) {
return event.reply && event.reply(null, null);
}
const fspath = Editor.assetdb.urlToFspath(url);
if (!fspath) {
return event.reply && event.reply(null, null);
}
const metaPath = fspath + ".meta";
if (fs.existsSync(metaPath)) {
const meta = JSON.parse(fs.readFileSync(metaPath, "utf8"));
if (meta && meta.subMetas) {
const subKeys = Object.keys(meta.subMetas);
for (let k of subKeys) {
if (meta.subMetas[k].uuid) {
return event.reply && event.reply(null, meta.subMetas[k].uuid);
}
}
}
}
return event.reply && event.reply(null, null);
} catch (e) {
return event.reply && event.reply(null, null);
}
},
/** /**
* 插件加载时的回调 * 插件加载时的回调
*/ */
@@ -840,9 +871,6 @@ module.exports = {
if (args) { if (args) {
try { try {
argsPreview = typeof args === "object" ? JSON.stringify(args) : String(args); argsPreview = typeof args === "object" ? JSON.stringify(args) : String(args);
if (argsPreview.length > 500) {
argsPreview = argsPreview.substring(0, 500) + "...[Truncated]";
}
} catch (e) { } catch (e) {
argsPreview = "[无法序列化的参数]"; argsPreview = "[无法序列化的参数]";
} }
@@ -868,11 +896,10 @@ module.exports = {
} else { } else {
let preview = ""; let preview = "";
if (typeof result === "string") { if (typeof result === "string") {
preview = result.length > 100 ? result.substring(0, 100) + "..." : result; preview = result;
} else if (typeof result === "object") { } else if (typeof result === "object") {
try { try {
const jsonStr = JSON.stringify(result); preview = JSON.stringify(result);
preview = jsonStr.length > 100 ? jsonStr.substring(0, 100) + "..." : jsonStr;
} catch (e) { } catch (e) {
preview = "Object (Circular/Unserializable)"; preview = "Object (Circular/Unserializable)";
} }

View File

@@ -22,5 +22,17 @@
"auto-start": false, "auto-start": false,
"last-port": 3456 "last-port": 3456
} }
},
"messages": {
"open-test-panel": {
"methods": [
"openTestPanel"
]
},
"query-spriteframe-uuid": {
"methods": [
"querySpriteFrameUuid"
]
}
} }
} }

View File

@@ -447,9 +447,35 @@ module.exports = {
loadedCount++; loadedCount++;
return; return;
} }
// 从主进程查询是否这个 UUID 其实是一个纹理,而我们真正需要的是它的 SpriteFrame 子资源
if (propertyType === cc.SpriteFrame || key.toLowerCase().includes("sprite")) {
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}`,
);
}
} catch (e) {
// 忽略
}
}
cc.AssetLibrary.loadAsset(uuid, (err, asset) => { cc.AssetLibrary.loadAsset(uuid, (err, asset) => {
loadedCount++; loadedCount++;
if (!err && asset) { 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; loadedAssets[idx] = asset;
Editor.log(`[scene-script] 成功为 ${key}[${idx}] 加载资源: ${asset.name}`); Editor.log(`[scene-script] 成功为 ${key}[${idx}] 加载资源: ${asset.name}`);
} else { } else {