一键导入
scene-runtime
Scene runtime APIs — async tasks, HTTP fetch, WebSockets, realm/scene info, timers, restricted actions, portable experiences, testing framework
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Scene runtime APIs — async tasks, HTTP fetch, WebSockets, realm/scene info, timers, restricted actions, portable experiences, testing framework
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Add 3D models (.glb/.gltf) to a Decentraland scene using GltfContainer. Covers loading models, positioning, scaling, colliders, and browsing the open-source 3D assets catalog for free CC0 models. Use when user wants to add models, import GLB files, or find free 3D assets.
Add click handlers, hover effects, pointer events, triggers, and raycasting to Decentraland scene entities. Use when user wants to make objects clickable, add interactions, detect player proximity, or handle user input.
Advanced input handling in Decentraland scenes. Use PointerLock to detect cursor capture state, InputModifier to freeze or restrict player movement, PrimaryPointerInfo for cursor position and world ray, inputSystem.getInputCommand for per-entity input polling, and keyboard patterns for WASD movement controls. Use when user wants custom input, cursor control, movement restriction, keyboard handling, FPS controls, or input polling.
Advanced rendering features in Decentraland scenes. Use Billboard to make entities face the camera, TextShape for 3D text, advanced PBR material properties like metallic/roughness/transparency, GltfNodeModifiers for per-node visibility and material overrides in GLTF models, and VisibilityComponent to show/hide entities. Use when user wants billboards, 3D text, text labels, material effects, transparency, glow, or model node control.
Animate objects in Decentraland scenes using Animator (GLTF animations), Tween (move/rotate/scale over time), and TweenSequence (chain animations). Use when user wants to animate, move, rotate, spin, slide, or create motion effects.
Build 2D user interfaces for Decentraland scenes using React-ECS. Create HUDs, menus, health bars, scoreboards, dialogs, buttons, and input forms. Use when user wants to add UI, HUD, buttons, text overlays, menus, or on-screen elements.
| name | scene-runtime |
| description | Scene runtime APIs — async tasks, HTTP fetch, WebSockets, realm/scene info, timers, restricted actions, portable experiences, testing framework |
Cross-cutting runtime APIs available in every Decentraland SDK7 scene.
The scene runtime is single-threaded. Wrap any async work in executeTask():
import { executeTask } from '@dcl/sdk/ecs'
executeTask(async () => {
const res = await fetch('https://api.example.com/data')
const data = await res.json()
console.log(data)
})
Plain fetch works for public APIs:
const res = await fetch('https://api.example.com/data')
signedFetch proves the player's identity to your backend. Use getHeaders() to obtain only the signed headers (useful when a library manages its own fetch):
import { signedFetch, getHeaders } from '~system/SignedFetch'
// Full signed request
const res = await signedFetch({ url: 'https://your-server.com/api', init: { method: 'POST', body: JSON.stringify(payload) } })
// Get signed headers only (for custom fetch calls)
const { headers } = await getHeaders({ url: 'https://your-server.com/api' })
Permission: External HTTP requires
"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE"or no special permission for plain fetch;signedFetchneeds the player to have interacted with the scene.
const ws = new WebSocket('wss://your-server.com/ws')
ws.onopen = () => ws.send('hello')
ws.onmessage = (event) => console.log(event.data)
ws.onclose = () => console.log('disconnected')
import { getSceneInformation, getRealm } from '~system/Runtime'
import { getExplorerInformation } from '~system/EnvironmentApi'
executeTask(async () => {
// Scene info: URN, content mappings, metadata JSON, baseUrl
const scene = await getSceneInformation({})
const metadata = JSON.parse(scene.metadataJson)
console.log(scene.urn, scene.baseUrl, metadata)
// Realm info: baseUrl, realmName, isPreview, networkId, commsAdapter
const realm = await getRealm({})
console.log(realm.realmInfo?.realmName, realm.realmInfo?.isPreview)
// Explorer info: agent string, platform, configurations
const explorer = await getExplorerInformation({})
console.log(explorer.agent, explorer.platform)
})
import { getWorldTime } from '~system/EnvironmentApi'
executeTask(async () => {
const { seconds } = await getWorldTime({})
// seconds = coordinated world time (cycles 0-86400 for day/night)
})
Read files deployed with the scene at runtime:
import { readFile } from '~system/Runtime'
executeTask(async () => {
const result = await readFile({ fileName: 'data/config.json' })
const text = new TextDecoder().decode(result.content)
const config = JSON.parse(text)
})
Access frame-level timing:
import { EngineInfo } from '@dcl/sdk/ecs'
engine.addSystem(() => {
const info = EngineInfo.getOrNull(engine.RootEntity)
if (info) {
console.log(info.frameNumber, info.tickNumber, info.totalRuntime)
}
})
These require player interaction before they can execute. Import from ~system/RestrictedActions:
import {
movePlayerTo,
teleportTo,
triggerEmote,
changeRealm,
openExternalUrl,
openNftDialog,
triggerSceneEmote,
copyToClipboard,
setCommunicationsAdapter
} from '~system/RestrictedActions'
// Move player within scene bounds
movePlayerTo({ newRelativePosition: { x: 8, y: 0, z: 8 } })
// Teleport to coordinates in Genesis City
teleportTo({ worldCoordinates: { x: 50, y: 70 } })
// Play a built-in emote
triggerEmote({ predefinedEmote: 'wave' })
// Open URL in browser (prompts user)
openExternalUrl({ url: 'https://decentraland.org' })
// Open NFT detail dialog
openNftDialog({ urn: 'urn:decentraland:ethereum:erc721:0x06012c8cf97BEaD5deAe237070F9587f8E7A266d:558536' })
// Copy text to clipboard
copyToClipboard({ value: 'Hello from Decentraland!' })
// Change realm
changeRealm({ realm: 'other-realm.dcl.eth', message: 'Join this realm?' })
setTimeout / setInterval are supported via the QuickJS runtime polyfill:
setTimeout(() => console.log('delayed'), 2000)
const id = setInterval(() => console.log('tick'), 1000)
clearInterval(id)
System-based timers (recommended for game logic — synchronized with the frame loop):
let elapsed = 0
engine.addSystem((dt: number) => {
elapsed += dt
if (elapsed >= 3) {
elapsed = 0
// Do something every 3 seconds
}
})
React to component changes on any entity:
Transform.onChange(engine.PlayerEntity, (newValue) => {
if (newValue) {
console.log('Player moved to', newValue.position)
}
})
Recursively remove an entity and all its children:
import { removeEntityWithChildren } from '@dcl/sdk/ecs'
removeEntityWithChildren(engine, parentEntity)
Scenes that persist across world navigation:
import { spawn, kill, exit, getPortableExperiencesLoaded } from '~system/PortableExperiences'
// Spawn a portable experience by URN
const result = await spawn({ urn: 'urn:decentraland:entity:bafk...' })
// List currently loaded portable experiences
const loaded = await getPortableExperiencesLoaded({})
// Kill a specific portable experience
await kill({ urn: 'urn:decentraland:entity:bafk...' })
// Exit self (if this scene IS a portable experience)
await exit({})
SDK7 includes a testing framework for automated scene tests:
import { test, assert, assertEquals, assertComponentValue } from '@dcl/sdk/testing'
import { setCameraTransform } from '@dcl/sdk/testing'
test('cube is at correct position', async (context) => {
// Set up camera for the test
setCameraTransform({ position: { x: 8, y: 1, z: 8 } })
// Wait for systems to run
await context.helpers.waitNTicks(2)
// Assert component values
assertComponentValue(cubeEntity, Transform, {
position: Vector3.create(8, 1, 8)
})
// Basic assertions
assert(Transform.has(cubeEntity), 'Entity should have Transform')
assertEquals(1 + 1, 2)
})
Run tests with:
npx @dcl/sdk-commands test
executeTask() — bare promises will be silently droppedsignedFetch (not plain fetch) when your backend needs to verify the player's identitysetTimeout/setInterval for game logic — they stay in sync with the frame looprealm.realmInfo?.isPreview to detect preview mode and enable debug featuresreadFile() for data files (JSON configs, level data) deployed alongside the sceneremoveEntityWithChildren() is essential when cleaning up complex entity hierarchies