mcpField
C
composites

composites

github.com/decentraland开发4286 次安装今天更新
代码仓库

关于 composites

为 Decentraland 场景开发提供 composite 格式与编辑指导,供创建或修改场景实体的 Agent 使用。说明 Creator Hub MCP 优先工作流、场景边界计算和校验要求。

主要功能

  1. 01Creator Hub MCP 工作流门槛
  2. 02根据地块计算场景边界
  3. 03GLB 模型边界框检查
  4. 04Composite 格式参考
  5. 05创建与编辑校验清单

适用场景

  1. 01在 Creator Hub 中编辑场景实体
  2. 02在文件模式下创建 composite
  3. 03检查模型是否超出场景边界

MCP 相关

说明 Creator Hub MCP 工具可用于实时创建实体、设置组件和场景设置,并将其作为手工编辑 composite 文件的优先方式。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/decentraland/sdk-skills --skill composites

安装后在 Claude Code 中输入/composites确认已加载。

附带文件composite-reference.md

详细介绍

复合体

此技能包含其他 Decentraland 技能(create-scene、add-3d-models、sdk-scenes)共用的复合体格式参考。

以下强制工作流适用于你编写或编辑的每个复合体:先检查 Creator Hub MCP 门槛,计算场景边界(步骤 0),编写实体时查阅参考资料中的格式目录,并在结束前运行验证门槛。

门槛 — 使用 Creator Hub MCP,而非编辑文件(先检查)

手动编辑 main.composite 是备用方案,而非默认做法。 Creator Hub 提供了一个 MCP 服务(技能:creator-hub-mcp),其工具——scene_state、create_entity、set_component、remove_entity、place_smart_item、attach_script、set_scene_settings,等等——可以实时更改已打开的场景:视口会更新,编辑器会自动保存复合体本身,inspector::Nodes 和编辑器的其他簿记信息会保持一致,并且用户可以撤销每项更改。反之,编辑文件可能导致典型问题:编辑器的自动保存会重新生成整个文件,你的工作会悄无声息地消失。

修改文件之前,请选择第一个符合条件的条目:

  1. 当前会话中提供 Creator Hub MCP 工具(Claude Code 中的 mcp__creator-hub__*;Creator Hub 自带 AI 助手中的裸 scene_state / create_entity)→ 不要手动编辑 main.composite、main.crdt 或 scene.json。所有实体、组件和设置的更改都使用这些工具。此技能中的格式目录仍可作为组件形状的参考——set_component 接受的 JSON 与复合体中存储的 JSON 相同;步骤 0(边界)仍然适用。
  2. Creator Hub 正在运行,但工具尚未连接 → 告知用户 MCP 是更安全的方案,并说明如何连接({baseDir}/../creator-hub-mcp/reference/connect.md:Settings > AI > Expose AI assistant MCP server,复制代码片段)。只有用户拒绝后,才可改为编辑文件;且只能在 Creator Hub 中关闭场景后进行。如果场景已打开(或可能已打开),而 MCP 未连接,则必须暂停并询问:提供关闭场景、连接 MCP 或通过代码构建这几种选择,然后等待用户回复。是否改为在 src/ 中构建静态实体由用户决定,绝不能由你决定——无论是为了搭建灰盒,还是因为会话处于非交互模式(规范规则:sdk-scenes → Agent Behavioral Guidelines,第 5 项)。
  3. 没有使用 Creator Hub(仅 CLI 项目、CI、应用已关闭且用户希望保持关闭)→ 完整采用以下文件工作流。

步骤 0 — 读取 scene.json 并计算边界(必须执行)

在编写任何实体位置之前,读取 scene.json 并计算场景边界。 必须先执行此步骤——所有实体位置都必须在这些边界内,否则将无法渲染。

如何计算边界
  1. 打开 scene.json,找到 scene.parcels(由 "x,y" 字符串组成的数组)和 scene.base。
  2. 将每个地块解析为整数。找出所有地块中 X 和 Y 的最小值与最大值。
  3. 计算:
parcelsWide = max(parcel_x) - min(parcel_x) + 1
parcelsDeep = max(parcel_y) - min(parcel_y) + 1

maxX = parcelsWide * 16
maxZ = parcelsDeep * 16
  1. 有效实体位置:X 在 [0, maxX] 内,Z 在 [0, maxZ] 内。负值以及大于 maxX/maxZ 的值均在场景范围之外,不会渲染。
步骤 0b — 计算 3D 模型的包围盒(GLB 模型必须执行)

模型的 Transform.position 是其局部原点,不是其视觉范围。 树木和植被模型通常会在一个或多个方向上延伸到原点之外 6–12 m。将树木放置在 x=2 处,可能导致其渲染在 x=–10 处,从而超出场景边界。

如何查找模型的包围盒 — 解析 GLB 二进制文件并应用节点级变换。仅凭原始 accessor 的 min/max 值并不可靠,因为许多 GLB 模型在 GLTF 节点层级中烘焙了较大的缩放系数或平移值(例如,某个模型的 accessor 显示为 0.6 m,但节点缩放为 24×,实际渲染尺寸便是 14 m)。

node -e "
const buf = require('fs').readFileSync('assets/Models/MyModel.glb');
const jsonLen = buf.readUInt32LE(12);
const json = JSON.parse(buf.slice(20, 20+jsonLen));
let minW=[Infinity,Infinity,Infinity], maxW=[-Infinity,-Infinity,-Infinity];
json.nodes?.forEach(n => {
  if (n.mesh === undefined) return;
  const s = n.scale || [1,1,1];
  const t = n.translation || [0,0,0];
  for (const prim of json.meshes[n.mesh].primitives) {
    const acc = json.accessors[prim.attributes.POSITION];
    if (!acc.min || !acc.max) continue;
    for (let i = 0; i < 3; i++) {
      const lo = acc.min[i]*s[i]+t[i], hi = acc.max[i]*s[i]+t[i];
      minW[i] = Math.min(minW[i], lo, hi);
      maxW[i] = Math.max(maxW[i], lo, hi);
    }
  }
});
const w=maxW[0]-minW[0], h=maxW[1]-minW[1], d=maxW[2]-minW[2];
console.log('Rendered size:', w.toFixed(2)+'m x', h.toFixed(2)+'m x', d.toFixed(2)+'m');
console.log('World min:', minW.map(v=>v.toFixed(2)), 'max:', maxW.map(v=>v.toFixed(2)));
"

逐个模型测量,不要猜测或硬编码。 模型范围差异很大:对典型树木运行上述脚本,通常会发现其在某个水平方向上从原点延伸约 11 m(安全的最小原点 z≥12);而柱子在各个方向上的延伸都不到 1 m。务必计算你要放置的特定 GLB 的包围盒。

规则: 对每个 GLB 模型,计算:

minSafeX = max(0, -bbox.minX) + margin      (≥1 m)
minSafeZ = max(0, -bbox.minZ) + margin      (≥1 m)
maxSafeX = maxX - (bbox.maxX + margin)
maxSafeZ = maxZ - (bbox.maxZ + margin)

只有当模型的 Transform 位置满足所有四项边界条件时,才放置该模型。

对于包围盒未知的树木/植被模型,假设其距所有边缘有12 m 的安全缓冲距离——也就是说,将原点放在 [12, maxX-12] × [12, maxZ-12] 范围内。

放置建筑时:按第三人称视角的比例规划,而非真实比例。 房间过大会改变楼层平面图所需的地块面积,因此应在确定边界时调整房间尺寸。尺寸说明见 add-3d-models → “规则:按第三人称相机视角放大室内空间”。

示例
scene.json 地块parcelsWideparcelsDeep有效 X有效 Z
["0,0"]110 – 160 – 16
["0,0","1,0"]210 – 320 – 16
["0,0","1,0","0,1","1,1"]220 – 320 – 32
未经用户明确指示,绝不要更改 scene.json 的地块数量

如果设计所需空间超出地块允许范围,请询问用户是否要添加地块。 不要擅自添加,也不要为了避免询问而压缩设计(例如垂直叠放房间,或将房间缩小到第三人称视角比例以下)。并非总能向 scene.json 添加地块,这取决于场景的发布位置。如果发布到 Genesis City,部署钱包必须拥有或租用这些地块;如果发布到 World,则可能可以添加。如果用户拒绝,请在现有地块范围内工作,并尽可能丰富场景内容。

Composite 格式目录(参考)

阅读 {baseDir}/composite-reference.md,了解 main.composite JSON 格式的完整规范:JSON 结构和 jsonSchema 规则、实体 ID 分配、各组件的格式目录(core::Transform、core::GltfContainer、… core::NftShape)、组件分组模式、编辑模式规则(inspector::Nodes)、运行时生成,以及从 TypeScript 获取 composite 实体的模式。

验证清单

步骤 1 — 检测模式。 扫描 composite,查找 inspector::*、composite::root 或 asset-packs::ActionTypes。如果存在其中任意一项,则处于编辑模式——使用下方的编辑模式清单。否则使用从头创作清单。

从零编写检查清单

编写新的复合体之前,请确认:

  • version 为 1
  • 不包含任何 inspector::* 组件——包括 inspector::Nodes、inspector::SceneMetadata(任何版本)、inspector::Selection、inspector::TransformConfig、inspector::UIState。这些组件均由 Creator Hub 自动生成,在新文件中加入它们会破坏实体树或导致构建错误。
  • 不包含 composite::root 或 asset-packs::ActionTypes——它们由引擎自动生成
  • 每个用户实体(512+)都有 core::Transform 和 core-schema::Name
  • 复合体中没有重复的实体 ID
  • 与通过代码创建且指定了 ID 的实体之间没有重复的实体 ID
  • core:: 组件不得包含 jsonSchema——这是硬性要求;在 core:: 组件中加入 jsonSchema 会导致 Creator Hub 无法正确解析实体
  • 非核心组件(asset-packs::*、core-schema::*)必须包含 jsonSchema(从目录复制)
  • 所有 GltfContainer.src 路径均使用 slug 化名称格式:assets/asset-packs/<slug>/<filename>
  • 所有引用的资源文件都已下载到磁盘(GLB、音频、图像)
  • GltfContainer 上已设置默认碰撞掩码(visibleMeshesCollisionMask: 0、invisibleMeshesCollisionMask: 3)
  • 所有位置均在地块边界内——边界已在 Step 0 中根据实际的 scene.json 地块列表计算得出。每个实体的 X 都在 [0, maxX] 范围内,Z 都在 [0, maxZ] 范围内。负值以及超过 maxX/maxZ 的值都不会渲染。如果用户要求创建“大型”场景,但地块数量未变更,则所有实体都必须位于原始边界内。
  • 对于每个 GltfContainer 实体:已检查 GLB 是否包含动画(嵌入在文件中的片段名称)。如果包含,则该实体上存在 core::Animator 组件。包含动画但没有 Animator 的模型会静默循环播放第一个片段,且无法控制。
  • 对于每个 GltfContainer 实体:已检查 GLB 是否包含碰撞网格(名称中包含字符串 _collider 的任意网格)。如果包含,则将 invisibleMeshesCollisionMask 设置为 3(CL_POINTER + CL_PHYSICS)以启用它们。如果没有内置碰撞体,则已评估是否需要使用 core::MeshCollider 盒体/球体来覆盖模型的大致形状(适用于可行走表面、墙壁或可点击对象)。
  • 如果复合体中的任何位置存在 asset-packs::Actions、asset-packs::Triggers 或 asset-packs::States,则实体 0 上必须存在 asset-packs::Counter,且其 value 等于 Actions/Triggers/States 数据中使用的最高 id(它是这些 ID 的分配器)
  • 不包含 {self}、{assetPath} 或占位字符串——所有内容均已解析为具体值
  • 组件名称使用基础名称(例如 asset-packs::Actions,而非 asset-packs::Actions-v1)。切勿使用带版本号的后缀,例如 -v3。
  • 仅使用核心组件的复合体不需要额外库。如果其中包含 asset-packs::* 组件,较旧的 SDK 需要将 @dcl/asset-packs 作为项目依赖;当前 SDK 则会回退到 @dcl/inspector 中捆绑的副本
编辑模式检查清单(组合文件中已包含 inspector::*)
  • Creator Hub MCP 不可用。 如果会话中包含其工具,请停止——以下所有事项都可由 create_entity / set_component 为你完成(请参阅此技能顶部的门槛说明和 creator-hub-mcp)。
  • 场景当前未在 Creator Hub 中打开——每次写入 main.composite 或 scene.json 之前都要再次检查,而不是每个会话只检查一次。所有者随时可能重新打开场景;当 Hub 处于打开状态时写入的 parcel 更改曾被其自动保存还原(2026-10)。检查器会自动保存,并从其内存中的引擎整体覆盖 main.composite,丢弃外部编辑且不会报错。写入前请让用户关闭场景,并在写入后让用户重新打开。如果场景已打开而 MCP 不可用,请停止并询问——除非用户明确选择,否则不要改为将静态实体移到 src/(sdk-scenes → Agent 行为指南,第 5 项,其中也列出了场景处于打开状态的迹象)。

对于你添加的每个新实体 <id>,除了遵循上述从头创作规则(但保留 inspector::* 等,不要删除)之外:

  • 已将 <id> 追加到 inspector::Nodes.data["0"].json.value 中 entity-0 条目的 children 数组末尾。
  • 已将新条目 { "entity": <id>, "children": [...] } 追加到 inspector::Nodes.data["0"].json 的 value 数组中(如果该实体没有自己的子实体,则使用 [])。
  • 如果 <id> 的 Transform.parent 不为 0,则 <id> 位于父实体的 children 数组中(而非根实体的数组中)。
  • core-schema::Name.data["<id>"] 中有一个 { "json": { "value": "..." } } 条目——实体必须有名称才能正确显示在实体树中并可通过代码查找。
  • inspector::TransformConfig.data["<id>"] 中有一个 { "json": {} } 条目(空对象即可)。
  • 已更新 entity-names.ts,在 EntityNames 中加入新名称,或者保持不变,以便 Creator Hub 在下次保存时重新生成。不要手动编辑自动生成的文件头。
  • 未删除或移除现有的 inspector::Nodes、inspector::SceneMetadata-*、inspector::Selection、inspector::TransformConfig、composite::root 或 asset-packs::ActionTypes。这些由 Creator Hub 管理,必须保留。
  • inspector::SceneMetadata-* 保持不变,除非 scene.json 中的 parcel 已更改(若有更改,布局块必须与 scene.json 一致)。
  • 保留实体 1(PlayerEntity)和 2(CameraEntity),并确保它们在 inspector::Nodes 中仍各自拥有顶层条目 { "entity": 1, "children": [] } / { "entity": 2, "children": [] }。它们是独立的树根——不得出现在 entity 0 的 children 数组(或任何其他 children 数组)中。

**验证命令(编辑模式):**编辑后,core::Transform.data 中的每个实体 ID 都应同时出现在以下位置:

  1. 作为顶层条目 { "entity": <id>, ... } 出现在 inspector::Nodes.data["0"].json.value 中;并且
  2. 出现在同一 value 列表中恰好一个 children 数组(即其父实体的 children 数组)中。

实体 0、1 和 2 是规则 2 的例外——它们是树根,不会出现在任何 children 数组中。

此处缺少条目是“实体已渲染,但在 Creator Hub 实体树中不可见”的根本原因。

写入后验证

写入复合体后,运行 SDK 构建进行验证:

npx sdk-commands build

构建必须零错误通过。如果失败,复合体无效。常见错误:

  • Composite references undefined component "X". Ensure provider.schemas was registered pre-seal via setCompositeProvider().(较旧或已发布的 SDK 会将其表述为 "X is not defined and there is no schema to define it")→ 非核心组件缺少 jsonSchema,或包含了不应存在的 inspector::* 组件
  • TypeScript 错误 → 修复生成的脚本
  • 错误指出了你原本不打算发布的文件(Composite 'art/.../before.composite' can't be read,或来自备份文件的架构错误)→ 构建会匹配项目下的所有 *.composite(sdk-commands logic/composite.ts),包括恢复副本。请使用其他扩展名(before-composite.json)保存备份,或将备份放在项目之外,并检查构建调试输出中的 .composite: 列表;部署忽略规则不会让构建忽略文件。
  • 你添加的 inspector::* 组件出现架构错误 → 切勿臆造 inspector 组件(inspector::TransformConfig 并不存在);只编写文件中已有条目的 inspector 组件,并照抄其准确结构。

评论

支持 Markdown