docs: 根据项目规则更新文档和注释,完善工具说明及最佳实践引导

This commit is contained in:
火焰库拉
2026-02-07 22:29:17 +08:00
parent 532cd08f9b
commit c274bd9db4
4 changed files with 96 additions and 55 deletions

View File

@@ -449,9 +449,9 @@ manageAsset(args, callback) {
| 任务 | 状态 | 描述 |
|------|------|------|
| 全局文件搜索 | ✅ 完成 | 实现 find_in_file 工具 |
| 撤销/重做支持 | ✅ 完成 | 实现 manage_undo 工具,并重构核心操作支持撤销 |
| 特效管理 | ✅ 完成 | 实现 manage_vfx 工具,支持粒子系统管理 |
| 文件哈希 | ✅ 完成 | 实现 get_sha 工具,支持文件 SHA-256 计算 |
| 动画管理 | ✅ 完成 | 实现 manage_animation 工具,支持动画播放与控制 |
### 11.4 第六阶段:可靠性与体验优化(已完成)
@@ -490,7 +490,8 @@ manageAsset(args, callback) {
| 脚本验证 | validate_script | ✅ 已实现 | |
| 撤销/重做 | undo/redo | ✅ 已实现 | |
| VFX 管理 | manage_vfx | ✅ 已实现 | |
| Git 集成 | get_sha | ❌ 未实现 | 优先级 |
| Git 集成 | get_sha | ✅ 已实现 | 虽然优先级中等,但已根据需求完成 |
| 动画管理 | manage_animation | ✅ 已实现 | 支持播放、暂停、停止及信息获取 |
| ScriptableObject | manage_so | ❌ 未实现 | 使用 AssetDB 替代 |
## 13. 风险评估

View File

@@ -117,13 +117,13 @@ Args: [你的项目所在盘符]:/[项目路径]/packages/mcp-bridge/mcp-proxy.j
### 6. open_scene
- **描述**: 在编辑器中打开指定的场景文件
- **描述**: 在编辑器中打开指定的场景文件。这是一个异步且耗时的操作,打开后请等待几秒。**重要提示**如果是新创建或空的场景请务必先创建并初始化基础节点Canvas/Camera
- **参数**:
- `url`: 场景资源路径,如 `db://assets/NewScene.fire`
### 7. create_node
- **描述**: 在当前场景中创建一个新节点
- **描述**: 在当前场景中创建一个新节点。对于 Canvas/Label 类型,会自动添加对应组件。
- **参数**:
- `name`: 节点名称
- `parentId`: 父节点 UUID (可选,不传则挂在场景根部)
@@ -131,13 +131,13 @@ Args: [你的项目所在盘符]:/[项目路径]/packages/mcp-bridge/mcp-proxy.j
### 8. manage_components
- **描述**: 管理节点组件
- **描述**: 管理节点组件。**重要最佳实践**:在执行 `add` 操作前,建议先通过 `get` 操作检查节点上是否已存在同类型的组件,以避免重复添加。
- **参数**:
- `nodeId`: 节点 UUID
- `action`: 操作类型(`add`, `remove`, `get`
- `componentType`: 组件类型,如 `cc.Sprite`(用于 `add` 操作)
- `componentId`: 组件 ID用于 `remove` 操作)
- `properties`: 组件属性(用于 `add` 操作)。**智能特性**如果属性期望组件类型但传入节点UUID插件会自动查找匹配组件。
- `action`: 操作类型(`add`, `remove`, `get`, `update`
- `componentType`: 组件类型,如 `cc.Sprite`(用于 `add`/`update` 操作)
- `componentId`: 组件 ID用于 `remove`/`update` 操作)
- `properties`: 组件属性(用于 `add`/`update` 操作)。**智能特性**:如果属性期望组件类型但传入节点 UUID插件会自动查找匹配组件。
### 9. manage_script
@@ -168,7 +168,7 @@ Args: [你的项目所在盘符]:/[项目路径]/packages/mcp-bridge/mcp-proxy.j
### 12. scene_management
- **描述**: 场景管理
- **描述**: 场景管理。创建并通过 `open_scene` 打开后,请务必初始化基础节点(如 Canvas 和 Camera
- **参数**:
- `action`: 操作类型(`create`, `delete`, `duplicate`, `get_info`
- `path`: 场景路径,如 `db://assets/scenes/NewScene.fire`

28
main.js
View File

@@ -121,7 +121,7 @@ const getToolsList = () => {
},
{
name: "create_scene",
description: "在 assets 目录下创建一个新的场景文件",
description: "在 assets 目录下创建一个新的场景文件。创建并通过 open_scene 打开后,请务必初始化基础节点(如 Canvas 和 Camera",
inputSchema: {
type: "object",
properties: {
@@ -144,7 +144,7 @@ const getToolsList = () => {
},
{
name: "open_scene",
description: "打开场景文件。注意:这是一个异步且耗时的操作,打开后请等待几秒再进行节点创建或保存操作。",
description: "打开场景文件。注意:这是一个异步且耗时的操作,打开后请等待几秒。重要如果是新创建或空的场景请务必先创建并初始化基础节点Canvas/Camera。",
inputSchema: {
type: "object",
properties: {
@@ -178,7 +178,7 @@ const getToolsList = () => {
},
{
name: "manage_components",
description: "管理节点组件",
description: "管理节点组件。重要提示:在执行 'add' 操作前,请务必先通过 'get' 操作检查节点上是否已存在同类型的组件,以避免重复添加导致逻辑异常。",
inputSchema: {
type: "object",
properties: {
@@ -713,7 +713,7 @@ module.exports = {
handleMcpCall(name, args, callback) {
if (isSceneBusy && (name === "save_scene" || name === "create_node")) {
return callback("Editor is busy (Processing Scene), please wait a moment.");
return callback("编辑器正忙(正在处理场景),请稍候。");
}
switch (name) {
case "get_selected_node":
@@ -730,16 +730,16 @@ module.exports = {
value: args.newName,
isSubProp: false
});
callback(null, `Node name updated to ${args.newName}`);
callback(null, `节点名称已更新为 ${args.newName}`);
break;
case "save_scene":
isSceneBusy = true;
addLog("info", "Preparing to save scene... Waiting for UI sync.");
addLog("info", "准备保存场景... 等待 UI 同步。");
Editor.Ipc.sendToPanel("scene", "scene:stash-and-save");
isSceneBusy = false;
addLog("info", "Safe Save completed.");
callback(null, "Scene saved successfully.");
addLog("info", "安全保存已完成。");
callback(null, "场景保存成功。");
break;
case "get_scene_hierarchy":
@@ -753,7 +753,7 @@ module.exports = {
addLog("error", `Transform update failed: ${err}`);
callback(err);
} else {
callback(null, "Transform updated");
callback(null, "变换信息已更新");
}
});
break;
@@ -761,17 +761,17 @@ module.exports = {
case "create_scene":
const sceneUrl = `db://assets/${args.sceneName}.fire`;
if (Editor.assetdb.exists(sceneUrl)) {
return callback("Scene already exists");
return callback("场景已存在");
}
Editor.assetdb.create(sceneUrl, getNewSceneTemplate(), (err) => {
callback(err, err ? null : `Standard Scene created at ${sceneUrl}`);
callback(err, err ? null : `标准场景已创建于 ${sceneUrl}`);
});
break;
case "create_prefab":
const prefabUrl = `db://assets/${args.prefabName}.prefab`;
Editor.Ipc.sendToMain("scene:create-prefab", args.nodeId, prefabUrl);
callback(null, `Command sent: Creating prefab '${args.prefabName}'`);
callback(null, `命令已发送:正在创建预制体 '${args.prefabName}'`);
break;
case "open_scene":
@@ -781,11 +781,11 @@ module.exports = {
Editor.Ipc.sendToMain("scene:open-by-uuid", openUuid);
setTimeout(() => {
isSceneBusy = false;
callback(null, `Success: Opening scene ${args.url}`);
callback(null, `成功:正在打开场景 ${args.url}`);
}, 2000);
} else {
isSceneBusy = false;
callback(`Could not find asset with URL ${args.url}`);
callback(`找不到路径为 ${args.url} 的资源`);
}
break;

View File

@@ -26,11 +26,11 @@ module.exports = {
});
if (event.reply) {
event.reply(null, `Node ${id} updated to ${value}`);
event.reply(null, `节点 ${id} 已更新为 ${value}`);
}
} else {
if (event.reply) {
event.reply(new Error("Scene Script: Node not found " + id));
event.reply(new Error("场景脚本:找不到节点 " + id));
}
}
},
@@ -102,10 +102,9 @@ module.exports = {
Editor.Ipc.sendToAll("scene:node-changed", { uuid: id });
Editor.log(`[scene-script] Update complete. New Pos: (${node.x}, ${node.y})`);
if (event.reply) event.reply(null, "Transform updated");
if (event.reply) event.reply(null, "变换信息已更新");
} else {
Editor.error(`[scene-script] Node not found: ${id}`);
if (event.reply) event.reply(new Error("Node not found"));
if (event.reply) event.reply(new Error("找不到节点"));
}
},
"create-node": function (event, args) {
@@ -172,6 +171,50 @@ module.exports = {
const compClass = component.constructor;
for (const [key, value] of Object.entries(props)) {
// 【核心修复】专门处理各类事件属性 (ClickEvents, ScrollEvents 等)
const isEventProp = Array.isArray(value) && (key.toLowerCase().endsWith('events') || key === 'clickEvents');
if (isEventProp) {
const eventHandlers = [];
for (const item of value) {
if (typeof item === 'object' && (item.target || item.component || item.handler)) {
const handler = new cc.Component.EventHandler();
// 解析 Target Node
if (item.target) {
let targetNode = null;
if (typeof item.target === 'string') {
targetNode = cc.engine.getInstanceById(item.target);
if (!targetNode && Editor.Utils && Editor.Utils.UuidUtils) {
try {
const decompressed = Editor.Utils.UuidUtils.decompressUuid(item.target);
targetNode = cc.engine.getInstanceById(decompressed);
} catch (e) { }
}
} else if (item.target instanceof cc.Node) {
targetNode = item.target;
}
if (targetNode) {
handler.target = targetNode;
Editor.log(`[scene-script] Resolved event target: ${targetNode.name}`);
}
}
if (item.component) handler.component = item.component;
if (item.handler) handler.handler = item.handler;
if (item.customEventData !== undefined) handler.customEventData = String(item.customEventData);
eventHandlers.push(handler);
} else {
// 如果不是对象,原样保留
eventHandlers.push(item);
}
}
component[key] = eventHandlers;
continue; // 处理完事件数组,跳出本次循环
}
// 检查属性是否存在
if (component[key] !== undefined) {
let finalValue = value;
@@ -207,7 +250,6 @@ module.exports = {
// 尝试获取属性定义类型
let typeName = null;
// 优先尝试 getClassAttrs (Cocos 2.x editor environment)
if (cc.Class.Attr.getClassAttrs) {
const attrs = cc.Class.Attr.getClassAttrs(compClass);
@@ -230,7 +272,6 @@ module.exports = {
}
if (typeName && (typeName.prototype instanceof cc.Component || typeName === cc.Component)) {
// 这是一个组件属性
const targetComp = targetNode.getComponent(typeName);
if (targetComp) {
@@ -260,16 +301,15 @@ module.exports = {
}
};
if (!node) {
if (event.reply) event.reply(new Error("Node not found"));
if (event.reply) event.reply(new Error("找不到节点"));
return;
}
switch (action) {
case "add":
if (!componentType) {
if (event.reply) event.reply(new Error("Component type is required"));
if (event.reply) event.reply(new Error("必须提供组件类型"));
return;
}
@@ -285,7 +325,7 @@ module.exports = {
}
if (!compClass) {
if (event.reply) event.reply(new Error(`Component type not found: ${componentType}`));
if (event.reply) event.reply(new Error(`找不到组件类型: ${componentType}`));
return;
}
@@ -300,15 +340,15 @@ module.exports = {
Editor.Ipc.sendToMain("scene:dirty");
Editor.Ipc.sendToAll("scene:node-changed", { uuid: nodeId });
if (event.reply) event.reply(null, `Component ${componentType} added`);
if (event.reply) event.reply(null, `组件 ${componentType} 已添加`);
} catch (err) {
if (event.reply) event.reply(new Error(`Failed to add component: ${err.message}`));
if (event.reply) event.reply(new Error(`添加组件失败: ${err.message}`));
}
break;
case "remove":
if (!componentId) {
if (event.reply) event.reply(new Error("Component ID is required"));
if (event.reply) event.reply(new Error("必须提供组件 ID"));
return;
}
@@ -328,12 +368,12 @@ module.exports = {
node.removeComponent(component);
Editor.Ipc.sendToMain("scene:dirty");
Editor.Ipc.sendToAll("scene:node-changed", { uuid: nodeId });
if (event.reply) event.reply(null, "Component removed");
if (event.reply) event.reply(null, "组件已移除");
} else {
if (event.reply) event.reply(new Error("Component not found"));
if (event.reply) event.reply(new Error("找不到组件"));
}
} catch (err) {
if (event.reply) event.reply(new Error(`Failed to remove component: ${err.message}`));
if (event.reply) event.reply(new Error(`移除组件失败: ${err.message}`));
}
break;
@@ -387,7 +427,7 @@ module.exports = {
if (event.reply) event.reply(new Error(`Component not found (Type: ${componentType}, ID: ${componentId})`));
}
} catch (err) {
if (event.reply) event.reply(new Error(`Failed to update component: ${err.message}`));
if (event.reply) event.reply(new Error(`更新组件失败: ${err.message}`));
}
break;
@@ -446,12 +486,12 @@ module.exports = {
});
if (event.reply) event.reply(null, components);
} catch (err) {
if (event.reply) event.reply(new Error(`Failed to get components: ${err.message}`));
if (event.reply) event.reply(new Error(`获取组件失败: ${err.message}`));
}
break;
default:
if (event.reply) event.reply(new Error(`Unknown component action: ${action}`));
if (event.reply) event.reply(new Error(`未知的组件操作类型: ${action}`));
break;
}
},
@@ -483,7 +523,7 @@ module.exports = {
}
if (!prefabUuid) {
if (event.reply) event.reply(new Error("Prefab UUID is required."));
if (event.reply) event.reply(new Error("必须提供预制体 UUID。"));
return;
}
@@ -491,14 +531,14 @@ module.exports = {
// 如果是旧版,可能需要 cc.loader.load({uuid: ...}),但在 2.4 环境下 assetManager 更推荐
cc.assetManager.loadAny(prefabUuid, (err, prefab) => {
if (err) {
if (event.reply) event.reply(new Error(`Failed to load prefab: ${err.message}`));
if (event.reply) event.reply(new Error(`加载预制体失败: ${err.message}`));
return;
}
// 实例化预制体
const instance = cc.instantiate(prefab);
if (!instance) {
if (event.reply) event.reply(new Error("Failed to instantiate prefab"));
if (event.reply) event.reply(new Error("实例化预制体失败"));
return;
}
@@ -518,9 +558,9 @@ module.exports = {
});
}, 10);
if (event.reply) event.reply(null, `Prefab instantiated successfully with UUID: ${instance.uuid}`);
if (event.reply) event.reply(null, `预制体实例化成功,UUID: ${instance.uuid}`);
} else {
if (event.reply) event.reply(new Error("Parent node not found"));
if (event.reply) event.reply(new Error("找不到父节点"));
}
});
},
@@ -610,9 +650,9 @@ module.exports = {
Editor.Ipc.sendToAll("scene:node-deleted", { uuid: uuid });
}, 10);
if (event.reply) event.reply(null, `Node ${uuid} deleted`);
if (event.reply) event.reply(null, `节点 ${uuid} 已删除`);
} else {
if (event.reply) event.reply(new Error(`Node not found: ${uuid}`));
if (event.reply) event.reply(new Error(`找不到节点: ${uuid}`));
}
},
@@ -715,9 +755,9 @@ module.exports = {
Editor.Ipc.sendToMain("scene:dirty");
Editor.Ipc.sendToAll("scene:node-changed", { uuid: nodeId });
if (event.reply) event.reply(null, "VFX updated");
if (event.reply) event.reply(null, "特效已更新");
} else {
if (event.reply) event.reply(new Error("Node not found"));
if (event.reply) event.reply(new Error("找不到节点"));
}
} else if (action === "get_info") {
@@ -747,7 +787,7 @@ module.exports = {
if (event.reply) event.reply(new Error("Node not found"));
}
} else {
if (event.reply) event.reply(new Error(`Unknown VFX action: ${action}`));
if (event.reply) event.reply(new Error(`未知的特效操作类型: ${action}`));
}
},