@dcl-regenesislabs/opendcl 0.1.0-22234509684.commit-63dfd19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +234 -0
- package/context/components-reference.md +113 -0
- package/context/open-source-3d-assets.md +705 -0
- package/context/sdk7-complete-reference.md +3684 -0
- package/context/sdk7-examples.md +1709 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/scene-context.d.ts +97 -0
- package/dist/scene-context.d.ts.map +1 -0
- package/dist/scene-context.js +203 -0
- package/dist/scene-context.js.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +4 -0
- package/dist/utils.js.map +1 -0
- package/extensions/dcl-context.ts +123 -0
- package/extensions/dcl-deploy.ts +89 -0
- package/extensions/dcl-header.ts +162 -0
- package/extensions/dcl-init.ts +62 -0
- package/extensions/dcl-preview.ts +144 -0
- package/extensions/dcl-setup-ollama.ts +312 -0
- package/extensions/dcl-setup.ts +96 -0
- package/extensions/dcl-status.ts +88 -0
- package/extensions/dcl-tasks.ts +102 -0
- package/extensions/dcl-update-check.ts +79 -0
- package/extensions/dcl-validate.ts +80 -0
- package/extensions/plan-mode/index.ts +340 -0
- package/extensions/plan-mode/utils.ts +168 -0
- package/extensions/process-registry.ts +25 -0
- package/extensions/scene-utils.ts +31 -0
- package/package.json +65 -0
- package/prompts/explain.md +16 -0
- package/prompts/review.md +19 -0
- package/prompts/system.md +126 -0
- package/skills/add-3d-models/SKILL.md +115 -0
- package/skills/add-interactivity/SKILL.md +176 -0
- package/skills/advanced-input/SKILL.md +238 -0
- package/skills/advanced-rendering/SKILL.md +235 -0
- package/skills/animations-tweens/SKILL.md +173 -0
- package/skills/audio-video/SKILL.md +167 -0
- package/skills/authoritative-server/SKILL.md +329 -0
- package/skills/build-ui/SKILL.md +231 -0
- package/skills/camera-control/SKILL.md +199 -0
- package/skills/create-scene/SKILL.md +67 -0
- package/skills/deploy-scene/SKILL.md +106 -0
- package/skills/deploy-worlds/SKILL.md +107 -0
- package/skills/lighting-environment/SKILL.md +216 -0
- package/skills/multiplayer-sync/SKILL.md +132 -0
- package/skills/nft-blockchain/SKILL.md +246 -0
- package/skills/optimize-scene/SKILL.md +160 -0
- package/skills/player-avatar/SKILL.md +239 -0
- package/skills/smart-items/SKILL.md +181 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: camera-control
|
|
3
|
+
description: Control camera behavior in Decentraland scenes. Switch between first-person and third-person with CameraMode, force camera in regions with CameraModeArea, create cinematic scripted cameras with VirtualCamera, and read camera position/rotation via MainCamera. Use when user wants camera control, cinematic views, cutscenes, or camera mode switching.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Camera Control in Decentraland
|
|
7
|
+
|
|
8
|
+
## Reading Camera State
|
|
9
|
+
|
|
10
|
+
Access the camera's current position and rotation via the reserved `engine.CameraEntity`:
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { engine, Transform, CameraMode, CameraType } from '@dcl/sdk/ecs'
|
|
14
|
+
|
|
15
|
+
function trackCamera() {
|
|
16
|
+
if (!Transform.has(engine.CameraEntity)) return
|
|
17
|
+
|
|
18
|
+
const cameraTransform = Transform.get(engine.CameraEntity)
|
|
19
|
+
console.log('Camera position:', cameraTransform.position)
|
|
20
|
+
console.log('Camera rotation:', cameraTransform.rotation)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
engine.addSystem(trackCamera)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Camera Mode Detection
|
|
27
|
+
|
|
28
|
+
Check whether the player is in first-person or third-person:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
function checkCameraMode() {
|
|
32
|
+
if (!CameraMode.has(engine.CameraEntity)) return
|
|
33
|
+
|
|
34
|
+
const cameraMode = CameraMode.get(engine.CameraEntity)
|
|
35
|
+
if (cameraMode.mode === CameraType.CT_FIRST_PERSON) {
|
|
36
|
+
console.log('First person camera')
|
|
37
|
+
} else if (cameraMode.mode === CameraType.CT_THIRD_PERSON) {
|
|
38
|
+
console.log('Third person camera')
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
engine.addSystem(checkCameraMode)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Camera Mode Values
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
CameraType.CT_FIRST_PERSON // First-person view
|
|
49
|
+
CameraType.CT_THIRD_PERSON // Third-person view (default)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## CameraModeArea (Force Camera in a Region)
|
|
53
|
+
|
|
54
|
+
Force a specific camera mode when the player enters an area:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { engine, Transform, CameraModeArea, CameraType } from '@dcl/sdk/ecs'
|
|
58
|
+
import { Vector3 } from '@dcl/sdk/math'
|
|
59
|
+
|
|
60
|
+
const fpArea = engine.addEntity()
|
|
61
|
+
Transform.create(fpArea, { position: Vector3.create(8, 1.5, 8) })
|
|
62
|
+
|
|
63
|
+
CameraModeArea.create(fpArea, {
|
|
64
|
+
area: Vector3.create(6, 4, 6), // 6x4x6 meter box
|
|
65
|
+
mode: CameraType.CT_FIRST_PERSON // Force first-person inside
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
When the player leaves the area, the camera reverts to their preferred mode.
|
|
70
|
+
|
|
71
|
+
## VirtualCamera (Cinematic Cameras)
|
|
72
|
+
|
|
73
|
+
Create scripted camera positions for cutscenes or special views:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { engine, Transform, VirtualCamera, CameraTransition } from '@dcl/sdk/ecs'
|
|
77
|
+
import { Vector3, Quaternion } from '@dcl/sdk/math'
|
|
78
|
+
|
|
79
|
+
const cinematicCam = engine.addEntity()
|
|
80
|
+
Transform.create(cinematicCam, {
|
|
81
|
+
position: Vector3.create(8, 5, 2),
|
|
82
|
+
rotation: Quaternion.fromEulerDegrees(-20, 0, 0)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
VirtualCamera.create(cinematicCam, {
|
|
86
|
+
defaultTransition: {
|
|
87
|
+
transitionMode: CameraTransition.CT_SPEED,
|
|
88
|
+
speed: 1.0
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Transition Modes
|
|
94
|
+
|
|
95
|
+
- `CameraTransition.CT_SPEED` — smooth transition at a fixed speed
|
|
96
|
+
- Higher `speed` values = faster camera movement to the virtual camera position
|
|
97
|
+
|
|
98
|
+
### Look-At Target
|
|
99
|
+
|
|
100
|
+
Make the virtual camera track an entity:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const target = engine.addEntity()
|
|
104
|
+
Transform.create(target, { position: Vector3.create(8, 1, 8) })
|
|
105
|
+
|
|
106
|
+
VirtualCamera.create(cinematicCam, {
|
|
107
|
+
lookAtEntity: target,
|
|
108
|
+
defaultTransition: {
|
|
109
|
+
transitionMode: CameraTransition.CT_SPEED,
|
|
110
|
+
speed: 2.0
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Tracking Camera Position
|
|
116
|
+
|
|
117
|
+
Poll camera position each frame for camera-triggered events:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { engine, Transform, CameraMode, CameraType } from '@dcl/sdk/ecs'
|
|
121
|
+
import { Vector3 } from '@dcl/sdk/math'
|
|
122
|
+
|
|
123
|
+
let lastNotifiedZone = ''
|
|
124
|
+
|
|
125
|
+
function cameraZoneSystem() {
|
|
126
|
+
if (!Transform.has(engine.CameraEntity)) return
|
|
127
|
+
|
|
128
|
+
const camPos = Transform.get(engine.CameraEntity).position
|
|
129
|
+
let currentZone = ''
|
|
130
|
+
|
|
131
|
+
if (camPos.y > 10) {
|
|
132
|
+
currentZone = 'sky'
|
|
133
|
+
} else if (camPos.x < 4) {
|
|
134
|
+
currentZone = 'west'
|
|
135
|
+
} else {
|
|
136
|
+
currentZone = 'center'
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (currentZone !== lastNotifiedZone) {
|
|
140
|
+
lastNotifiedZone = currentZone
|
|
141
|
+
console.log('Camera entered zone:', currentZone)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
engine.addSystem(cameraZoneSystem)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Common Patterns
|
|
149
|
+
|
|
150
|
+
### Camera-Triggered Events
|
|
151
|
+
|
|
152
|
+
Use the camera position to trigger actions when the player looks at a specific area:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
function cameraLookTrigger() {
|
|
156
|
+
const camTransform = Transform.get(engine.CameraEntity)
|
|
157
|
+
const targetPos = Vector3.create(8, 2, 8)
|
|
158
|
+
const distance = Vector3.distance(camTransform.position, targetPos)
|
|
159
|
+
|
|
160
|
+
if (distance < 5) {
|
|
161
|
+
// Player is close — check if camera is pointing at target
|
|
162
|
+
// Use raycasting for precise look detection (see add-interactivity skill)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
engine.addSystem(cameraLookTrigger)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Following an NPC
|
|
170
|
+
|
|
171
|
+
Move camera to track an NPC by updating a VirtualCamera's Transform:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
function followNpcCamera(dt: number) {
|
|
175
|
+
const npcPos = Transform.get(npcEntity).position
|
|
176
|
+
const camTransform = Transform.getMutable(cinematicCam)
|
|
177
|
+
|
|
178
|
+
// Position camera behind and above the NPC
|
|
179
|
+
camTransform.position = Vector3.create(
|
|
180
|
+
npcPos.x - 2,
|
|
181
|
+
npcPos.y + 3,
|
|
182
|
+
npcPos.z - 2
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
engine.addSystem(followNpcCamera)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Best Practices
|
|
190
|
+
|
|
191
|
+
- Only one VirtualCamera should be active at a time
|
|
192
|
+
- Use `CameraModeArea` to force first-person in tight indoor spaces
|
|
193
|
+
- Keep transition speeds between 0.5 and 3.0 for comfortable camera movement
|
|
194
|
+
- Always provide a way for the player to exit forced camera modes (e.g., leave the area)
|
|
195
|
+
- Read camera state via `engine.CameraEntity` — never try to write to it directly
|
|
196
|
+
- For look-at detection, combine camera position with raycasting (see `add-interactivity` skill)
|
|
197
|
+
- Camera control is read-only outside of VirtualCamera and CameraModeArea — you cannot directly move the player's camera
|
|
198
|
+
|
|
199
|
+
For component field details, see `context/components-reference.md`.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-scene
|
|
3
|
+
description: Scaffold a new Decentraland SDK7 scene project from scratch. Creates scene.json, package.json, tsconfig.json, and src/index.ts with a basic scene setup. Use when user wants to start a new scene, initialize a project, or set up from an empty folder.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create a New Decentraland SDK7 Scene
|
|
7
|
+
|
|
8
|
+
When the user wants to create a new scene, follow these steps:
|
|
9
|
+
|
|
10
|
+
## 1. Ask What They Want to Build
|
|
11
|
+
|
|
12
|
+
If the user hasn't described their scene, ask them:
|
|
13
|
+
- What kind of scene? (gallery, game, social space, interactive art, etc.)
|
|
14
|
+
- How many parcels? (default: 1 parcel = 16x16m)
|
|
15
|
+
- Any specific features? (3D models, interactivity, UI, multiplayer)
|
|
16
|
+
|
|
17
|
+
## 2. Scaffold the Project with `/init`
|
|
18
|
+
|
|
19
|
+
**Always run `/init` first.** This uses the official `@dcl/sdk-commands init` to create scene.json, package.json, tsconfig.json, and src/index.ts with the correct, up-to-date configuration.
|
|
20
|
+
|
|
21
|
+
Never manually create scene.json, package.json, or tsconfig.json — the SDK templates may change between versions and hand-written copies will diverge.
|
|
22
|
+
|
|
23
|
+
## 3. Customize the Generated Files
|
|
24
|
+
|
|
25
|
+
After `/init` completes, customize the generated files based on what the user wants:
|
|
26
|
+
|
|
27
|
+
### scene.json
|
|
28
|
+
Update the `display` fields and parcels:
|
|
29
|
+
- `display.title` — set to the scene name
|
|
30
|
+
- `display.description` — set to a short description
|
|
31
|
+
- `scene.parcels` — for multi-parcel scenes, list all parcels (e.g., `["0,0", "0,1", "1,0", "1,1"]` for 2x2)
|
|
32
|
+
- `scene.base` — set to the southwest corner parcel
|
|
33
|
+
|
|
34
|
+
### src/index.ts
|
|
35
|
+
Replace the generated code with the user's scene. Example:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { engine, Transform, MeshRenderer, Material } from '@dcl/sdk/ecs'
|
|
39
|
+
import { Vector3, Color4 } from '@dcl/sdk/math'
|
|
40
|
+
|
|
41
|
+
export function main() {
|
|
42
|
+
// Create a cube at the center of the scene
|
|
43
|
+
const cube = engine.addEntity()
|
|
44
|
+
Transform.create(cube, {
|
|
45
|
+
position: Vector3.create(8, 1, 8)
|
|
46
|
+
})
|
|
47
|
+
MeshRenderer.setBox(cube)
|
|
48
|
+
Material.setPbrMaterial(cube, {
|
|
49
|
+
albedoColor: Color4.create(0.2, 0.5, 1, 1)
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 4. Post-Creation Steps
|
|
55
|
+
|
|
56
|
+
After creating the files, tell the user:
|
|
57
|
+
1. Run `npm install` to install dependencies
|
|
58
|
+
2. Use the `preview` tool to start the preview server (or run `npx @dcl/sdk-commands start --bevy-web` manually)
|
|
59
|
+
3. The scene will open in a browser at http://localhost:8000
|
|
60
|
+
|
|
61
|
+
## Important Notes
|
|
62
|
+
|
|
63
|
+
- Always place objects within the scene boundaries (0 to 16*parcelsX for X, 0 to 16*parcelsZ for Z)
|
|
64
|
+
- Center of a single-parcel scene is (8, 0, 8) at ground level
|
|
65
|
+
- Y axis is up, minimum Y=0 (ground)
|
|
66
|
+
- The `main` field in scene.json MUST be `"bin/index.js"` — this is the compiled output path
|
|
67
|
+
- The `jsx` and `jsxImportSource` in tsconfig are required for React-ECS UI support
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deploy-scene
|
|
3
|
+
description: Deploy and publish a Decentraland scene to Genesis City (LAND-based). Use when user wants to deploy, publish, upload, go live, or make their scene accessible on parcels they own.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Deploying to Genesis City
|
|
7
|
+
|
|
8
|
+
Deploy to specific parcels you own or have permission to deploy to.
|
|
9
|
+
|
|
10
|
+
**Use the `/deploy` command** to deploy. It runs `npx @dcl/sdk-commands deploy` and handles the full process:
|
|
11
|
+
1. Build the scene
|
|
12
|
+
2. Upload assets to IPFS
|
|
13
|
+
3. Deploy to the specified parcels
|
|
14
|
+
4. Requires a wallet with LAND or deployment permissions
|
|
15
|
+
|
|
16
|
+
> **Deploying to a World instead?** See the `deploy-worlds` skill for Worlds deployment (personal spaces using DCL NAMEs or ENS domains).
|
|
17
|
+
|
|
18
|
+
## Pre-Deployment Checklist
|
|
19
|
+
|
|
20
|
+
Before deploying, verify:
|
|
21
|
+
|
|
22
|
+
1. **scene.json is valid**:
|
|
23
|
+
- `ecs7: true` and `runtimeVersion: "7"`
|
|
24
|
+
- Correct `parcels` matching your LAND (for Genesis City)
|
|
25
|
+
- Valid `base` parcel
|
|
26
|
+
- `main: "bin/index.js"`
|
|
27
|
+
|
|
28
|
+
2. **Code compiles**:
|
|
29
|
+
```bash
|
|
30
|
+
npx tsc --noEmit
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
3. **Scene previews correctly**:
|
|
34
|
+
Use the `preview` tool to verify the scene works (or `npx @dcl/sdk-commands start --bevy-web` manually)
|
|
35
|
+
|
|
36
|
+
4. **Dependencies installed**:
|
|
37
|
+
```bash
|
|
38
|
+
npm install
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
5. **Assets are within limits** (check parcel count):
|
|
42
|
+
| Parcels | Entities | Triangles | Textures |
|
|
43
|
+
|---------|----------|-----------|----------|
|
|
44
|
+
| 1 | 512 | 10,000 | 10 MB |
|
|
45
|
+
| 2 | 1,024 | 20,000 | 20 MB |
|
|
46
|
+
| 4 | 2,048 | 40,000 | 40 MB |
|
|
47
|
+
| 8+ | Scales linearly | | |
|
|
48
|
+
|
|
49
|
+
## Deployment Process
|
|
50
|
+
|
|
51
|
+
### Using CLI
|
|
52
|
+
```bash
|
|
53
|
+
# Build first
|
|
54
|
+
npx @dcl/sdk-commands build
|
|
55
|
+
|
|
56
|
+
# Deploy (will open browser for wallet connection)
|
|
57
|
+
npx @dcl/sdk-commands deploy
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Using Creator Hub
|
|
61
|
+
1. Open Creator Hub
|
|
62
|
+
2. Select your scene
|
|
63
|
+
3. Click "Publish"
|
|
64
|
+
4. Connect wallet
|
|
65
|
+
5. Confirm transaction
|
|
66
|
+
|
|
67
|
+
## scene.json for Deployment
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"ecs7": true,
|
|
72
|
+
"runtimeVersion": "7",
|
|
73
|
+
"display": {
|
|
74
|
+
"title": "My Awesome Scene",
|
|
75
|
+
"description": "A description for the marketplace",
|
|
76
|
+
"navmapThumbnail": "images/thumbnail.png"
|
|
77
|
+
},
|
|
78
|
+
"scene": {
|
|
79
|
+
"parcels": ["0,0", "0,1"],
|
|
80
|
+
"base": "0,0"
|
|
81
|
+
},
|
|
82
|
+
"main": "bin/index.js",
|
|
83
|
+
"requiredPermissions": []
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Permissions
|
|
88
|
+
|
|
89
|
+
Some features require permissions in scene.json:
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"requiredPermissions": [
|
|
93
|
+
"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE",
|
|
94
|
+
"ALLOW_TO_TRIGGER_AVATAR_EMOTE",
|
|
95
|
+
"ALLOW_MEDIA_HOSTNAMES"
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Best Practices
|
|
101
|
+
|
|
102
|
+
- Always preview locally before deploying
|
|
103
|
+
- Use a thumbnail image (`navmapThumbnail`) for the Genesis City map
|
|
104
|
+
- Write a clear description for discovery
|
|
105
|
+
- Test with multiple browser tabs to verify multiplayer behavior
|
|
106
|
+
- Keep scene load time under 15 seconds (optimize assets)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deploy-worlds
|
|
3
|
+
description: Deploy and publish a Decentraland scene to a World (personal 3D space). Use when user wants to deploy to a World, publish to a World, set up worldConfiguration, use a DCL NAME or ENS domain for deployment, or opt out of Places listing.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Deploying to Decentraland Worlds
|
|
7
|
+
|
|
8
|
+
Worlds are personal 3D spaces not tied to LAND. They have no parcel limitations and are automatically listed on the Places page.
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
To publish to a World, the user must own either:
|
|
13
|
+
- A **Decentraland NAME** (e.g., `my-name.dcl.eth`)
|
|
14
|
+
- An **ENS domain** (e.g., `my-name.eth`)
|
|
15
|
+
|
|
16
|
+
The wallet signing the deployment must own the NAME, or have been granted permission via Access Control Lists (ACL).
|
|
17
|
+
|
|
18
|
+
## 1. Configure scene.json
|
|
19
|
+
|
|
20
|
+
Add a `worldConfiguration` section to `scene.json`:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"worldConfiguration": {
|
|
25
|
+
"name": "my-name.dcl.eth"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The `name` field must match a Decentraland NAME or ENS domain owned by the deploying wallet.
|
|
31
|
+
|
|
32
|
+
### Opt out of Places listing
|
|
33
|
+
|
|
34
|
+
All Worlds are automatically listed on the [Places page](https://places.decentraland.org). To opt out:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"worldConfiguration": {
|
|
39
|
+
"name": "my-name.dcl.eth",
|
|
40
|
+
"placesConfig": {
|
|
41
|
+
"optOut": true
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 2. Deploy
|
|
48
|
+
|
|
49
|
+
**Use the `/deploy` command** — it auto-detects the `worldConfiguration` in scene.json and deploys to the Worlds content server automatically.
|
|
50
|
+
|
|
51
|
+
Alternatively, deploy manually via CLI:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx @dcl/sdk-commands deploy --target-content https://worlds-content-server.decentraland.org
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This will prompt the user to sign the deployment with their wallet. Validations run automatically to allow or reject the scene.
|
|
58
|
+
|
|
59
|
+
### Via Creator Hub
|
|
60
|
+
|
|
61
|
+
1. Open the scene project in Creator Hub
|
|
62
|
+
2. Click the **Publish** button (top-right corner)
|
|
63
|
+
3. Select **PUBLISH TO WORLD**
|
|
64
|
+
4. Choose which NAME or ENS domain to publish to
|
|
65
|
+
|
|
66
|
+
## 3. Access the World
|
|
67
|
+
|
|
68
|
+
Once deployed, the World is accessible at:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
decentraland://?realm=NAME.dcl.eth
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Replace `NAME` with the Decentraland NAME or ENS domain used for deployment.
|
|
75
|
+
|
|
76
|
+
From inside Decentraland, use the chatbox command:
|
|
77
|
+
```
|
|
78
|
+
/goto NAME.dcl.eth
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Full scene.json Example
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"ecs7": true,
|
|
86
|
+
"runtimeVersion": "7",
|
|
87
|
+
"display": {
|
|
88
|
+
"title": "My World",
|
|
89
|
+
"description": "A personal 3D space"
|
|
90
|
+
},
|
|
91
|
+
"scene": {
|
|
92
|
+
"parcels": ["0,0"],
|
|
93
|
+
"base": "0,0"
|
|
94
|
+
},
|
|
95
|
+
"main": "bin/index.js",
|
|
96
|
+
"worldConfiguration": {
|
|
97
|
+
"name": "my-name.dcl.eth"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Key Differences from Genesis City
|
|
103
|
+
|
|
104
|
+
- **No parcel limitations** — Worlds are not constrained by LAND ownership
|
|
105
|
+
- **NAME/ENS required** — must own a Decentraland NAME or ENS domain instead of LAND
|
|
106
|
+
- **Different deploy target** — uses `--target-content https://worlds-content-server.decentraland.org`
|
|
107
|
+
- **Auto-listed on Places** — unless opted out via `placesConfig.optOut`
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lighting-environment
|
|
3
|
+
description: Add dynamic lighting and control environment settings in Decentraland scenes. Create point, spot, and directional lights with LightSource, configure shadows, control day/night cycle with SkyboxTime, detect realm info, and use emissive materials for glow effects. Use when user wants lights, shadows, skybox, day-night cycle, or glowing materials.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Lighting and Environment in Decentraland
|
|
7
|
+
|
|
8
|
+
## Point Lights
|
|
9
|
+
|
|
10
|
+
Emit light in all directions from a position:
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { engine, Transform, LightSource } from '@dcl/sdk/ecs'
|
|
14
|
+
import { Vector3, Color3 } from '@dcl/sdk/math'
|
|
15
|
+
|
|
16
|
+
const light = engine.addEntity()
|
|
17
|
+
Transform.create(light, { position: Vector3.create(8, 3, 8) })
|
|
18
|
+
|
|
19
|
+
LightSource.create(light, {
|
|
20
|
+
type: LightSource.Type.Point({}),
|
|
21
|
+
color: Color3.White(),
|
|
22
|
+
intensity: 300 // candela
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Colored Point Light
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
LightSource.create(light, {
|
|
30
|
+
type: LightSource.Type.Point({}),
|
|
31
|
+
color: Color3.create(1, 0.5, 0), // Warm orange
|
|
32
|
+
intensity: 200,
|
|
33
|
+
range: 15 // Maximum distance in meters
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Spot Lights
|
|
38
|
+
|
|
39
|
+
Emit a cone of light in a direction:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
const spotlight = engine.addEntity()
|
|
43
|
+
Transform.create(spotlight, {
|
|
44
|
+
position: Vector3.create(8, 4, 8),
|
|
45
|
+
rotation: Quaternion.fromEulerDegrees(-90, 0, 0) // Point downward
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
LightSource.create(spotlight, {
|
|
49
|
+
type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
|
|
50
|
+
color: Color3.White(),
|
|
51
|
+
intensity: 800
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `innerAngle` — full-brightness cone angle (degrees)
|
|
56
|
+
- `outerAngle` — outer fade angle (degrees)
|
|
57
|
+
- The light direction follows the entity's forward vector (set via Transform rotation)
|
|
58
|
+
|
|
59
|
+
## Shadows
|
|
60
|
+
|
|
61
|
+
Enable shadows on point or spot lights:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
LightSource.create(spotlight, {
|
|
65
|
+
type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
|
|
66
|
+
shadow: true,
|
|
67
|
+
intensity: 800
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Shadow Mask Textures (Gobos)
|
|
72
|
+
|
|
73
|
+
Project a pattern through the light:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const maskedLight = LightSource.getMutable(spotlight)
|
|
77
|
+
maskedLight.shadowMaskTexture = Material.Texture.Common({
|
|
78
|
+
src: 'assets/scene/images/lightmask1.png'
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Toggling Lights
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Toggle on/off
|
|
86
|
+
const lightData = LightSource.getMutable(light)
|
|
87
|
+
lightData.active = !lightData.active
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Light Limits
|
|
91
|
+
|
|
92
|
+
- Maximum **one active light per parcel** (16m x 16m)
|
|
93
|
+
- The renderer auto-culls lights based on quality settings and proximity
|
|
94
|
+
- Up to ~3 shadowed lights visible at once
|
|
95
|
+
- Intensity is in candela — visible distance grows roughly with `sqrt(intensity)`
|
|
96
|
+
|
|
97
|
+
## SkyboxTime (Day/Night Cycle)
|
|
98
|
+
|
|
99
|
+
### Fixed Time in scene.json
|
|
100
|
+
|
|
101
|
+
Set a permanent time of day without code:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"skyboxConfig": {
|
|
106
|
+
"fixedTime": 36000
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Time values: 0 = midnight, 36000 = noon, 54000 = dusk, 72000 = midnight again.
|
|
112
|
+
|
|
113
|
+
### Read Current World Time
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { getWorldTime } from '~system/Runtime'
|
|
117
|
+
|
|
118
|
+
executeTask(async () => {
|
|
119
|
+
const time = await getWorldTime({})
|
|
120
|
+
console.log('Seconds since midnight:', time.seconds)
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Change Time Dynamically
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { SkyboxTime, TransitionMode } from '~system/Runtime'
|
|
128
|
+
|
|
129
|
+
// Set time of day (must target root entity)
|
|
130
|
+
SkyboxTime.create(engine.RootEntity, { fixed_time: 36000 }) // Noon
|
|
131
|
+
|
|
132
|
+
// Change with transition direction
|
|
133
|
+
SkyboxTime.createOrReplace(engine.RootEntity, {
|
|
134
|
+
fixed_time: 54000, // Dusk
|
|
135
|
+
direction: TransitionMode.TM_BACKWARD
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Day/Night Cycle System
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
let currentTime = 36000
|
|
143
|
+
const CYCLE_SPEED = 100 // Time units per second
|
|
144
|
+
|
|
145
|
+
function dayNightCycle(dt: number) {
|
|
146
|
+
currentTime = (currentTime + CYCLE_SPEED * dt) % 72000
|
|
147
|
+
SkyboxTime.createOrReplace(engine.RootEntity, {
|
|
148
|
+
fixed_time: currentTime
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
engine.addSystem(dayNightCycle)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Realm Info
|
|
156
|
+
|
|
157
|
+
Detect which realm (server) the player is connected to:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { getRealm } from '~system/Runtime'
|
|
161
|
+
|
|
162
|
+
executeTask(async () => {
|
|
163
|
+
const realm = await getRealm({})
|
|
164
|
+
console.log('Realm:', realm.realmInfo?.realmName)
|
|
165
|
+
console.log('Network:', realm.realmInfo?.networkId)
|
|
166
|
+
console.log('Base URL:', realm.realmInfo?.baseUrl)
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Emissive Materials (Glow Effects)
|
|
171
|
+
|
|
172
|
+
For a visual glow without casting light on surroundings:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { engine, Material } from '@dcl/sdk/ecs'
|
|
176
|
+
import { Color4, Color3 } from '@dcl/sdk/math'
|
|
177
|
+
|
|
178
|
+
// Self-illuminated material
|
|
179
|
+
Material.setPbrMaterial(entity, {
|
|
180
|
+
albedoColor: Color4.create(0, 0, 0, 1),
|
|
181
|
+
emissiveColor: Color4.create(0, 1, 0, 1), // Green glow
|
|
182
|
+
emissiveIntensity: 2.0
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Combining Emissive + LightSource
|
|
187
|
+
|
|
188
|
+
For an object that both glows visually and casts light:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Visual glow on the mesh
|
|
192
|
+
Material.setPbrMaterial(bulb, {
|
|
193
|
+
emissiveColor: Color4.create(1, 0.9, 0.7, 1),
|
|
194
|
+
emissiveIntensity: 1.5
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// Actual light emission
|
|
198
|
+
LightSource.create(bulb, {
|
|
199
|
+
type: LightSource.Type.Point({}),
|
|
200
|
+
color: Color3.create(1, 0.9, 0.7),
|
|
201
|
+
intensity: 200,
|
|
202
|
+
range: 10
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Best Practices
|
|
207
|
+
|
|
208
|
+
- Stay within the **one light per parcel** budget — plan light placement around scene parcels
|
|
209
|
+
- Use emissive materials for decorative glow that doesn't need to illuminate surroundings
|
|
210
|
+
- Combine emissive materials with LightSource for realistic light fixtures (lamp = emissive mesh + point light)
|
|
211
|
+
- Use spot lights with shadows for dramatic effects (stage lighting, flashlights)
|
|
212
|
+
- Keep shadow count low (max ~3 visible) — disable `shadow` on lights that don't need it
|
|
213
|
+
- Set `range` on lights to limit their influence and save performance
|
|
214
|
+
- Use `SkyboxTime` for atmosphere — nighttime scenes with point lights create dramatic environments
|
|
215
|
+
|
|
216
|
+
For component field details, see `context/components-reference.md`.
|