```
feat(mcp-bridge): 添加场景和节点创建功能 - 新增 getNewSceneTemplate 函数用于生成标准场景模板 - 实现 create_scene 工具用于在 assets 目录下创建新场景文件 - 实现 create_prefab 工具用于将节点保存为预制体资源 - 实现 open_scene 工具用于在编辑器中打开指定场景 - 实现 create_node 工具用于在当前场景中创建新节点 - 在 scene-script.js 中添加 create-node 处理逻辑 - 支持创建不同类型的节点(空节点、精灵、标签等) - 添加 UUID 生成的多种兼容方案 ```
This commit is contained in:
178
main.js
178
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;
|
||||
|
||||
Reference in New Issue
Block a user