- 新增 CommandQueue 指令队列,所有 /call-tool 请求串行化执行 - 新增 callSceneScriptWithTimeout 超时保护(15秒),防止IPC永久挂起 - batchExecute 从并行 forEach 改为串行链式执行 - 9处 callSceneScript 替换为超时版本 - 清理 /list-tools 中重复的 res.end 死代码 - 更新注意事项文档,记录并发安全与防卡死机制(第9章)
7.8 KiB
7.8 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返回的现有属性列表来确定准确的属性名称。
- 节点校验:在操作前必须调用
- 禁止行为:禁止基于假设进行盲目赋值或删除。如果发现对象不存在,应立即报错或尝试重建,而非继续尝试修改。
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 IPC 超时保护 (callSceneScriptWithTimeout)
- 背景:
Editor.Scene.callSceneScript的回调依赖 Scene 面板响应 IPC 消息。如果主线程阻塞,Scene 面板无法处理消息,导致 callback 永远不返回,HTTP 连接堆积。 - 解决方案:所有
callSceneScript调用均通过callSceneScriptWithTimeout包装,默认 15 秒超时。超时后自动返回错误,释放 HTTP 连接和队列位置。 - 日志标识:超时会记录
[超时] callSceneScript "方法名" 超过 15000ms 未响应。