advanced-input
Advanced Input Handling in Decentraland
For basic click/hover events, see the add-interactivity skill. This skill covers advanced input patterns.
Pointer Lock State
Detect whether the cursor is captured (first-person mode) or free:
import { engine, PointerLock } from '@dcl/sdk/ecs'
function checkPointerLock() {
const isLocked = PointerLock.get(engine.CameraEntity).isPointerLocked
if (isLocked) {
// Cursor is captured — player is in first-person control
} else {
// Cursor is free — player can click UI elements
}
}
engine.addSystem(checkPointerLock)
Pointer Lock Change Detection
PointerLock.onChange(engine.CameraEntity, (pointerLock) => {
if (pointerLock?.isPointerLocked) {
console.log('Cursor locked')
} else {
console.log('Cursor unlocked')
}
})
Cursor Position and World Ray
Get the cursor's screen position and the ray it casts into the 3D world:
import { engine, PrimaryPointerInfo } from '@dcl/sdk/ecs'
function readPointer() {
const pointerInfo = PrimaryPointerInfo.get(engine.RootEntity)
console.log('Cursor position:', pointerInfo.screenCoordinates)
console.log('Cursor delta:', pointerInfo.screenDelta)
console.log('World ray direction:', pointerInfo.worldRayDirection)
}
engine.addSystem(readPointer)
Input Polling with inputSystem
Per-Entity Input Commands
Check if a specific input action occurred on a specific entity:
import { engine, inputSystem, InputAction, PointerEventType } from '@dcl/sdk/ecs'
function myInputSystem() {
// Check for click on a specific entity
const clickData = inputSystem.getInputCommand(
InputAction.IA_POINTER,
PointerEventType.PET_DOWN,
myEntity
)
if (clickData) {
console.log('Entity clicked via system:', clickData.hit.entityId)
}
}
engine.addSystem(myInputSystem)
Global Input Checks
function globalInputSystem() {
// Was the key just pressed this frame?
if (inputSystem.isTriggered(InputAction.IA_PRIMARY, PointerEventType.PET_DOWN)) {
console.log('E key pressed!')
}
// Is the key currently held down?
if (inputSystem.isPressed(InputAction.IA_SECONDARY)) {
console.log('F key is held!')
}
}
engine.addSystem(globalInputSystem)
All InputAction Values
| InputAction | Key/Button |
|---|---|
IA_POINTER |
Left mouse button |
IA_PRIMARY |
E key |
IA_SECONDARY |
F key |
IA_ACTION_3 |
1 key |
IA_ACTION_4 |
2 key |
IA_ACTION_5 |
3 key |
IA_ACTION_6 |
4 key |
IA_JUMP |
Space key |
IA_FORWARD |
W key |
IA_BACKWARD |
S key |
IA_LEFT |
A key |
IA_RIGHT |
D key |
IA_WALK |
Shift key |
Event Types
PointerEventType.PET_DOWN // Button/key pressed
PointerEventType.PET_UP // Button/key released
PointerEventType.PET_HOVER_ENTER // Cursor enters entity
PointerEventType.PET_HOVER_LEAVE // Cursor leaves entity
InputModifier (Movement Restriction)
Restrict or freeze the player's movement:
import { engine, InputModifier } from '@dcl/sdk/ecs'
// Freeze player completely
InputModifier.create(engine.PlayerEntity, {
mode: InputModifier.Mode.Standard({ disableAll: true })
})
// Restrict specific movement
InputModifier.createOrReplace(engine.PlayerEntity, {
mode: InputModifier.Mode.Standard({
disableRun: true,
disableJump: true,
disableEmote: true
})
})
// Restore normal movement
InputModifier.deleteFrom(engine.PlayerEntity)
Important: InputModifier only works in the DCL 2.0 desktop client. It has no effect in the web browser explorer.
Cutscene Pattern
Freeze the player during a cinematic sequence:
function startCutscene() {
// Freeze player
InputModifier.create(engine.PlayerEntity, {
mode: InputModifier.Mode.Standard({ disableAll: true })
})
// ... play cinematic with VirtualCamera ...
// After cutscene ends, restore movement
// InputModifier.deleteFrom(engine.PlayerEntity)
}
WASD Movement Pattern
Poll movement keys to control custom entities:
import { engine, inputSystem, InputAction, PointerEventType, Transform } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const MOVE_SPEED = 5
function customMovementSystem(dt: number) {
const transform = Transform.getMutable(controllableEntity)
let moveX = 0
let moveZ = 0
if (inputSystem.isPressed(InputAction.IA_FORWARD)) moveZ += 1
if (inputSystem.isPressed(InputAction.IA_BACKWARD)) moveZ -= 1
if (inputSystem.isPressed(InputAction.IA_LEFT)) moveX -= 1
if (inputSystem.isPressed(InputAction.IA_RIGHT)) moveX += 1
transform.position.x += moveX * MOVE_SPEED * dt
transform.position.z += moveZ * MOVE_SPEED * dt
}
engine.addSystem(customMovementSystem)
Combining Input Patterns
Action Bar with Number Keys
function actionBarSystem() {
if (inputSystem.isTriggered(InputAction.IA_ACTION_3, PointerEventType.PET_DOWN)) {
console.log('Slot 1 activated')
useAbility(1)
}
if (inputSystem.isTriggered(InputAction.IA_ACTION_4, PointerEventType.PET_DOWN)) {
console.log('Slot 2 activated')
useAbility(2)
}
if (inputSystem.isTriggered(InputAction.IA_ACTION_5, PointerEventType.PET_DOWN)) {
console.log('Slot 3 activated')
useAbility(3)
}
if (inputSystem.isTriggered(InputAction.IA_ACTION_6, PointerEventType.PET_DOWN)) {
console.log('Slot 4 activated')
useAbility(4)
}
}
engine.addSystem(actionBarSystem)
Best Practices
- Use
isTriggered()for one-shot actions (fire weapon, open door) — it returns true only on the frame the key is first pressed - Use
isPressed()for continuous actions (movement, holding a shield) — it returns true every frame while held getInputCommand()gives hit data (position, entity) — use it when you need to know what was clicked- Prefer
pointerEventsSystem.onPointerDown()for simple entity clicks — useinputSystemfor complex multi-key or polling patterns - InputModifier only works in the DCL 2.0 desktop client — test with the desktop client if your scene relies on it
- WASD keys (
IA_FORWARD, etc.) also control player movement — polling them reads the movement state but doesn't override it
For basic pointer events and click handlers, see the add-interactivity skill.
More from dcl-regenesislabs/opendcl
optimize-scene
Optimize Decentraland scene performance. Scene limit formulas (triangles, entities, materials, textures, height per parcel count), object pooling, LOD patterns, texture optimization, system throttling, and asset preloading. Use when the user wants to optimize, improve performance, fix lag, reduce load time, check limits, or reduce entity/triangle count. Do NOT use for deployment (see deploy-scene).
50game-design
Plan and design Decentraland games and interactive experiences. Scene limit formulas, performance budgets, texture requirements, asset preloading, state management patterns (module-level, component-based, state machines), object pooling, UX/UI guidelines, input design, and MVP planning. 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).
29audio-video
Add sound effects, music, audio streaming, and video players to Decentraland scenes. Covers AudioSource (local files), AudioStream (streaming URLs), VideoPlayer (video surfaces), video events, and media permissions. Use when the user wants sound, music, audio, video screens, radio, or media playback. Do NOT use for 3D model animations (see animations-tweens).
29add-3d-models
Add 3D models (.glb/.gltf) to a Decentraland scene using GltfContainer. Covers loading, positioning, scaling, colliders, parenting, and browsing 5,700+ free assets from the OpenDCL catalog. 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).
27nft-blockchain
NFT display and blockchain interaction in Decentraland. NftShape (framed NFT artwork), wallet checks (getPlayer, isGuest), signedFetch (authenticated requests), smart contract interaction (eth-connect, createEthereumProvider), and RPC calls. Use when the user wants NFTs, blockchain, wallet, smart contracts, Web3, crypto, or token gating. Do NOT use for player avatar data or emotes (see player-avatar).
26build-ui
Build 2D screen-space UI for Decentraland scenes using React-ECS (JSX). Create HUDs, menus, health bars, scoreboards, dialogs, buttons, inputs, and dropdowns. Use when the user wants screen overlays, on-screen UI, HUD elements, menus, or form inputs. Do NOT use for 3D in-world text (see advanced-rendering) or clickable 3D objects (see add-interactivity).
25