decentraland/sdk-skills

lighting-environment

Dynamic lighting and environment in Decentraland scenes.

ソースを見る

使い方

  1. インストールコマンドをコピーし、自分の環境へ導入する前にソースを確認します。
  2. 必要なツール、認証情報、対応環境を原文で確認します。
  3. 入力と成果物を指定し、出力を資料と照合します。
npx skills add https://github.com/decentraland/sdk-skills --skill lighting-environment

開始前の確認

  • 導入にはモデル契約、API枠、ファイルへのアクセス許可は含まれません。
  • スター数や導入数は活動の指標であり、独立した品質・安全性試験ではありません。
リポジトリの原文

見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。

Lighting and Environment in Decentraland

RULE: Use Smart Items for lights when the Creator Hub MCP is available

When the user asks to add a light and the Creator Hub MCP tools are present (mcp__creator-hub__*), always `search_catalog` first before manually creating an entity with LightSource. The Creator Hub catalog includes ready-made light Smart Items:

  • Spotlight (87c829b3-8e8d-4fcb-9f43-85d3aa9084f6, category lights) -- comes with a real GLB model (assets/asset-packs/spotlight/spotlight.glb), a pre-configured core::LightSource, built-in Turn On / Turn Off / Toggle actions, and proper inspector::Config for the Creator Hub UI.
  • Point Light (category lights) -- same pattern: GLB placeholder, pre-configured core::LightSource of type Point, built-in actions.
  • There is also a decorative "Spotlight" (category decorations) that is a visual-only fixture model with no LightSource -- make sure you pick from the lights category when the user wants actual illumination.

Use place_smart_item with the assetId and a position. It handles everything: downloading the GLB files, resolving asset paths, setting up asset-packs::Placeholder, asset-packs::Script, asset-packs::Actions, and inspector::Config components. The entity is immediately usable in the editor with action triggers wired up.

Only fall back to manual `create_entity` + `set_component` `core::LightSource` when:

  • No Creator Hub MCP tools are available (pure code workflow).
  • The user needs a light with no visual model (invisible light source).
  • The user needs non-standard light parameters that a Smart Item does not expose.

The manual LightSource component API documented below remains the authoritative reference for both paths -- Smart Items use the same underlying component.

RULE: 3D model light fixtures do not emit light

Light-looking geometry in a GLB model (lamp meshes, bulb shapes, glowing filaments) is purely visual -- it does not cast actual dynamic light in Decentraland. The renderer treats it as regular geometry (possibly with an emissive material for a glow effect, but no illumination of surrounding objects). To get real dynamic lighting from a fixture model, attach a LightSource component to the same entity or to a child entity positioned at the light source. When a user has a scene model with built-in light fixtures, proactively mention this: "The light fixtures in your model are decorative geometry only -- I need to add LightSource components for actual illumination."

Point Lights

Emit light in all directions from a position:

typescript
import { engine, Transform, LightSource } from '@dcl/sdk/ecs'
import { Vector3, Color3 } from '@dcl/sdk/math'

const light = engine.addEntity()
Transform.create(light, { position: Vector3.create(8, 3, 8) })

LightSource.create(light, {
  type: LightSource.Type.Point({}),
  color: Color3.White(),
  intensity: 16000  // candela
})

Colored Point Light

typescript
LightSource.create(light, {
  type: LightSource.Type.Point({}),
  color: Color3.create(1, 0.5, 0),  // Warm orange
  intensity: 16000,
  range: 15  // Maximum distance in meters
})

Defaults (from the protocol): active true, color white, intensity 16000 candela, range -1 (auto), shadow false. color is Color3 (RGB, each 0–1).

Spot Lights

Emit a cone of light in a direction:

typescript
import { Quaternion } from '@dcl/sdk/math'

const spotlight = engine.addEntity()
Transform.create(spotlight, {
  position: Vector3.create(8, 4, 8),
  rotation: Quaternion.fromEulerDegrees(90, 0, 0)  // Point straight down — +90, not -90 (see below)
})

LightSource.create(spotlight, {
  type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
  color: Color3.White(),
  intensity: 16000
})
  • innerAngle — full-brightness cone angle (degrees). Default 21.8. Min 0, max 179.
  • outerAngle — outer fade angle (degrees). Default 30. Max 179.
  • innerAngle cannot exceed outerAngle — if it does, the engine clamps them to the same value.
  • The cone shines along the entity's local +Z (its forward): the Explorer parents the Unity Light under the entity's transform with an identity local pose (LightSourceLifecycleSystem.cs, unity-explorer main 2026-10). Pitch `+90` about X points it straight down; `-90` points it at the sky — Quaternion.fromEulerDegrees(90, 0, 0) rotates Vector3.Forward() to (0, -1, 0) and -90 to (0, 1, 0), computed with @dcl/ecs-math (2026-10-22). The SDK docs' spot example and an earlier version of this skill wrote -90 for "downward", so a ceiling spot that seems to do nothing may be lighting the roof. Not yet re-confirmed in-world — check the cone once with the sanity test under Light Limits.
  • type is a discriminated union. To read/mutate spot params at runtime, narrow first:

if (comp.type?.$case === 'spot') { comp.type.spot.innerAngle = 30 }

Shadows

Enable shadows on point or spot lights:

typescript
LightSource.create(spotlight, {
  type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
  shadow: true,
  intensity: 800
})

Note: shadows are only rendered for spot lights, not point lights. shadow is a top-level boolean on the component (not inside Spot/Point).

Shadow Mask Textures (Gobos)

Project a pattern through the light:

typescript
const maskedLight = LightSource.getMutable(spotlight)
maskedLight.shadowMaskTexture = Material.Texture.Common({
  src: 'assets/Images/lightmask1.png'
})
  • Set shadowMaskTexture = undefined to remove the mask again.
  • The mask projects light shape (e.g. a window pattern) — simulating caustics/soft shadows. Used on spot lights.

Toggling Lights

typescript
// Toggle on/off
const lightData = LightSource.getMutable(light)
lightData.active = !lightData.active

Light Limits

  • A scene may create many lights (the engine test scene spawns ~9 across 2 parcels); the renderer decides how many render.
  • The desktop quality preset caps scene lights: Low = 0 (scene lights disabled entirely), Medium = 6, High = 10 (QualityPresetLow/Medium/High.asset, unity-explorer main 2026-10; the Settings slider can lower a preset's cap further). If the scene has more than the cap, only the closest lights to the player render.
  • Troubleshoot "my light doesn't show" by checking the player's active preset first — on Low a correctly authored LightSource renders nothing, and no amount of intensity, range, material or attachment-offset tuning changes that. State the supported minimum (Medium) rather than demanding High. A visible emissive flame is not proof the candle lights the room: keep the glowing mesh and the light cast onto surroundings as separate checks.
  • Up to ~3 shadow-casting lights render at once.
  • The renderer auto-culls lights based on quality settings and proximity.
  • Intensity is in candela (lumens/m² at 1m, i.e. lumens/4π). Default 16000.
  • range default is -1 → auto-computed as intensity^0.25 (fourth root, in meters). Set an explicit range to override — this also limits a light's influence and saves performance.
  • Spread lights out so few are near the player at once (only the closest ones render).
  • Keep the scene readable with no scene light at all. Desktop Low and mobile Very Low render zero scene lights, and a desktop owner test (2026-10-09) saw a point light while two spots in the same room stayed dark (cause not established). Anything gameplay-critical — the path, the interactable, a "lights turn warm" story beat — must survive on emissive materials, textures and the skybox; LightSource is accent. This generalises the Mobile rule under Platform Support.
  • One-minute sanity test before tuning anything: at the default intensity: 16000, place one point light 2.5 m above the floor and one spot at the same height pitched +90 (straight down), next to an emissive plane as a reference. Point visible, spot dark → aim or preset, not intensity. Neither visible → the preset is Low. Only then tune. Eight rounds of 8–60 cd guesses written in code (2026-10-08) preceded the one test that answered this; the catalog's Point Light / Spotlight Smart Items start at 16000 for a reason.

SkyboxTime (Day/Night Cycle)

Use SkyboxTime for atmosphere — nighttime scenes with point lights create dramatic environments.

Fixed Time in scene.json

Set a permanent time of day without code. Two valid locations:

json
// Genesis City scene — top-level
{ "skyboxConfig": { "fixedTime": 43200 } }

// World — inside worldConfiguration
{ "worldConfiguration": { "name": "my-name.dcl.eth", "skyboxConfig": { "fixedTime": 36000 } } }

Time values (seconds since midnight, full day = 86400): 0 = midnight, 21600 = 6 AM, 43200 = noon, 64800 = 6 PM (dusk), 86400 = full day.

Precedence (verified against the engine test scenes): worldConfiguration.skyboxConfig.fixedTime wins over top-level skyboxConfig.fixedTime; either JSON value is in turn overridden at runtime by a SkyboxTime component on engine.RootEntity.

Read Current World Time

typescript
import { getWorldTime } from '~system/Runtime'

executeTask(async () => {
  const time = await getWorldTime({})
  console.log('Seconds since midnight:', time.seconds)
})

Change Time Dynamically

typescript
import { engine, SkyboxTime, TransitionMode } from '@dcl/sdk/ecs'

// Set time of day (must target the root entity)
SkyboxTime.create(engine.RootEntity, { fixedTime: 43200 })  // Noon

// Change with transition direction
SkyboxTime.createOrReplace(engine.RootEntity, {
  fixedTime: 64800,  // Dusk (6 PM)
  transitionMode: TransitionMode.TM_BACKWARD  // TM_FORWARD (0, default) or TM_BACKWARD (1)
})

// Remove the component to hand control back to global/world time
SkyboxTime.deleteFrom(engine.RootEntity)
  • transitionMode (optional) sets the animation direction when the time changes. Default TM_FORWARD.
  • The component must live on engine.RootEntity. Deleting it reverts to the scene.json/world time (or the global day/night cycle).

Day/Night Cycle System

typescript
let currentTime = 43200
const CYCLE_SPEED = 100  // Time units per second

function dayNightCycle(dt: number) {
  currentTime = (currentTime + CYCLE_SPEED * dt) % 86400
  SkyboxTime.createOrReplace(engine.RootEntity, {
    fixedTime: currentTime
  })
}

engine.addSystem(dayNightCycle)

Realm Info

Detect which realm (server) the player is connected to:

typescript
import { getRealm } from '~system/Runtime'

executeTask(async () => {
  const realm = await getRealm({})
  console.log('Realm:', realm.realmInfo?.realmName)
  console.log('Network:', realm.realmInfo?.networkId)
  console.log('Base URL:', realm.realmInfo?.baseUrl)
})

Emissive Materials (Glow Effects)

For a visual glow without casting light on surroundings:

typescript
import { engine, Material } from '@dcl/sdk/ecs'
import { Color4, Color3 } from '@dcl/sdk/math'

// Self-illuminated material (emissiveColor uses Color3, not Color4)
Material.setPbrMaterial(entity, {
  albedoColor: Color4.create(0, 0, 0, 1),
  emissiveColor: Color3.create(0, 1, 0),  // Green glow
  emissiveIntensity: 2.0
})

Note: emissive materials don't illuminate other surrounding entities, they just have a glow effect on them.

Combining Emissive + LightSource

For an object that both glows visually and casts light:

typescript
// Visual glow on the mesh
Material.setPbrMaterial(bulb, {
  emissiveColor: Color3.create(1, 0.9, 0.7),
  emissiveIntensity: 1.5
})

// Actual light emission
LightSource.create(bulb, {
  type: LightSource.Type.Point({}),
  color: Color3.create(1, 0.9, 0.7),
  intensity: 200,
  range: 10
})

Shadow Quality

shadow is a top-level optional boolean on the LightSource component (default false). There is no shadow-type enum — quality is automatic and distance-based. Spot({...}) accepts only innerAngle? and outerAngle?.

typescript
import { LightSource } from '@dcl/sdk/ecs'

// Spot light with shadows enabled
LightSource.create(spotEntity, {
  type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
  shadow: true,  // top-level boolean
  intensity: 800
})

Constraints:

  • Shadows are only supported for spot lights; point lights do not cast shadows.
  • Max 3 shadow-casting lights rendered at a time — disable shadow on lights that don't need it. Spot lights with shadows suit dramatic effects such as flashlights.
  • Shadow quality/culling is automatic, based on the light's distance from the player. Exact distances vary by light type and the player's quality settings; general rule:
Distance from playerResult
< 10 mSoft shadows (high quality)
10–20 mHard shadows (low quality)
> 20 mNo shadows rendered
  • The light itself keeps illuminating at much larger distances — it is only disabled when the player is more than 160 m away (10 parcels). This makes LightSource suitable for large-scale setups like stage lighting at live events, where most of the audience is far from the fixtures.
  • Lights only render while the player is standing inside the scene; outside, they are not rendered.
Need advanced material effects? See the advanced-rendering skill for metallic, roughness, transparency, texture maps, texture tweens, and texture modes.

Platform Support

  • Desktop (Unity explorer): Full support (point lights, spot lights, shadows). One owner test (2026-10-09, point + two spots at default intensity) showed only the point light; not reproduced, and the spots may have been pitched up (see Spot Lights) — run the sanity test under Light Limits before calling it a client defect.
  • Mobile (Godot explorer): LightSource (scene dynamic lights) is available since v1.13.0 — no longer a parity gap. Budgeted at runtime, see below.
  • Bevy explorer: Full support (point lights, spot lights, shadows).

Verified against docs commits b4d4c88 (mobile parity tracker) and 8139a7f (mobile limits), Oct 2026.

Mobile light budget

Mobile enforces flat scene limits on authored lights: 8 soft / 16 hard light sources, 2 soft / 4 hard shadow-casting lights. Soft = warning in the mobile performance panel; hard = the scene will not load.

On top of that, each mobile graphics profile only activates a budget of the nearest light sources — 0 on Very Low, up to 8 on High — and lower profiles may disable shadows entirely. So:

  • Authoring 20 lights still costs the full scene budget even though at most 8 ever emit. Author to 8, do not lean on the runtime budget.
  • Never make gameplay-critical visibility depend on a specific light being lit: on Very Low no scene light is active. Keep a baseline readable with ambient/emissive materials and the skybox, and treat LightSource as accent.
  • Shadows are not guaranteed. Do not use a cast shadow as the only cue for an object's position.

Full mobile limit table: optimize-scene → Mobile Scene Limits.

Gotchas

  • range left unset (-1) is auto-derived from intensity as intensity^0.25 — small intensities give surprisingly short range. Set range explicitly for predictable falloff.
  • shadow only affects spot lights; setting it on a point light has no effect.
  • Animating a light's direction: put a Tween/TweenSequence (Rotate mode) on the light entity — the beam follows the entity's forward vector.
  • SkyboxTime on RootEntity overrides any scene.json fixedTime; deleteFrom reverts to it.
  • Baked light geometry is inert. GLB models with lamp/bulb meshes do not emit light -- add a LightSource component for real illumination. See the rule above.
  • Smart Item `Placeholder` needs a resolved file path, not a template variable. Setting {assetPath}/spotlight.glb as the src in an asset-packs::Placeholder results in an invisible/broken gizmo. place_smart_item resolves this automatically to e.g. assets/asset-packs/spotlight/spotlight.glb. If you must set Placeholder manually via set_component, use the real on-disk path.

Example scenes

Engine-team test scenes (real API, exercised against the engine):

  • https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/0,4-dynamic-lights — point & spot LightSource: toggle active, color, range, intensity, spot inner/outer angle at runtime, shadow-mask (gobo) swapping, and tweened light rotation.
  • https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/2,0-skybox-scene-json — fixed skybox time via top-level skyboxConfig.fixedTime; reads it back with getSceneInformation.
  • https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/3,0-skybox-world-json — fixed skybox time via worldConfiguration.skyboxConfig.fixedTime (World variant); demonstrates the worldConfiguration-wins precedence.
  • https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/2,1-skybox-sdk-scene-a and https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/3,1-skybox-sdk-scene-b — runtime SkyboxTime on RootEntity with TransitionMode, plus deleteFrom to return to global time.
同じリポジトリから

関連する Skills

すべての Skills
decentraland
コミュニティ

game-design

Plan and design Decentraland games and interactive experiences. Use when the user wants game design advice, scene architecture, performance planning, or help structuring a game. Do NOT use for specific implementation (see add-interactivity, build-ui, multiplayer-sync).

導入数
1
GitHub Stars
4
更新日
10月25日
decentraland
コミュニティ

migrate-sdk6-to-sdk7

Migrate a legacy Decentraland SDK6 scene to SDK7. Use when the user wants to port an SDK6 scene, upgrade the decentraland-ecs dependency to @dcl/sdk, or rewrite class-based scene code in the ECS style. Do NOT use for new scenes (see create-scene) or for SDK7-to-SDK7 refactors.

導入数
1
GitHub Stars
4
更新日
10月25日
decentraland
コミュニティ

player-avatar

The live player in a Decentraland scene. Use when the user wants to read player position or profile, fetch avatar appearance for off-scene addresses (parcel owners, NFT holders), trigger emotes, read equipped wearables, attach items to players/avatar (cosmetic vs held gameplay items), show a custom label plate above a player's or NPC's nametag (AvatarNametag), hide avatars or disable passports in zones (AvatarModifierArea), adjust locomotion speed, teleport the player (movePlayerTo), or listen f

導入数
1
GitHub Stars
4
更新日
10月25日
decentraland
コミュニティ

optimize-scene

Optimize Decentraland scene performance. Scene limit formulas, object pooling, LOD patterns, texture optimization, system throttling, and asset preloading. Use when the user wants to optimize performance, fix lag, reduce load time, check limits, or reduce entity/triangle count. Do NOT use for deployment (see deploy-scene).

導入数
1
GitHub Stars
3
更新日
10月11日