From f901bcc38fd5be9f987ac2d6ea03cec98aee4d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=81=AB=E7=84=B0=E5=BA=93=E6=8B=89?= Date: Thu, 29 Jan 2026 14:26:28 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(mcp-bridge):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E5=92=8C=E8=8A=82=E7=82=B9=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 getNewSceneTemplate 函数用于生成标准场景模板 - 实现 create_scene 工具用于在 assets 目录下创建新场景文件 - 实现 create_prefab 工具用于将节点保存为预制体资源 - 实现 open_scene 工具用于在编辑器中打开指定场景 - 实现 create_node 工具用于在当前场景中创建新节点 - 在 scene-script.js 中添加 create-node 处理逻辑 - 支持创建不同类型的节点(空节点、精灵、标签等) - 添加 UUID 生成的多种兼容方案 ``` --- main.js | 178 ++++++++++++++++++++++++++++++++++++++++++++++-- scene-script.js | 51 ++++++++++++++ 2 files changed, 225 insertions(+), 4 deletions(-) diff --git a/main.js b/main.js index 35eb99e..6655e69 100644 --- a/main.js +++ b/main.js @@ -1,6 +1,44 @@ "use strict"; const http = require("http"); +const path = require("path"); + +const getNewSceneTemplate = () => { + // 尝试获取 UUID 生成函数 + let newId = ""; + if (Editor.Utils && Editor.Utils.uuid) { + newId = Editor.Utils.uuid(); + } else if (Editor.Utils && Editor.Utils.UuidUtils && Editor.Utils.UuidUtils.uuid) { + newId = Editor.Utils.UuidUtils.uuid(); + } else { + // 兜底方案:如果找不到编辑器 API,生成一个随机字符串 + newId = Math.random().toString(36).substring(2, 15); + } + + const sceneData = [ + { + __type__: "cc.SceneAsset", + _name: "", + _objFlags: 0, + _native: "", + scene: { __id__: 1 }, + }, + { + __id__: 1, + __type__: "cc.Scene", + _name: "", + _objFlags: 0, + _parent: null, + _children: [], + _active: true, + _level: 0, + _components: [], + autoReleaseAssets: false, + _id: newId, + }, + ]; + return JSON.stringify(sceneData); +}; module.exports = { "scene-script": "scene-script.js", @@ -44,8 +82,6 @@ module.exports = { // 简易 MCP 桥接服务器 startMcpServer() { - const http = require("http"); - this.server = http.createServer((req, res) => { // 设置 CORS 方便调试 res.setHeader("Access-Control-Allow-Origin", "*"); @@ -110,9 +146,67 @@ module.exports = { required: ["id"], }, }, + { + name: "create_scene", + description: "在 assets 目录下创建一个新的场景文件", + inputSchema: { + type: "object", + properties: { + sceneName: { type: "string", description: "场景名称" }, + }, + required: ["sceneName"], + }, + }, + { + name: "create_prefab", + description: "将场景中的某个节点保存为预制体资源", + inputSchema: { + type: "object", + properties: { + nodeId: { type: "string", description: "节点 UUID" }, + prefabName: { type: "string", description: "预制体名称" }, + }, + required: ["nodeId", "prefabName"], + }, + }, + { + name: "open_scene", + description: "在编辑器中打开指定的场景文件", + inputSchema: { + type: "object", + properties: { + url: { + type: "string", + description: "场景资源路径,如 db://assets/NewScene.fire", + }, + }, + required: ["url"], + }, + }, + { + name: "create_node", + description: "在当前场景中创建一个新节点", + inputSchema: { + type: "object", + properties: { + name: { type: "string", description: "节点名称" }, + parentId: { + type: "string", + description: "父节点 UUID (可选,不传则挂在场景根部)", + }, + type: { + type: "string", + enum: ["empty", "sprite", "label"], + description: "节点预设类型", + }, + }, + required: ["name"], + }, + }, ]; - res.end(JSON.stringify({ tools })); - } else if (req.url === "/call-tool" && req.method === "POST") { + return res.end(JSON.stringify({ tools })); + } + if (req.url === "/call-tool" && req.method === "POST") { // 2. 执行工具逻辑 const { name, arguments: args } = JSON.parse(body); @@ -168,6 +262,82 @@ module.exports = { }), ); }); + } else if (name === "create_scene") { + const url = `db://assets/${args.sceneName}.fire`; + if (Editor.assetdb.exists(url)) { + return res.end( + JSON.stringify({ + content: [{ type: "text", text: "Error: Scene already exists" }], + }), + ); + } + + // 生成标准场景内容 + const sceneJson = getNewSceneTemplate(); + + Editor.assetdb.create(url, sceneJson, (err, results) => { + if (err) { + res.end( + JSON.stringify({ + content: [{ type: "text", text: "Error creating scene: " + err }], + }), + ); + } else { + res.end( + JSON.stringify({ + content: [{ type: "text", text: `Standard Scene created at ${url}` }], + }), + ); + } + }); + } else if (name === "create_prefab") { + const url = `db://assets/${args.prefabName}.prefab`; + // 2.4.x 创建预制体的 IPC 消息 + Editor.Ipc.sendToMain("scene:create-prefab", args.nodeId, url); + res.end( + JSON.stringify({ + content: [ + { type: "text", text: `Command sent: Creating prefab '${args.prefabName}'` }, + ], + }), + ); + } else if (name === "open_scene") { + const url = args.url; + // 1. 将 db:// 路径转换为 UUID + const uuid = Editor.assetdb.urlToUuid(url); + + if (uuid) { + // 2. 发送核心 IPC 消息给主进程 + // scene:open-by-uuid 是编辑器内置的场景打开逻辑 + Editor.Ipc.sendToMain("scene:open-by-uuid", uuid); + + res.end( + JSON.stringify({ + content: [ + { type: "text", text: `Success: Opening scene ${url} (UUID: ${uuid})` }, + ], + }), + ); + } else { + res.end( + JSON.stringify({ + content: [ + { type: "text", text: `Error: Could not find asset with URL ${url}` }, + ], + }), + ); + } + } else if (name === "create_node") { + // 转发给场景脚本处理 + Editor.Scene.callSceneScript("mcp-bridge", "create-node", args, (err, result) => { + res.end( + JSON.stringify({ + content: [ + { type: "text", text: err ? `Error: ${err}` : `Node created: ${result}` }, + ], + }), + ); + }); } } else { res.statusCode = 404; diff --git a/scene-script.js b/scene-script.js index 615b0ea..c4104a6 100644 --- a/scene-script.js +++ b/scene-script.js @@ -88,4 +88,55 @@ module.exports = { if (event.reply) event.reply(new Error("Node not found")); } }, + "create-node": function (event, args) { + const { name, parentId, type } = args; + const scene = cc.director.getScene(); + + let newNode = null; + + // 特殊处理:如果是创建 Canvas,自动设置好适配 + if (type === "canvas" || name === "Canvas") { + newNode = new cc.Node("Canvas"); + let canvas = newNode.addComponent(cc.Canvas); + newNode.addComponent(cc.Widget); + // 设置默认设计分辨率 + canvas.designResolution = cc.size(960, 640); + canvas.fitHeight = true; + // 自动在 Canvas 下创建一个 Camera + let camNode = new cc.Node("Main Camera"); + camNode.addComponent(cc.Camera); + camNode.parent = newNode; + } else if (type === "sprite") { + newNode = new cc.Node(name || "New Sprite"); + newNode.addComponent(cc.Sprite); + } else if (type === "label") { + newNode = new cc.Node(name || "New Label"); + let l = newNode.addComponent(cc.Label); + l.string = "New Label"; + } else { + newNode = new cc.Node(name || "New Node"); + } + + // 设置层级 + let parent = parentId ? cc.engine.getInstanceById(parentId) : scene; + if (newNode) { + newNode.parent = parent; + + // 坐标居中处理(如果是 Canvas 子节点) + if (parent.name === "Canvas") { + newNode.setPosition(0, 0); + } else { + newNode.setPosition(cc.v2(cc.winSize.width / 2, cc.winSize.height / 2)); + } + + // 通知编辑器刷新 + Editor.Ipc.sendToMain("scene:dirty"); + Editor.Ipc.sendToAll("scene:node-created", { + uuid: newNode.uuid, + parentUuid: parent.uuid, + }); + + if (event.reply) event.reply(null, newNode.uuid); + } + }, };