关于 create-scene
指导创建 Decentraland SDK7 场景的技能,涵盖需求梳理、使用 `/init` 搭建项目、选择资源和配置文件,适用于场景开发者,并强调声明式实体与行为分离。
主要功能
- 01梳理场景需求
- 02使用 `/init` 搭建项目
- 03搜索 3D 模型与音频目录
- 04配置场景文件和地块
- 05分离声明式实体与行为
适用场景
- 01启动美术馆、游戏或社交场景
- 02按场景主题挑选模型和音效
- 03配置出生点与地块布局
- 04预览新建场景
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dcl-regenesislabs/ opendcl --skill create-scene安装后在 Claude Code 中输入/create-scene确认已加载。
详细介绍
创建新的 Decentraland SDK7 场景
运行时限制: Decentraland 在 QuickJS 沙盒中运行。不支持 Node.js API(
fs、http、path、process)。请使用 SDK 的executeTask()+fetch()执行异步工作。详情请参阅 scene-runtime 技能。
当用户想要创建新场景时,请按以下步骤操作:
1. 询问他们想要构建什么
如果用户尚未描述场景,请询问他们:
- 场景类型是什么?(画廊、游戏、社交空间、交互艺术等)
- 有多少块地块?(默认值:1 块地块 = 16x16m)
- 有哪些特定功能?(3D 模型、交互功能、UI、多人游戏)
2. 使用 /init 搭建项目框架
始终先运行 /init。 它会使用官方的 @dcl/sdk-commands init 创建 scene.json、package.json、tsconfig.json 和 src/index.ts,并采用正确且最新的配置,同时自动安装依赖项。
切勿手动创建 scene.json、package.json 或 tsconfig.json——SDK 模板可能会随版本变化,手动编写的副本会逐渐偏离。
3. 查找匹配的 3D 资源
编写场景代码前,请在资源目录中搜索与用户主题匹配的免费模型:
- 搜索模型目录(包含 5,700 多个经过优化的 3D 模型——角色、建筑、道具、自然元素、载具等)
grep -i "keyword" {baseDir}/../add-3d-models/references/model-catalog.md - 阅读
{baseDir}/../../context/audio-catalog.md(包含 50 种免费音效——音乐、环境音、音效、游戏机制等) - 获取预览缩略图(每行目录末尾的
preview:URL),以直观确认模型 - 向用户推荐匹配的模型和音效
- 将选定的模型下载到场景的
models/目录中:mkdir -p models curl -o models/zombie-purple.glb "https://models.dclregenesislabs.xyz/blobs/bafybeiffc..."
重要:
GltfContainer仅适用于本地文件。模型的src字段绝不可使用外部 URL。
4. 自定义生成的文件
/init 完成后,根据用户的需求自定义生成的文件:
scene.json
更新 display 字段和地块:
display.title— 设置场景名称display.description— 设置简短描述scene.parcels— 对于多地块场景,列出所有地块(例如,2x2 场景使用["0,0", "0,1", "1,0", "1,1"])scene.base— 设置为西南角地块
main-entities.ts + src/index.ts
OpenDCL 场景采用双文件创作模式:
main-entities.ts(场景根目录)——使用类型化的声明式实体及其数据组件,并以 Name 为键。构建时会编译为main.crdt,并由引擎在运行main()之前预加载。src/index.ts——仅包含行为。通过engine.getEntityOrNullByName<EntityName>(name)按Name引用实体,并附加系统、指针事件、补间等。
main-entities.ts 示例:
import type { Scene } from '@dcl/sdk/scene-types'
export const scene = {
blue_cube: {
components: {
Transform: { position: { x: 8, y: 1, z: 8 }, rotation: { x: 0, y: 0, z: 0, w: 1 }, scale: { x: 1, y: 1, z: 1 } },
MeshRenderer: { mesh: { $case: 'box', box: { uvs: [] } } },
Material: {
material: { $case: 'pbr', pbr: { albedoColor: { r: 0.2, g: 0.5, b: 1, a: 1 } } },
},
},
},
} satisfies Scene
satisfies Scene 子句会保留字面量键的类型(因此 keyof typeof scene 会得到类型化的实体名称联合类型),同时仍会根据 Scene 架构验证对象的形状。
src/index.ts 示例:
import { engine, pointerEventsSystem, InputAction } from '@dcl/sdk/ecs'
import type { scene } from '../main-entities'
type EntityName = keyof typeof scene
export function main() {
const cube = engine.getEntityOrNullByName<EntityName>('blue_cube')
if (cube === null) return
pointerEventsSystem.onPointerDown(
{ entity: cube, opts: { button: InputAction.IA_POINTER, hoverText: 'Click me' } },
() => console.log('clicked'),
)
}
tsconfig.json 应包含 main-entities.ts,以便对其进行类型检查:
{
"extends": "@dcl/sdk/types/tsconfig.ecs7.json",
"include": ["src/**/*.ts", "src/**/*.tsx", "main-entities.ts"]
}
规则:
- 每个可编辑/已声明的实体都必须在
main-entities.ts中拥有唯一的 Name。 - 运行时创建的动态实体(特效、弹射物、动态 UI 标记)直接使用
engine.addEntity()。不要为动态实体设置 Name——它们不属于main-entities.ts。 - 纯数据内容(Transform、GltfContainer、MeshRenderer、MeshCollider、Material、AudioSource、VideoPlayer、TextShape、Animator 配置、NftShape、Billboard、VisibilityComponent)放在
main-entities.ts中。 - 行为内容(指针回调、系统、补间触发器、条件逻辑)放在
src/中。 scene字面量必须兼容 JSON——对象中不能有函数调用、展开语法或注释。
scene.json 参考
所有有效的 scene.json 字段:
| 字段 | 必填 | 说明 |
|---|---|---|
ecs7 | 是 | SDK7 必须设为 true |
runtimeVersion | 是 | 必须为 "7" |
main | 是 | 必须为 "bin/index.js" — 编译后的输出路径 |
display.title | 建议 | 显示在地图和 Places 中的场景名称 |
display.description | 建议 | 用于发现的简短说明 |
display.navmapThumbnail | 可选 | Genesis City 小地图的图片路径 |
scene.parcels | 是 | "x,y" 坐标字符串数组 |
scene.base | 是 | 原点地块(通常为西南角) |
spawnPoints | 可选 | 玩家进入时出现的位置(见下文) |
requiredPermissions | 可选 | 权限数组(例如,"ALLOW_MEDIA_HOSTNAMES") |
allowedMediaHostnames | 可选 | 外部媒体的白名单域名 |
featureToggles | 可选 | 启用/禁用 SDK 功能 |
worldConfiguration | 可选 | 用于 Worlds 部署(参见 deploy-worlds 技能) |
authoritativeMultiplayer | 可选 | 启用权威服务器模式(参见 authoritative-server 技能) |
出生点
配置玩家进入场景的位置和方式:
{
"spawnPoints": [
{
"name": "spawn1",
"default": true,
"position": { "x": [1, 5], "y": [0, 0], "z": [2, 4] },
"cameraTarget": { "x": 8, "y": 1, "z": 8 }
}
]
}
- 位置范围(例如,
[1, 5])会让玩家在该范围内随机出生 cameraTarget用于确定玩家出生时的镜头朝向 — 将其指向场景的焦点区域- 固定出生点:使用单个值而非范围(例如,
"x": 8)
多地块布局
| 布局 | 地块数组 | 使用场景 |
|---|---|---|
| 单地块 | ["0,0"] | 小型游戏、画廊、单房间体验 |
| 条带 | ["0,0", "1,0", "2,0"] | 走廊、赛车道、线性旅程 |
| L 形 | ["0,0", "1,0", "0,1"] | 转角建筑、分区体验 |
| 2x2 方形 | ["0,0", "1,0", "0,1", "1,1"] | 开放广场、竞技场、中型游戏 |
| 3x3 方形 | 从 "0,0" 到 "2,2" 的 9 个地块 | 大型游戏、多房间建筑 |
基础地块: 始终将 scene.base 设置为西南角(x、y 最小)的地块。
每个地块的边界: 16m x 16m,高 20m。2x2 场景占地 32m x 32m。
5. 创建后的步骤
自定义文件后:
- 使用
preview工具启动预览服务器(或手动运行npx @dcl/sdk-commands start --bevy-web) - 场景将在浏览器中打开,地址为 http://localhost:8000
交叉引用
- 准备部署?请参阅 deploy-scene 技能(Genesis City)或 deploy-worlds 技能(个人 Worlds)
- 需要针对地块限制进行优化?请参阅 optimize-scene 技能
- 正在规划游戏?请参阅 game-design 技能,了解设计模式和性能预算
重要说明
- 始终将物体放置在场景边界内(X 轴为 0 到 16parcelsX,Z 轴为 0 到 16parcelsZ)
- 单地块场景的中心位于地面高度的 (8, 0, 8)
- Y 轴朝上,最小 Y=0(地面)
- scene.json 中的
main字段必须为"bin/index.js"— 这是编译后的输出路径 jsx和jsxImportSourcetsconfig 设置已由/init包含 — 请勿修改- 切勿在 Transform 字段(position、rotation、scale)中传入
undefined值 — SDK 序列化器会崩溃。如果字段是可选的,请完全省略该键,而不要将其设为undefined。