Loading...
Loading...
Control camera behavior in Decentraland scenes. Covers CameraMode, CameraModeArea, VirtualCamera, MainCamera, and camera vs collider interactions. Use when the user wants camera control, cutscenes, forced camera modes, or camera tracking. Do NOT use for input restriction during cutscenes (see advanced-input for InputModifier) or cursor lock detection (see advanced-input for PointerLock).
npx skill4agent add decentraland/sdk-skills camera-controlengine.CameraEntityimport { engine, Transform } from '@dcl/sdk/ecs'
function trackCamera() {
if (!Transform.has(engine.CameraEntity)) return
const cameraTransform = Transform.get(engine.CameraEntity)
console.log('Camera position:', cameraTransform.position)
console.log('Camera rotation:', cameraTransform.rotation)
}
engine.addSystem(trackCamera)engine.CameraEntityimport { engine, CameraMode, CameraType } from '@dcl/sdk/ecs'
function checkCameraMode() {
if (!CameraMode.has(engine.CameraEntity)) return
const cameraMode = CameraMode.get(engine.CameraEntity)
if (cameraMode.mode === CameraType.CT_FIRST_PERSON) {
console.log('First person camera')
} else if (cameraMode.mode === CameraType.CT_THIRD_PERSON) {
console.log('Third person camera')
}
}
engine.addSystem(checkCameraMode)CameraType.CT_FIRST_PERSON // First-person view
CameraType.CT_THIRD_PERSON // Third-person view (default)CameraMode.onChangeimport { CameraMode, engine } from '@dcl/sdk/ecs'
CameraMode.onChange(engine.CameraEntity, (camera) => {
if (!camera) return
// camera.mode is 0 for first person, 1 for third person
console.log('Camera mode changed:', camera.mode)
})import { engine, Transform, CameraModeArea, CameraType } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const fpArea = engine.addEntity()
Transform.create(fpArea, { position: Vector3.create(8, 1.5, 8) })
CameraModeArea.create(fpArea, {
area: Vector3.create(6, 4, 6), // 6x4x6 meter box
mode: CameraType.CT_FIRST_PERSON, // Force first-person inside
})import { engine, Transform, VirtualCamera, MainCamera } from '@dcl/sdk/ecs'
import { Vector3, Quaternion } from '@dcl/sdk/math'
const cinematicCam = engine.addEntity()
Transform.create(cinematicCam, {
position: Vector3.create(8, 5, 2),
rotation: Quaternion.fromEulerDegrees(-20, 0, 0),
})
VirtualCamera.create(cinematicCam, {
defaultTransition: {
transitionMode: VirtualCamera.Transition.Speed(1.0),
},
// fov: 45, // optional: field of view in degrees (overrides the Explorer default)
})
// Activate the virtual camera (createOrReplace on first activation:
// getMutable throws if MainCamera doesn't exist yet)
MainCamera.createOrReplace(engine.CameraEntity, { virtualCameraEntity: cinematicCam })
// Return to normal camera (component now exists, so getMutable is safe)
MainCamera.getMutable(engine.CameraEntity).virtualCameraEntity = undefinedVirtualCamera.Transition.Speed(1.0) // Speed-based smooth transition
VirtualCamera.Transition.Time(2) // Time-based transition (2 seconds)const target = engine.addEntity()
Transform.create(target, { position: Vector3.create(8, 1, 8) })
VirtualCamera.create(cinematicCam, {
lookAtEntity: target,
defaultTransition: {
transitionMode: VirtualCamera.Transition.Speed(2.0),
},
})
// Activate
MainCamera.createOrReplace(engine.CameraEntity, { virtualCameraEntity: cinematicCam })lookAtEntityengine.PlayerEntity2,22-virtual-camerasQuaternion.fromLookAt(cameraPosition, targetPosition)TransformVirtualCamera.create(entity)VirtualCamerafov?: numberMainCamera.virtualCameraEntityPBVirtualCamera26cb9251MainCameraMainCamera.getMutableOrNull(engine.CameraEntity)virtualCameraEntityundefined2,22-virtual-camerasMainCamera.virtualCameraEntityMainCamera.virtualCameraEntity2,22-virtual-camerasInputModifierTransform.get(engine.CameraEntity).positioncameraZoneSystem{baseDir}/references/camera-patterns.md// NO CAMERA GOING THROUGH THE WALL
// default (both pointer and physics use the invisible geometry)
GltfContainer.create(myEntity, {
src: '/models/myModel.gltf',
})
// NO CAMERA GOING THROUGH THE WALL
// Both use the same invisible geometry
GltfContainer.create(myEntity2, {
src: '/models/myModel.gltf',
invisibleMeshesCollisionMask:
ColliderLayer.CL_PHYSICS | ColliderLayer.CL_POINTER,
})
// NO CAMERA GOING THROUGH THE WALL
// Both use the same visible geometry
GltfContainer.create(myEntity2, {
src: '/models/myModel.gltf',
visibleMeshesCollisionMask:
ColliderLayer.CL_PHYSICS | ColliderLayer.CL_POINTER,
})
// YES CAMERA GOES THROUGH THE WALL
// physics and pointer are on different layers
GltfContainer.create(myEntity2, {
src: '/models/myModel.gltf',
invisibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS,
visibleMeshesCollisionMask: ColliderLayer.CL_POINTER,
})
// YES CAMERA GOES THROUGH THE WALL
// physics and pointer are on different layers
GltfContainer.create(myEntity2, {
src: '/models/myModel.gltf',
invisibleMeshesCollisionMask: ColliderLayer.CL_POINTER,
visibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS,
}){baseDir}/references/camera-patterns.mdPrimaryPointerInfo.screenDeltadisableAll{baseDir}/references/camera-patterns.mdFreezing player during cutscenes? Combine VirtualCamera withfrom the advanced-input skill to prevent player movement during cinematic sequences.InputModifier
SpeedTimelookAtEntity: engine.PlayerEntityCameraModeAreaAvatarModifierAreaMainCameraPrimaryPointerInfo.screenDeltaInputModifier