文档与功能更新 (Texture & Transform):增强 manage_texture 和 update_node_transform 工具,修复属性应用 bug,并更新相关文档
This commit is contained in:
16
README.md
16
README.md
@@ -112,6 +112,7 @@ Args: [你的项目所在盘符]:/[项目路径]/packages/mcp-bridge/mcp-proxy.j
|
||||
- **参数**:
|
||||
- `id`: 节点 UUID
|
||||
- `x`, `y`: 坐标
|
||||
- `width`, `height`: 节点宽高 (新增支持)
|
||||
- `scaleX`, `scaleY`: 缩放值
|
||||
- `color`: HEX 颜色代码(如 #FF0000)
|
||||
- **重要提示**: 执行前必须调用 `get_scene_hierarchy` 确保 `id` 有效,防止操作不存在的节点。
|
||||
@@ -246,12 +247,15 @@ Args: [你的项目所在盘符]:/[项目路径]/packages/mcp-bridge/mcp-proxy.j
|
||||
|
||||
- **描述**: 管理纹理
|
||||
- **参数**:
|
||||
- `action`: 操作类型(`create`, `delete`, `get_info`)
|
||||
- `path`: 纹理路径,如 `db://assets/textures/NewTexture.png`
|
||||
- `properties`: 纹理属性(用于 `create` 操作)
|
||||
- `width`: 宽度
|
||||
- `height`: 高度
|
||||
- `native`: 原生路径
|
||||
- `action`: 操作类型(`create`, `delete`, `get_info`, `update`)
|
||||
- `path`: 纹理路径,如 `db://assets/textures/NewTexture.png`
|
||||
- `properties`: 纹理属性(用于 `create`/`update` 操作)
|
||||
- `type`: 纹理类型(如 `sprite`, `texture`, `raw`)(用于 `update`)
|
||||
- `border`: 九宫格边距数组 `[top, bottom, left, right]` (用于 `update`,仅当 type 为 sprite 时有效)
|
||||
- `subMetas`: (内部使用)
|
||||
- `width`: 宽度 (用于 `create` 生成占位图)
|
||||
- `height`: 高度 (用于 `create` 生成占位图)
|
||||
- `native`: 原生路径
|
||||
|
||||
### 18. execute_menu_item
|
||||
|
||||
|
||||
@@ -48,5 +48,18 @@
|
||||
|
||||
---
|
||||
|
||||
## 四、 总结
|
||||
## 四、 纹理与节点变换增强 (Texture & Transform Updates)
|
||||
|
||||
### 1. `manage_texture` 工具增强
|
||||
- **新增 `update` 操作**: 支持修改现有纹理的类型(如 `texture` -> `sprite`)和九宫格边距 (`border`)。
|
||||
- **Meta 加载健壮性**: 修复了 `Editor.assetdb.loadMeta` 在某些情况下返回空值的问题,增加了读取文件系统 `.meta` 文件的 Fallback 机制。
|
||||
- **多版本兼容**: 针对 Cocos Creator 不同版本 `.meta` 文件结构差异(数组 vs 独立字段),实现了对 9-slice 数据写入的自动兼容。
|
||||
|
||||
### 2. `update_node_transform` 工具增强
|
||||
- **新增尺寸控制**: 添加了 `width` 和 `height` 参数,允许 AI 直接调整节点大小(对于测试九宫格拉伸效果至关重要)。
|
||||
|
||||
### 3. 关键 Bug 修复
|
||||
- **属性批量应用中断**: 修复了 `scene-script.js` 中 `applyProperties` 函数在处理 Asset 类型属性时错误使用 `return` 导致后续属性(如 `type`)被忽略的问题。现在改为 `continue`,确保所有属性都能被正确应用。
|
||||
|
||||
## 五、 总结
|
||||
本次更新不仅修复了制约生产力的材质与资源同步 bug,还通过引入 `manage_shader` 和全方位的文档中文化,极大提升了开发者(及 AI 助手)在 Cocos Creator 2.4.x 环境下的操作体验。
|
||||
|
||||
167
main.js
167
main.js
@@ -129,6 +129,8 @@ const getToolsList = () => {
|
||||
id: { type: "string", description: "节点 UUID" },
|
||||
x: { type: "number" },
|
||||
y: { type: "number" },
|
||||
width: { type: "number" },
|
||||
height: { type: "number" },
|
||||
scaleX: { type: "number" },
|
||||
scaleY: { type: "number" },
|
||||
color: { type: "string", description: "HEX 颜色代码如 #FF0000" },
|
||||
@@ -354,7 +356,7 @@ const getToolsList = () => {
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
action: { type: "string", enum: ["create", "delete", "get_info"], description: "操作类型" },
|
||||
action: { type: "string", enum: ["create", "delete", "get_info", "update"], description: "操作类型" },
|
||||
path: { type: "string", description: "纹理路径,如 db://assets/textures/NewTexture.png" },
|
||||
properties: { type: "object", description: "纹理属性" },
|
||||
},
|
||||
@@ -1630,16 +1632,65 @@ CCProgram fs %{
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
}
|
||||
// 【修复】Cocos 2.4.x 无法直接用 Editor.assetdb.create 创建带后缀的纹理(会校验内容)
|
||||
// 我们需要先物理写入一个 1x1 的透明图片,再刷新数据库
|
||||
const base64Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==";
|
||||
|
||||
// 1. 准备文件内容 (优先使用 properties.content,否则使用默认 1x1)
|
||||
let base64Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==";
|
||||
if (properties && properties.content) {
|
||||
base64Data = properties.content;
|
||||
}
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
|
||||
try {
|
||||
// 2. 写入物理文件
|
||||
fs.writeFileSync(absolutePath, buffer);
|
||||
Editor.assetdb.refresh(path, (err) => {
|
||||
|
||||
// 3. 刷新该资源以生成 Meta
|
||||
Editor.assetdb.refresh(path, (err, results) => {
|
||||
if (err) return callback(err);
|
||||
callback(null, `Texture created and refreshed at ${path}`);
|
||||
|
||||
// 4. 如果有 9-slice 设置,更新 Meta
|
||||
if (properties && (properties.border || properties.type)) {
|
||||
const uuid = Editor.assetdb.urlToUuid(path);
|
||||
if (!uuid) return callback(null, `Texture created but UUID not found immediately.`);
|
||||
|
||||
// 稍微延迟确保 Meta 已生成
|
||||
setTimeout(() => {
|
||||
const meta = Editor.assetdb.loadMeta(uuid);
|
||||
if (meta) {
|
||||
let changed = false;
|
||||
if (properties.type) {
|
||||
meta.type = properties.type;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// 设置 9-slice (border)
|
||||
// 注意:Cocos 2.4 纹理 Meta 中 subMetas 下通常有一个与纹理同名的 key (或者主要的一个 key)
|
||||
if (properties.border) {
|
||||
// 确保类型是 sprite
|
||||
meta.type = 'sprite';
|
||||
|
||||
// 找到 SpriteFrame 的 subMeta
|
||||
const subKeys = Object.keys(meta.subMetas);
|
||||
if (subKeys.length > 0) {
|
||||
const subMeta = meta.subMetas[subKeys[0]];
|
||||
subMeta.border = properties.border; // [top, bottom, left, right]
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
Editor.assetdb.saveMeta(uuid, JSON.stringify(meta), (err) => {
|
||||
if (err) Editor.warn(`Failed to save meta for ${path}: ${err}`);
|
||||
callback(null, `Texture created and meta updated at ${path}`);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback(null, `Texture created at ${path}`);
|
||||
}, 100);
|
||||
} else {
|
||||
callback(null, `Texture created at ${path}`);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
callback(`Failed to write texture file: ${e.message}`);
|
||||
@@ -1662,6 +1713,110 @@ CCProgram fs %{
|
||||
callback(`Texture not found: ${path}`);
|
||||
}
|
||||
break;
|
||||
case "update":
|
||||
if (!Editor.assetdb.exists(path)) {
|
||||
return callback(`Texture not found at ${path}`);
|
||||
}
|
||||
const uuid = Editor.assetdb.urlToUuid(path);
|
||||
let meta = Editor.assetdb.loadMeta(uuid);
|
||||
|
||||
// Fallback: 如果 Editor.assetdb.loadMeta 失败 (API 偶尔不稳定),尝试直接读取文件系统中的 .meta 文件
|
||||
if (!meta) {
|
||||
try {
|
||||
const fspath = Editor.assetdb.urlToFspath(path);
|
||||
const metaPath = fspath + ".meta";
|
||||
if (fs.existsSync(metaPath)) {
|
||||
const metaContent = fs.readFileSync(metaPath, "utf-8");
|
||||
meta = JSON.parse(metaContent);
|
||||
addLog("info", `[manage_texture] Loaded meta from fs fallback: ${metaPath}`);
|
||||
}
|
||||
} catch (e) {
|
||||
addLog("warn", `[manage_texture] Meta fs fallback failed: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!meta) {
|
||||
return callback(`Failed to load meta for ${path}`);
|
||||
}
|
||||
|
||||
let changed = false;
|
||||
if (properties) {
|
||||
// 更新类型
|
||||
if (properties.type) {
|
||||
if (meta.type !== properties.type) {
|
||||
meta.type = properties.type;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 9-slice border
|
||||
if (properties.border) {
|
||||
// 确保类型是 sprite
|
||||
if (meta.type !== 'sprite') {
|
||||
meta.type = 'sprite';
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// 找到 SubMeta
|
||||
// Cocos Meta 结构: { subMetas: { "textureName": { ... } } }
|
||||
// 注意:Cocos 2.x 的 meta 结构因版本而异,旧版可能使用 border: [t, b, l, r] 数组,
|
||||
// 而新版 (如 2.3.x+) 通常使用 borderTop, borderBottom 等独立字段。
|
||||
// 此处逻辑实现了兼容性处理。
|
||||
const subKeys = Object.keys(meta.subMetas);
|
||||
if (subKeys.length > 0) {
|
||||
const subMeta = meta.subMetas[subKeys[0]];
|
||||
const newBorder = properties.border; // [top, bottom, left, right]
|
||||
|
||||
// 方式 1: standard array style
|
||||
if (subMeta.border !== undefined) {
|
||||
const oldBorder = subMeta.border;
|
||||
if (!oldBorder ||
|
||||
oldBorder[0] !== newBorder[0] ||
|
||||
oldBorder[1] !== newBorder[1] ||
|
||||
oldBorder[2] !== newBorder[2] ||
|
||||
oldBorder[3] !== newBorder[3]) {
|
||||
subMeta.border = newBorder;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// 方式 2: individual fields style (common in 2.3.x)
|
||||
else if (subMeta.borderTop !== undefined) {
|
||||
// top, bottom, left, right
|
||||
if (subMeta.borderTop !== newBorder[0] ||
|
||||
subMeta.borderBottom !== newBorder[1] ||
|
||||
subMeta.borderLeft !== newBorder[2] ||
|
||||
subMeta.borderRight !== newBorder[3]) {
|
||||
|
||||
subMeta.borderTop = newBorder[0];
|
||||
subMeta.borderBottom = newBorder[1];
|
||||
subMeta.borderLeft = newBorder[2];
|
||||
subMeta.borderRight = newBorder[3];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// 方式 3: 如果都没有,尝试写入 individual fields
|
||||
else {
|
||||
subMeta.borderTop = newBorder[0];
|
||||
subMeta.borderBottom = newBorder[1];
|
||||
subMeta.borderLeft = newBorder[2];
|
||||
subMeta.borderRight = newBorder[3];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
// 使用 saveMeta 或者 fs 写入
|
||||
// 为了安全,如果 loadMeta 失败了,safeMeta 可能也会失败,所以这里尽量用 API,不行再 fallback (暂且只用 API)
|
||||
Editor.assetdb.saveMeta(uuid, JSON.stringify(meta), (err) => {
|
||||
if (err) return callback(`Failed to save meta: ${err}`);
|
||||
callback(null, `Texture updated at ${path}`);
|
||||
});
|
||||
} else {
|
||||
callback(null, `No changes needed for ${path}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
callback(`Unknown texture action: ${action}`);
|
||||
break;
|
||||
|
||||
@@ -112,13 +112,22 @@ module.exports = {
|
||||
Editor.log(`[scene-script] Node found: ${node.name}, Current Pos: (${node.x}, ${node.y})`);
|
||||
|
||||
if (x !== undefined) {
|
||||
node.x = Number(x); // 强制转换确保类型正确
|
||||
node.x = Number(x);
|
||||
Editor.log(`[scene-script] Set x to ${node.x}`);
|
||||
}
|
||||
if (y !== undefined) {
|
||||
node.y = Number(y);
|
||||
Editor.log(`[scene-script] Set y to ${node.y}`);
|
||||
}
|
||||
// [新增] 支持设置节点宽高 (用于测试 9-slice 等需要调整尺寸的情况)
|
||||
if (args.width !== undefined) {
|
||||
node.width = Number(args.width);
|
||||
Editor.log(`[scene-script] Set width to ${node.width}`);
|
||||
}
|
||||
if (args.height !== undefined) {
|
||||
node.height = Number(args.height);
|
||||
Editor.log(`[scene-script] Set height to ${node.height}`);
|
||||
}
|
||||
if (scaleX !== undefined) node.scaleX = Number(scaleX);
|
||||
if (scaleY !== undefined) node.scaleY = Number(scaleY);
|
||||
if (color) {
|
||||
@@ -368,7 +377,9 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
});
|
||||
return; // 跳过后续的直接赋值
|
||||
// 【重要修复】使用 continue 而不是 return,确保处理完 Asset 属性后
|
||||
// 还能继续处理后续的普通属性 (如 type, sizeMode 等)
|
||||
continue;
|
||||
} else if (propertyType && (propertyType.prototype instanceof cc.Component || propertyType === cc.Component || propertyType === cc.Node)) {
|
||||
// 2. 处理节点或组件引用
|
||||
const targetNode = findNode(value);
|
||||
|
||||
Reference in New Issue
Block a user