@dcl-regenesislabs/opendcl 0.1.4 → 0.1.5-23161709858.commit-828c176
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 +4 -3
- package/context/sdk7-cheat-sheet.md +3 -3
- package/extensions/permissions/index.ts +1 -12
- package/package.json +2 -2
- package/skills/add-3d-models/SKILL.md +16 -1
- package/skills/add-interactivity/SKILL.md +146 -37
- package/skills/add-interactivity/references/input-reference.md +148 -0
- package/skills/advanced-input/SKILL.md +1 -1
- package/skills/advanced-rendering/SKILL.md +27 -11
- package/skills/animations-tweens/SKILL.md +54 -24
- package/skills/audio-video/SKILL.md +16 -1
- package/skills/audio-video/references/media-reference.md +184 -0
- package/skills/authoritative-server/SKILL.md +6 -4
- package/skills/authoritative-server/references/server-patterns.md +251 -0
- package/skills/build-ui/SKILL.md +27 -1
- package/skills/build-ui/references/ui-components.md +228 -0
- package/skills/camera-control/SKILL.md +3 -1
- package/skills/create-scene/SKILL.md +65 -1
- package/skills/deploy-scene/SKILL.md +26 -9
- package/skills/deploy-worlds/SKILL.md +13 -1
- package/skills/game-design/SKILL.md +230 -0
- package/skills/lighting-environment/SKILL.md +23 -15
- package/skills/multiplayer-sync/SKILL.md +198 -72
- package/skills/multiplayer-sync/references/networking-patterns.md +238 -0
- package/skills/nft-blockchain/SKILL.md +18 -31
- package/skills/optimize-scene/SKILL.md +55 -12
- package/skills/player-avatar/SKILL.md +18 -1
- package/skills/player-avatar/references/avatar-apis.md +152 -0
- package/skills/scene-runtime/SKILL.md +4 -2
- package/skills/scene-runtime/references/runtime-apis.md +206 -0
- package/skills/visual-feedback/SKILL.md +16 -1
package/README.md
CHANGED
|
@@ -28,11 +28,11 @@ The result: **more creators building more scenes, faster.**
|
|
|
28
28
|
- **Branded header** — on startup, displays a block-character "Decentraland" ASCII art banner with version and working directory. Falls back to a compact text header on narrow terminals
|
|
29
29
|
- **Multi-provider LLM support** — works with Claude, OpenAI, Google, Ollama (free/local), OpenRouter, and more
|
|
30
30
|
- **Scene-aware** — automatically detects your project's `scene.json`, SDK version, and entry points
|
|
31
|
-
- **
|
|
31
|
+
- **20 built-in skills** — scaffolding, 3D models, interactivity, UI, animations, multiplayer, authoritative server, audio/video, deployment (Genesis City & Worlds), optimization, camera control, lighting, player/avatar, NFT/blockchain, advanced rendering, advanced input, scene runtime, visual feedback, game design
|
|
32
32
|
- **Integrated commands** — `/init` to scaffold, `/preview` to launch the dev server, `/tasks` to manage running processes, `/review` to audit code
|
|
33
33
|
- **TypeScript validation** — catches type errors immediately after writing code
|
|
34
34
|
- **Free asset catalogs** — 2,700+ Creator Hub 3D models, 900+ CC0-licensed models, and 50 audio files the agent proactively suggests when building scenes
|
|
35
|
-
- **Permission gate** — prompts for confirmation before destructive bash commands, writes to sensitive files, or
|
|
35
|
+
- **Permission gate** — prompts for confirmation before destructive bash commands, writes to sensitive files, or writes/edits outside the working directory
|
|
36
36
|
- **Compact tool output** — write shows path + size instead of file content, read shows a 5-line preview instead of 10
|
|
37
37
|
- **Session persistence** — pick up where you left off across sessions
|
|
38
38
|
|
|
@@ -128,6 +128,7 @@ OpenDCL loads domain-specific skills on demand based on what you're asking:
|
|
|
128
128
|
| `advanced-input` | Cursor state, movement restriction, WASD patterns |
|
|
129
129
|
| `scene-runtime` | Async tasks, fetch, timers, realm info, restricted actions, testing |
|
|
130
130
|
| `visual-feedback` | Use the screenshot tool to see your scene, verify changes, iterate visually |
|
|
131
|
+
| `game-design` | Plan game architecture, scene limits, state management, MVP planning |
|
|
131
132
|
|
|
132
133
|
## How It Works
|
|
133
134
|
|
|
@@ -160,7 +161,7 @@ opendcl/
|
|
|
160
161
|
│ ├── dcl-tasks.ts # /tasks command (process manager)
|
|
161
162
|
│ ├── process-registry.ts # Shared background process registry
|
|
162
163
|
│ └── permissions/ # Permission gate for dangerous operations
|
|
163
|
-
├── skills/ #
|
|
164
|
+
├── skills/ # 20 SKILL.md files (domain expertise)
|
|
164
165
|
├── prompts/ # System prompt + command templates
|
|
165
166
|
├── context/ # SDK7 reference docs + asset catalog
|
|
166
167
|
└── tests/ # Vitest test suites
|
|
@@ -18,8 +18,8 @@ import ReactEcs, { ReactEcsRenderer, UiEntity, Label, Button, Input, Dropdown }
|
|
|
18
18
|
import { movePlayerTo, teleportTo, triggerEmote, changeRealm,
|
|
19
19
|
openExternalUrl, openNftDialog, triggerSceneEmote,
|
|
20
20
|
copyToClipboard } from '~system/RestrictedActions'
|
|
21
|
-
import { getSceneInformation, getRealm, readFile
|
|
22
|
-
|
|
21
|
+
import { getSceneInformation, getRealm, readFile, getWorldTime,
|
|
22
|
+
getExplorerInformation } from '~system/Runtime'
|
|
23
23
|
import { signedFetch, getHeaders } from '~system/SignedFetch'
|
|
24
24
|
import { getPlayer } from '@dcl/sdk/src/players'
|
|
25
25
|
```
|
|
@@ -136,7 +136,7 @@ ColliderLayer.CL_CUSTOM1 … CL_CUSTOM8 // user-defined layers
|
|
|
136
136
|
"featureToggles": { "voiceChat": "enabled" },
|
|
137
137
|
"worldConfiguration": {
|
|
138
138
|
"name": "my-world.dcl.eth",
|
|
139
|
-
"skyboxConfig": { "
|
|
139
|
+
"skyboxConfig": { "fixedTime": 36000 }
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
```
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
10
|
-
import { classifyBashCommand, classifyFilePath,
|
|
10
|
+
import { classifyBashCommand, classifyFilePath, OUTSIDE_CWD_REASON } from "./utils.js";
|
|
11
11
|
import { resolve } from "node:path";
|
|
12
12
|
|
|
13
13
|
type BlockResult = { block: true; reason: string };
|
|
@@ -91,17 +91,6 @@ const extension: ExtensionFactory = (pi) => {
|
|
|
91
91
|
return promptOrBlock(ctx, reason, `Path: ${filePath}`, () => sessionAllow.add(reason));
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
if (toolName === "read" || toolName === "grep" || toolName === "find" || toolName === "ls") {
|
|
95
|
-
const filePath = (event.input as { path?: string }).path ?? "";
|
|
96
|
-
if (!filePath) return;
|
|
97
|
-
|
|
98
|
-
const resolved = resolve(ctx.cwd, filePath);
|
|
99
|
-
const reason = isOutsideCwd(filePath, ctx.cwd);
|
|
100
|
-
if (!reason) return;
|
|
101
|
-
if (isPathAllowed(resolved)) return;
|
|
102
|
-
|
|
103
|
-
return promptOrBlock(ctx, reason, `Path: ${filePath}`, () => allowedPaths.add(resolved));
|
|
104
|
-
}
|
|
105
94
|
});
|
|
106
95
|
};
|
|
107
96
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dcl-regenesislabs/opendcl",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5-23161709858.commit-828c176",
|
|
4
4
|
"description": "AI coding assistant for Decentraland SDK7 scene development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"prompts/",
|
|
68
68
|
"context/"
|
|
69
69
|
],
|
|
70
|
-
"commit": "
|
|
70
|
+
"commit": "828c176b27aa3da4e6eeebd001e2434c1df161fe"
|
|
71
71
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: add-3d-models
|
|
3
|
-
description: Add 3D models (.glb/.gltf) to a Decentraland scene using GltfContainer. Covers loading
|
|
3
|
+
description: Add 3D models (.glb/.gltf) to a Decentraland scene using GltfContainer. Covers loading, positioning, scaling, colliders, parenting, and browsing 2,700+ free assets from the Creator Hub catalog and 991 CC0 models. Use when the user wants to add models, import GLB files, find free 3D assets, or set up model colliders. Do NOT use for materials/textures (see advanced-rendering) or model animations (see animations-tweens).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Adding 3D Models to Decentraland Scenes
|
|
@@ -145,6 +145,21 @@ engine.addSystem(() => {
|
|
|
145
145
|
})
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
+
## Troubleshooting
|
|
149
|
+
|
|
150
|
+
| Problem | Cause | Solution |
|
|
151
|
+
|---------|-------|----------|
|
|
152
|
+
| Model not visible | Wrong file path | Verify the file exists at the exact path relative to project root (e.g., `models/myModel.glb`) |
|
|
153
|
+
| Model not visible | Position outside scene boundaries | Check Transform position is within 0-16 per parcel. Center of 1-parcel scene is (8, 0, 8) |
|
|
154
|
+
| Model not visible | Scale is 0 or very small | Check `Transform.scale` — default is (1,1,1). Try larger values if model was exported very small |
|
|
155
|
+
| Model not visible | Behind the camera | Move the avatar or rotate to look in the model's direction |
|
|
156
|
+
| Model loads but looks wrong | Y-up vs Z-up mismatch | Decentraland uses Y-up. Re-export from Blender with "Y Up" checked |
|
|
157
|
+
| "FINISHED_WITH_ERROR" load state | Corrupted or unsupported .glb | Re-export the model. Use `.glb` (binary GLTF) format. Ensure no unsupported extensions |
|
|
158
|
+
| Clicking model does nothing | Missing collider | Add `visibleMeshesCollisionMask: ColliderLayer.CL_POINTER` to `GltfContainer` or add `MeshCollider` |
|
|
159
|
+
|
|
160
|
+
> **Need to optimize models for scene limits?** See the **optimize-scene** skill for triangle budgets and LOD patterns.
|
|
161
|
+
> **Need animations from your model?** See the **animations-tweens** skill for playing GLTF animation clips with Animator.
|
|
162
|
+
|
|
148
163
|
## Model Best Practices
|
|
149
164
|
|
|
150
165
|
- Keep models under 50MB per file for good loading times
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: add-interactivity
|
|
3
|
-
description: Add click handlers, hover effects, pointer events,
|
|
3
|
+
description: Add click handlers, hover effects, pointer events, trigger areas, raycasting, and global input to Decentraland scene entities. Use when the user wants to make objects clickable, add hover effects, detect player proximity, handle E/F key actions, or cast rays. Do NOT use for advanced input patterns like movement restriction, cursor lock, or WASD control (see advanced-input). Do NOT use for screen-space UI buttons (see build-ui).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Adding Interactivity to Decentraland Scenes
|
|
7
7
|
|
|
8
|
+
## Decision Tree
|
|
9
|
+
|
|
10
|
+
| Need | Approach | API |
|
|
11
|
+
|------|----------|-----|
|
|
12
|
+
| Click/hover on a specific entity | Pointer events | `pointerEventsSystem.onPointerDown()` |
|
|
13
|
+
| Detect player entering an area | Trigger area | `TriggerArea` + `triggerAreaEventsSystem` |
|
|
14
|
+
| Poll key state every frame | Global input | `inputSystem.isTriggered()` / `isPressed()` |
|
|
15
|
+
| Detect objects in a direction | Raycasting | `raycastSystem` or `Raycast` component |
|
|
16
|
+
| Read cursor position / lock state | Cursor state | `PointerLock`, `PrimaryPointerInfo` |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
8
20
|
## Pointer Events (Click / Hover)
|
|
9
21
|
|
|
10
22
|
### Using the Helper System (Recommended)
|
|
@@ -32,15 +44,29 @@ pointerEventsSystem.onPointerDown(
|
|
|
32
44
|
)
|
|
33
45
|
```
|
|
34
46
|
|
|
35
|
-
###
|
|
47
|
+
### All Input Actions
|
|
48
|
+
```typescript
|
|
49
|
+
InputAction.IA_POINTER // Left mouse button
|
|
50
|
+
InputAction.IA_PRIMARY // E key
|
|
51
|
+
InputAction.IA_SECONDARY // F key
|
|
52
|
+
InputAction.IA_ACTION_3 // 1 key
|
|
53
|
+
InputAction.IA_ACTION_4 // 2 key
|
|
54
|
+
InputAction.IA_ACTION_5 // 3 key
|
|
55
|
+
InputAction.IA_ACTION_6 // 4 key
|
|
56
|
+
InputAction.IA_JUMP // Space key
|
|
57
|
+
InputAction.IA_FORWARD // W key
|
|
58
|
+
InputAction.IA_BACKWARD // S key
|
|
59
|
+
InputAction.IA_LEFT // A key
|
|
60
|
+
InputAction.IA_RIGHT // D key
|
|
61
|
+
InputAction.IA_WALK // Shift key
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### All Event Types
|
|
36
65
|
```typescript
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
InputAction.IA_ACTION_4 // Key 2
|
|
42
|
-
InputAction.IA_ACTION_5 // Key 3
|
|
43
|
-
InputAction.IA_ACTION_6 // Key 4
|
|
66
|
+
PointerEventType.PET_DOWN // Button pressed
|
|
67
|
+
PointerEventType.PET_UP // Button released
|
|
68
|
+
PointerEventType.PET_HOVER_ENTER // Cursor enters entity
|
|
69
|
+
PointerEventType.PET_HOVER_LEAVE // Cursor leaves entity
|
|
44
70
|
```
|
|
45
71
|
|
|
46
72
|
### Pointer Up (Release)
|
|
@@ -77,6 +103,8 @@ GltfContainer.create(entity, {
|
|
|
77
103
|
})
|
|
78
104
|
```
|
|
79
105
|
|
|
106
|
+
---
|
|
107
|
+
|
|
80
108
|
## Trigger Areas (Proximity Detection)
|
|
81
109
|
|
|
82
110
|
Detect when the player enters, exits, or stays inside an area:
|
|
@@ -121,9 +149,66 @@ Transform.create(mover, { position: Vector3.create(8, 0, 8) })
|
|
|
121
149
|
MeshCollider.setBox(mover, ColliderLayer.CL_CUSTOM1)
|
|
122
150
|
```
|
|
123
151
|
|
|
152
|
+
---
|
|
153
|
+
|
|
124
154
|
## Raycasting
|
|
125
155
|
|
|
126
|
-
|
|
156
|
+
### Raycast Direction Types
|
|
157
|
+
|
|
158
|
+
Four direction modes are available:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// 1. Local direction — relative to entity rotation
|
|
162
|
+
{ $case: 'localDirection', localDirection: Vector3.Forward() }
|
|
163
|
+
|
|
164
|
+
// 2. Global direction — world-space, ignores entity rotation
|
|
165
|
+
{ $case: 'globalDirection', globalDirection: Vector3.Down() }
|
|
166
|
+
|
|
167
|
+
// 3. Global target — aim at a world position
|
|
168
|
+
{ $case: 'globalTarget', globalTarget: Vector3.create(10, 0, 10) }
|
|
169
|
+
|
|
170
|
+
// 4. Target entity — aim at another entity
|
|
171
|
+
{ $case: 'targetEntity', targetEntity: entityId }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Callback-Based Raycasting (Recommended)
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { raycastSystem, RaycastQueryType, ColliderLayer } from '@dcl/sdk/ecs'
|
|
178
|
+
|
|
179
|
+
// Local direction raycast
|
|
180
|
+
raycastSystem.registerLocalDirectionRaycast(
|
|
181
|
+
{ entity: myEntity, opts: { queryType: RaycastQueryType.RQT_HIT_FIRST, direction: Vector3.Forward(), maxDistance: 16, collisionMask: ColliderLayer.CL_POINTER } },
|
|
182
|
+
(result) => {
|
|
183
|
+
if (result.hits.length > 0) {
|
|
184
|
+
console.log('Hit:', result.hits[0].entityId)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
// Global direction raycast
|
|
190
|
+
raycastSystem.registerGlobalDirectionRaycast(
|
|
191
|
+
{ entity: myEntity, opts: { queryType: RaycastQueryType.RQT_HIT_FIRST, direction: Vector3.Down(), maxDistance: 20 } },
|
|
192
|
+
(result) => { /* handle hits */ }
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
// Target position raycast
|
|
196
|
+
raycastSystem.registerGlobalTargetRaycast(
|
|
197
|
+
{ entity: myEntity, opts: { globalTarget: Vector3.create(8, 0, 8), maxDistance: 20 } },
|
|
198
|
+
(result) => { /* handle result */ }
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
// Target entity raycast
|
|
202
|
+
raycastSystem.registerTargetEntityRaycast(
|
|
203
|
+
{ entity: sourceEntity, opts: { targetEntity: targetEntity, maxDistance: 15 } },
|
|
204
|
+
(result) => { /* handle result */ }
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
// Remove raycast from entity
|
|
208
|
+
raycastSystem.removeRaycasterEntity(myEntity)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Component-Based Raycasting
|
|
127
212
|
|
|
128
213
|
```typescript
|
|
129
214
|
import { engine, Raycast, RaycastResult, RaycastQueryType } from '@dcl/sdk/ecs'
|
|
@@ -147,6 +232,27 @@ engine.addSystem(() => {
|
|
|
147
232
|
})
|
|
148
233
|
```
|
|
149
234
|
|
|
235
|
+
### Camera Raycast
|
|
236
|
+
|
|
237
|
+
Cast a ray from the camera to detect what the player is looking at:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
raycastSystem.registerGlobalDirectionRaycast(
|
|
241
|
+
{
|
|
242
|
+
entity: engine.CameraEntity,
|
|
243
|
+
opts: {
|
|
244
|
+
direction: Vector3.rotate(Vector3.Forward(), Transform.get(engine.CameraEntity).rotation),
|
|
245
|
+
maxDistance: 16
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
(result) => {
|
|
249
|
+
if (result.hits.length > 0) console.log('Looking at:', result.hits[0].entityId)
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
150
256
|
## Global Input Handling
|
|
151
257
|
|
|
152
258
|
Listen for key presses anywhere (not entity-specific):
|
|
@@ -164,9 +270,35 @@ engine.addSystem(() => {
|
|
|
164
270
|
if (inputSystem.isPressed(InputAction.IA_SECONDARY)) {
|
|
165
271
|
console.log('F key is held!')
|
|
166
272
|
}
|
|
273
|
+
|
|
274
|
+
// Entity-specific input via system
|
|
275
|
+
const clickData = inputSystem.getInputCommand(
|
|
276
|
+
InputAction.IA_POINTER,
|
|
277
|
+
PointerEventType.PET_DOWN,
|
|
278
|
+
myEntity
|
|
279
|
+
)
|
|
280
|
+
if (clickData) {
|
|
281
|
+
console.log('Entity clicked via system:', clickData.hit.entityId)
|
|
282
|
+
}
|
|
167
283
|
})
|
|
168
284
|
```
|
|
169
285
|
|
|
286
|
+
## Cursor State
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { PointerLock, PrimaryPointerInfo } from '@dcl/sdk/ecs'
|
|
290
|
+
|
|
291
|
+
// Check if cursor is locked
|
|
292
|
+
const isLocked = PointerLock.get(engine.CameraEntity).isPointerLocked
|
|
293
|
+
|
|
294
|
+
// Get cursor position and world ray
|
|
295
|
+
const pointerInfo = PrimaryPointerInfo.get(engine.RootEntity)
|
|
296
|
+
console.log('Cursor position:', pointerInfo.screenCoordinates)
|
|
297
|
+
console.log('World ray direction:', pointerInfo.worldRayDirection)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
170
302
|
## Toggle Pattern (Click to Switch States)
|
|
171
303
|
|
|
172
304
|
Common pattern for toggleable objects:
|
|
@@ -186,33 +318,6 @@ pointerEventsSystem.onPointerDown(
|
|
|
186
318
|
)
|
|
187
319
|
```
|
|
188
320
|
|
|
189
|
-
### Raycast System Helpers
|
|
190
|
-
|
|
191
|
-
Use `raycastSystem` for convenient raycasting without manual component management:
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
import { raycastSystem, RaycastQueryType, ColliderLayer } from '@dcl/sdk/ecs'
|
|
195
|
-
|
|
196
|
-
// Register a continuous local-direction raycast
|
|
197
|
-
raycastSystem.registerLocalDirectionRaycast(
|
|
198
|
-
{ entity: myEntity, opts: { queryType: RaycastQueryType.RQT_HIT_FIRST, direction: Vector3.Forward(), maxDistance: 16, collisionMask: ColliderLayer.CL_POINTER } },
|
|
199
|
-
(result) => {
|
|
200
|
-
if (result.hits.length > 0) {
|
|
201
|
-
console.log('Hit:', result.hits[0].entityId)
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
// Register a global-direction raycast
|
|
207
|
-
raycastSystem.registerGlobalDirectionRaycast(
|
|
208
|
-
{ entity: myEntity, opts: { queryType: RaycastQueryType.RQT_HIT_FIRST, direction: Vector3.Down(), maxDistance: 20 } },
|
|
209
|
-
(result) => { /* handle hits */ }
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
// Remove raycast from entity
|
|
213
|
-
raycastSystem.removeRaycasterEntity(myEntity)
|
|
214
|
-
```
|
|
215
|
-
|
|
216
321
|
## Best Practices
|
|
217
322
|
|
|
218
323
|
- Always set `maxDistance` on pointer events (8-16m is typical)
|
|
@@ -221,3 +326,7 @@ raycastSystem.removeRaycasterEntity(myEntity)
|
|
|
221
326
|
- Use `MeshCollider` for invisible trigger surfaces
|
|
222
327
|
- For complex interactions, use a system with state tracking
|
|
223
328
|
- Test interactions in preview — hover text should be visible and clear
|
|
329
|
+
- Set `continuous: false` on raycasts unless you need per-frame results
|
|
330
|
+
- Design for both desktop and mobile — mobile has no keyboard, rely on pointer and on-screen buttons
|
|
331
|
+
|
|
332
|
+
For the full input action list and advanced patterns, see `{baseDir}/references/input-reference.md`.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Input System Reference
|
|
2
|
+
|
|
3
|
+
## All Input Actions
|
|
4
|
+
|
|
5
|
+
| Action | Key Binding | Constant |
|
|
6
|
+
|--------|-------------|----------|
|
|
7
|
+
| Left mouse button | Mouse click / tap | `InputAction.IA_POINTER` |
|
|
8
|
+
| Primary action | E key | `InputAction.IA_PRIMARY` |
|
|
9
|
+
| Secondary action | F key | `InputAction.IA_SECONDARY` |
|
|
10
|
+
| Action 3 | 1 key | `InputAction.IA_ACTION_3` |
|
|
11
|
+
| Action 4 | 2 key | `InputAction.IA_ACTION_4` |
|
|
12
|
+
| Action 5 | 3 key | `InputAction.IA_ACTION_5` |
|
|
13
|
+
| Action 6 | 4 key | `InputAction.IA_ACTION_6` |
|
|
14
|
+
| Jump | Space key | `InputAction.IA_JUMP` |
|
|
15
|
+
| Forward | W key | `InputAction.IA_FORWARD` |
|
|
16
|
+
| Backward | S key | `InputAction.IA_BACKWARD` |
|
|
17
|
+
| Left | A key | `InputAction.IA_LEFT` |
|
|
18
|
+
| Right | D key | `InputAction.IA_RIGHT` |
|
|
19
|
+
| Walk | Shift key | `InputAction.IA_WALK` |
|
|
20
|
+
|
|
21
|
+
**Notes:**
|
|
22
|
+
- Mouse wheel is **not available** as an input
|
|
23
|
+
- Always design for both desktop and mobile — mobile has no keyboard, rely on pointer and on-screen buttons
|
|
24
|
+
- Set `maxDistance` on pointer events (8-10 meters typical) to prevent interactions from across the scene
|
|
25
|
+
- Use `hoverText` to communicate what an interaction does before the player commits
|
|
26
|
+
|
|
27
|
+
## All Pointer Event Types
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
PointerEventType.PET_DOWN // Button/key pressed
|
|
31
|
+
PointerEventType.PET_UP // Button/key released
|
|
32
|
+
PointerEventType.PET_HOVER_ENTER // Cursor enters entity bounds
|
|
33
|
+
PointerEventType.PET_HOVER_LEAVE // Cursor leaves entity bounds
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Declarative Pointer Events Component
|
|
37
|
+
|
|
38
|
+
Instead of the callback system, you can use the `PointerEvents` component directly:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { PointerEvents, PointerEventType, InputAction } from '@dcl/sdk/ecs'
|
|
42
|
+
|
|
43
|
+
PointerEvents.create(entity, {
|
|
44
|
+
pointerEvents: [
|
|
45
|
+
{
|
|
46
|
+
eventType: PointerEventType.PET_DOWN,
|
|
47
|
+
eventInfo: {
|
|
48
|
+
button: InputAction.IA_POINTER,
|
|
49
|
+
hoverText: 'Click me',
|
|
50
|
+
showFeedback: true,
|
|
51
|
+
maxDistance: 10
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Then read results in a system using `inputSystem.getInputCommand()`.
|
|
59
|
+
|
|
60
|
+
## Raycast Direction Types
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// 1. Local direction — relative to entity rotation
|
|
64
|
+
{ $case: 'localDirection', localDirection: Vector3.Forward() }
|
|
65
|
+
|
|
66
|
+
// 2. Global direction — world-space direction, ignores entity rotation
|
|
67
|
+
{ $case: 'globalDirection', globalDirection: Vector3.Down() }
|
|
68
|
+
|
|
69
|
+
// 3. Global target — aim at a specific world position
|
|
70
|
+
{ $case: 'globalTarget', globalTarget: Vector3.create(10, 0, 10) }
|
|
71
|
+
|
|
72
|
+
// 4. Target entity — aim at another entity dynamically
|
|
73
|
+
{ $case: 'targetEntity', targetEntity: entityId }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Raycast Options
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
{
|
|
80
|
+
direction: Vector3.Forward(),
|
|
81
|
+
maxDistance: 16,
|
|
82
|
+
queryType: RaycastQueryType.RQT_HIT_FIRST, // or RQT_QUERY_ALL
|
|
83
|
+
originOffset: Vector3.create(0, 0.5, 0), // offset from entity origin
|
|
84
|
+
collisionMask: ColliderLayer.CL_PHYSICS | ColliderLayer.CL_CUSTOM1,
|
|
85
|
+
continuous: false // true = every frame, false = one-shot
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Camera Raycast
|
|
90
|
+
|
|
91
|
+
Cast a ray from the camera to detect what the player is looking at:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
raycastSystem.registerGlobalDirectionRaycast(
|
|
95
|
+
{
|
|
96
|
+
entity: engine.CameraEntity,
|
|
97
|
+
opts: {
|
|
98
|
+
direction: Vector3.rotate(Vector3.Forward(), Transform.get(engine.CameraEntity).rotation),
|
|
99
|
+
maxDistance: 16
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
(result) => {
|
|
103
|
+
if (result.hits.length > 0) console.log('Looking at:', result.hits[0].entityId)
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Avatar Modifier Areas
|
|
109
|
+
|
|
110
|
+
Modify how avatars appear or behave in a region:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { AvatarModifierArea, AvatarModifierType } from '@dcl/sdk/ecs'
|
|
114
|
+
|
|
115
|
+
AvatarModifierArea.create(entity, {
|
|
116
|
+
area: { box: Vector3.create(4, 3, 4) },
|
|
117
|
+
modifiers: [AvatarModifierType.AMT_HIDE_AVATARS],
|
|
118
|
+
excludeIds: ['0x123...abc'] // Optional
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Available modifiers:
|
|
122
|
+
// AMT_HIDE_AVATARS — Hide all avatars in the area
|
|
123
|
+
// AMT_DISABLE_PASSPORTS — Disable clicking on avatars to see profiles
|
|
124
|
+
// AMT_DISABLE_JUMPING — Prevent jumping in the area
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Cursor State
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Check if cursor is locked (pointer lock mode)
|
|
131
|
+
const isLocked = PointerLock.get(engine.CameraEntity).isPointerLocked
|
|
132
|
+
|
|
133
|
+
// Get cursor position and world ray
|
|
134
|
+
const pointerInfo = PrimaryPointerInfo.get(engine.RootEntity)
|
|
135
|
+
console.log('Cursor screen position:', pointerInfo.screenCoordinates)
|
|
136
|
+
console.log('World ray direction:', pointerInfo.worldRayDirection)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Trigger Area Callback Fields
|
|
140
|
+
|
|
141
|
+
The trigger area event callback provides:
|
|
142
|
+
- `triggeredEntity` — the entity that activated the area
|
|
143
|
+
- `eventType` — ENTER, EXIT, or STAY
|
|
144
|
+
- `trigger.entity` — the trigger area entity
|
|
145
|
+
- `trigger.layer` — the collider layer
|
|
146
|
+
- `trigger.position` — position of the triggered entity
|
|
147
|
+
- `trigger.rotation` — rotation of the triggered entity
|
|
148
|
+
- `trigger.scale` — scale of the triggered entity
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: advanced-input
|
|
3
|
-
description: Advanced input handling in Decentraland
|
|
3
|
+
description: Advanced input handling in Decentraland. PointerLock (cursor capture state), InputModifier (freeze/restrict player movement), PrimaryPointerInfo (cursor position and world ray), WASD keyboard patterns, and action bar slots. Use when the user wants movement restriction, cursor control, FPS controls, input polling, or cutscene freezing. Do NOT use for basic click/hover events on entities (see add-interactivity).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Advanced Input Handling in Decentraland
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: advanced-rendering
|
|
3
|
-
description: Advanced rendering
|
|
3
|
+
description: Advanced rendering in Decentraland scenes. Billboard (face camera), TextShape (3D world text), PBR materials (metallic, roughness, transparency, emissive glow), GltfNodeModifiers (per-node shadow/material overrides), VisibilityComponent (show/hide entities), and texture modes. Use when the user wants billboards, floating labels, 3D text, material effects, glow, transparency, or model node control. Do NOT use for screen-space UI (see build-ui) or loading 3D models (see add-3d-models).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Advanced Rendering in Decentraland
|
|
7
7
|
|
|
8
|
+
## When to Use Which Rendering Feature
|
|
9
|
+
|
|
10
|
+
| Need | Component | When |
|
|
11
|
+
|------|-----------|------|
|
|
12
|
+
| Entity faces the camera | `Billboard` | Name tags, signs, sprite-like objects |
|
|
13
|
+
| Text in the 3D world | `TextShape` | Labels, signs, floating text above entities |
|
|
14
|
+
| Custom material appearance | `Material.setPbrMaterial` | Metallic, rough, transparent, emissive surfaces |
|
|
15
|
+
| Show/hide without removing | `VisibilityComponent` | LOD systems, toggling objects, conditional display |
|
|
16
|
+
| Modify GLTF model nodes | `GltfNodeModifiers` | Override materials or shadow casting on specific mesh nodes |
|
|
17
|
+
|
|
18
|
+
**Decision flow:**
|
|
19
|
+
1. Need text on screen? → Use **build-ui** (React-ECS Label) instead
|
|
20
|
+
2. Need text in 3D space? → `TextShape` (+ `Billboard` to face camera)
|
|
21
|
+
3. Need glowing/transparent materials? → `Material.setPbrMaterial` with emissive/transparency
|
|
22
|
+
4. Need to override material on a model node? → `GltfNodeModifiers` with `modifiers` array
|
|
23
|
+
|
|
8
24
|
## Billboard (Face the Camera)
|
|
9
25
|
|
|
10
26
|
Make entities always rotate to face the player's camera:
|
|
@@ -212,20 +228,20 @@ function lodSystem() {
|
|
|
212
228
|
engine.addSystem(lodSystem)
|
|
213
229
|
```
|
|
214
230
|
|
|
215
|
-
### Per-Node
|
|
231
|
+
### Per-Node Modifiers (GltfNodeModifiers)
|
|
216
232
|
|
|
217
|
-
Override
|
|
233
|
+
Override material or shadow casting on specific nodes within a GLTF model:
|
|
218
234
|
|
|
219
235
|
```typescript
|
|
220
|
-
import {
|
|
236
|
+
import { GltfNodeModifiers } from '@dcl/sdk/ecs'
|
|
221
237
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
238
|
+
GltfNodeModifiers.create(entity, {
|
|
239
|
+
modifiers: [
|
|
240
|
+
{
|
|
241
|
+
path: 'RootNode/Armor', // GLTF hierarchy path
|
|
242
|
+
castShadows: false // Disable shadow casting for this node
|
|
243
|
+
}
|
|
244
|
+
]
|
|
229
245
|
})
|
|
230
246
|
```
|
|
231
247
|
|