- 将 src/IpcManager.ts 和 src/IpcUi.ts 手动转写为干净的 JavaScript,消除编译产物中的 polyfill 代码 - 移动 main.js、scene-script.js、mcp-proxy.js 至 src/ 目录,统一源码管理 - 移动 DEVELOPMENT.md、IPC_MESSAGES.md、UPDATE_LOG.md、注意事项.md 至 docs/ 目录 - 删除 dist/ 编译产物目录和 tsconfig.json - 更新 package.json 入口路径、所有模块引用路径 - 更新 README.md 和 DEVELOPMENT.md 中的架构说明、文件路径引用和项目规范 - 更新 .gitignore 启用 dist 忽略规则
10 KiB
10 KiB
Cocos Creator 插件开发注意事项
1. 资源与预制体管理
1.1 禁止手动干预 .meta 文件
- 问题:手动使用
manage_asset创建或修改以.meta结尾的文件会导致编辑器报错(如Invalid assetpath, must not use .meta as suffix)。 - 原因:Cocos Creator 资源数据库会自动识别新资源并生成对应的
.meta文件。外部强行介入会破坏资源索引一致性。 - 最佳实践:始终使用高级资源工具(如
prefab_management)来处理资源,让编辑器自行管理.meta文件。
1.2 预制体的正确创建流程
- 推荐工具:使用
prefab_management的create操作。 - 核心逻辑:该工具会同步处理节点的序列化、db 路径映射以及资源刷新(Refresh),确保预制体及其配套的
.meta文件原子化生成。
2. 脚本属性(Inspector)关联
2.1 路径与 UUID 的区别
- 问题:在 Inspector 面板中,脚本属性(如
cc.Prefab或cc.Node)若通过路径关联,经常会出现 "Type Error"。 - 原因:编辑器 Inspector 期望获得的是引擎内部的对象引用。虽然
db://路径可以指向文件,但在属性赋值层面,编辑器无法自动将字符串路径转换为对应的类实例。 - 最佳实践:
- 通过
manage_asset -> get_info获取资源的 UUID。 - 在调用
manage_components -> update时,优先使用 UUID 进行赋值。 - UUID 是底层唯一标识,能确保编辑器精准识别资源类型并正确完成引用链接。
- 通过
3. 场景同步与编辑器状态
3.1 刷新与编译
- 注意事项:创建新脚本后,必须调用
manage_editor -> refresh_editor或等待几秒钟以触发编译。 - 风险:在脚本编译完成前尝试将其作为组件添加到节点,会导致
添加组件失败: Cannot read property 'constructor' of null或找不到脚本组件的问题。
3.2 节点删除 (IPC 协议)
- 正确协议:应使用
Editor.Ipc.sendToMain('scene:delete-nodes', uuid_or_array)。 - 注意:不要误用
scene:delete-selected,因为它在某些版本的编辑器底层不接受参数或行为不一致。 - 技巧:在
mcp-bridge中,调用execute_menu_item("delete-node:NODE_UUID")会走场景脚本的直连删除,而execute_menu_item("Delete")则会走主进程的scene:delete-nodes并自动带上选中的节点。
4. MCP Bridge 源码补丁说明
4.1 属性解析增强 (Asset 序列化修复)
- 改进点:在
scene-script.js的applyProperties中通过cc.AssetLibrary.loadAsset解决了资源属性在 Inspector 报错 "Type Error" 的问题。 - 原理:
- 问题根源:在场景进程中直接将 UUID 字符串赋给资源属性(如
comp.prefab = "uuid"),会导致.fire文件将其保存为纯字符串而非对象格式。编辑器 Inspector 期望的是资源引用结构{ "__uuid__": "..." },类型不匹配产生 Type Error。 - 修复逻辑:
- 真实加载:使用
cc.AssetLibrary.loadAsset(uuid, callback)在场景进程中异步加载真实的资源实例。 - 实例赋值:在回调中将加载到的
asset对象赋予组件(component[key] = asset),这确保了场景保存时能生成正确的序列化对象。 - UI 同步:同步发送 IPC
scene:set-property,使用{ uuid: value }格式通知编辑器面板更新 Inspector UI。
- 真实加载:使用
- Node/Component: 对于节点 or 组件引用,通过
findNode查找实例并直接赋值实例对象,而非 UUID 字符串。
- 问题根源:在场景进程中直接将 UUID 字符串赋给资源属性(如
5. AI 操作安全守则 (Subject Validation Rule)
5.1 确定性优先
- 核心法则:任何对节点、组件、属性的操作,都必须建立在 “主体已确认存在” 的基础上。
- 具体流程:
- 节点校验:在操作前必须调用
get_scene_hierarchy确认nodeId。 - 组件校验:在
update或remove前必须调用manage_components(action: 'get')确认目标组件存在。 - 属性校验:严禁猜测属性名。在
update前,应通过读取脚本定义或get返回的现有属性列表来确定准确的属性名称。
- 节点校验:在操作前必须调用
- 禁止行为:禁止基于假设进行盲目赋值或删除。如果发现对象不存在,应立即报错或尝试重建,而非继续尝试修改。
5.2 传参精准与别名容错
- 传参风险:大语言模型在生成 JSON 时可能会出现操作类型字段名“幻觉”(例如在调用
manage_components时将本应是action的参数写为含义相近的operation)。 - 优化机制:底层脚本 (
scene-script.js) 已经全面引入参数别名回落机制(如action = action || operation)。 - 提示开发:尽管底层具备一定的容错率,在维护 MCP 工具说明书(Schema)时,仍应严格要求 AI 书写标准参数名,避免纵容产生更大的幻觉偏移。
6. 常见资源关键字
- 资产识别启发式:当通过
manage_components赋值时,如果属性名包含以下关键字,插件会尝试将其作为 UUID 资源处理:prefab,sprite,texture,material,skeleton,spine,atlas,font,audio,data - 建议:如果资源未正确加载,请检查属性名是否包含以上关键字,或手动确认该 UUID 不属于任何节点。
7. 搜索工具 (search_project) 使用建议
- 性能建议:尽量指定
path参数缩小搜索范围(如db://assets/scripts),避免全项目大面积搜索,尤其是在包含大量旧资源的 Library 目录(虽然插件已过滤)。 - 正则表达式:在使用
useRegex时,确保正则模式的语法正确。如果正则匹配失败,工具会返回详细的错误提示。 - 模式选择:
- 查找具体逻辑代码:使用
matchType: "content"。 - 定位资源文件:使用
matchType: "file_name"并配合extensions过滤。 - 重构目录结构前:使用
matchType: "dir_name"检查目录名冲突。
- 查找具体逻辑代码:使用
8. Undo/Redo (撤销/重做) 使用指南
8.1 事务分组
- 背景:连续执行多次修改(如同时移动并缩放)时,通常希望一次“撤销”能回滚所有更改。
- 最佳实践:
- 调用
manage_undo(action: 'begin_group', description: '操作描述')。 - 执行多次修改(如调用
update_node_transform)。 - 调用
manage_undo(action: 'end_group')。
- 调用
- 注意:
undo-record需要在begin_group时明确关联节点 ID,否则可能导致撤销栈无法精准匹配对象。
8.2 属性修改方式
- 核心规则:在
scene-script.js中严禁直接使用node.x = 100。 - 正确做法:必须通过
Editor.Ipc.sendToPanel('scene', 'scene:set-property', ...)。只有这样,修改才会被 Cocos Creator 的 UndoManager 捕获,从而支持撤销。
9. 并发安全与防卡死机制
9.1 指令队列 (CommandQueue)
- 背景:AI 客户端可能在短时间内连续发送多个 MCP 请求(如
delete-node→refresh_editor→search_project),如果并发执行,AssetDB.refresh()等异步重操作会与后续请求产生 I/O 和 IPC 通道冲突,导致编辑器主线程阻塞。 - 解决方案:在
/call-toolHTTP 入口处引入enqueueCommand机制,将所有 MCP 工具调用串行化执行。前一个指令的回调完成后,才会出队并处理下一个指令。 - 注意事项:
- 队列在
processNextCommand的catch块中有防死锁保护,即使某个指令抛出异常也不会永久阻塞后续指令。 batchExecute内部也已从并行forEach改为串行链式执行。- 队列长度会在日志中显示(
REQ -> [toolName] (队列长度: N)),可用于排查积压问题。
- 队列在
9.2 refresh_editor 路径要求
- ⚠️ 必须带路径:在调用
manage_editor的refresh_editor时,务必通过properties.path指定精确的文件或目录(如db://assets/scripts/Test.ts)。严禁空参数进行全局刷新,在生产项目中会导致数分钟的编辑器卡死。
9.3 IPC 超时保护 (callSceneScriptWithTimeout)
- 背景:
Editor.Scene.callSceneScript的回调依赖 Scene 面板响应 IPC 消息。如果主线程阻塞,Scene 面板无法处理消息,导致 callback 永远不返回,HTTP 连接堆积。 - 解决方案:所有
callSceneScript调用均通过callSceneScriptWithTimeout包装,默认 15 秒超时。超时后自动返回错误,释放 HTTP 连接和队列位置。 - 日志标识:超时会记录
[超时] callSceneScript "方法名" 超过 15000ms 未响应。
10. Token 消耗与长数据保护防爆机制
10.1 get_scene_hierarchy 深度与层级限制
- 背景:在一两千个节点的大型 UI 场景中,无限制地获取全场景树会瞬间消耗十万以上的 Token,导致 AI 丢失上下文甚至触发截断报错。
- 最佳实践:
- 默认使用
depth: 2(默认限制) 来逐步探查。 - 结合
nodeId参数:找到关键模块(例如Canvas/LoginPanel)的 UUID 后,再单独向该nodeId请求下一层的结构,而非每次从根部拉取。
- 默认使用
10.2 大对象与长数组截断
- 背景:在读取某些特定组件数据(如多边形顶点坐标、Sprite 曲线数据或序列化的内联 Base64 图片)时,JSON 可能会异常庞大。
- 保护机制:
scene-script.js内部在执行manage_components(get)序列化时,对于长度超过 10 的 Array 会强制截断,返回字面量字符串"[Array(X)]"。- 对于长度大于 200 的长字符串,也会强制缩略并追加
...[Truncated, total length: X]。
- 应对策略:如果 AI 看到截断提示,这意味着此处为海量无语义数据,请勿尝试盲目通过
update覆盖或还原被截断的字段,极易导致源数据被破坏。请仅修改自己能够完全看清的轻量级属性(如name,x,scale等)。