camera-control
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCamera Control in Decentraland
Decentraland 中的摄像机控制
Reading Camera State
读取摄像机状态
Access the camera's current position and rotation via the reserved :
engine.CameraEntitytypescript
import { 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.CameraEntity通过保留实体 访问摄像机的当前位置和旋转:
engine.CameraEntitytypescript
import { 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.CameraEntityCamera Mode Detection
摄像机模式检测
Check whether the player is in first-person or third-person:
typescript
import { 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)检查玩家当前处于第一人称还是第三人称:
typescript
import { 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)Camera Mode Values
摄像机模式值
typescript
CameraType.CT_FIRST_PERSON // First-person view
CameraType.CT_THIRD_PERSON // Third-person view (default)typescript
CameraType.CT_FIRST_PERSON // First-person view
CameraType.CT_THIRD_PERSON // Third-person view (default)React to Camera Mode Changes
响应摄像机模式变化
Use to get notified only when the player toggles between first and third person — cheaper than polling every frame:
CameraMode.onChangetypescript
import { 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)
})使用 ,仅在玩家于第一人称和第三人称之间切换时收到通知——这比每帧轮询更节省开销:
CameraMode.onChangetypescript
import { 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)
})CameraModeArea (Force Camera in a Region)
CameraModeArea(在区域内强制指定摄像机模式)
Force a specific camera mode when the player enters an area (e.g. force first-person in tight indoor spaces):
typescript
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
})When the player leaves the area, the camera reverts to their preferred mode.
玩家进入某个区域时强制指定特定的摄像机模式(例如,在狭窄的室内空间中强制使用第一人称):
typescript
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
})当玩家离开该区域时,摄像机会恢复为玩家首选的模式。
VirtualCamera (Cinematic Cameras)
VirtualCamera(电影级摄像机)
Create scripted camera positions for cutscenes or special views:
typescript
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 = undefined为过场动画或特殊视角创建脚本化的摄像机位置:
typescript
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 = undefinedTransition Modes
过渡模式
typescript
VirtualCamera.Transition.Speed(1.0) // Speed-based smooth transition
VirtualCamera.Transition.Time(2) // Time-based transition (2 seconds)Keep transition speeds between 0.5 and 3.0 for comfortable camera movement.
typescript
VirtualCamera.Transition.Speed(1.0) // Speed-based smooth transition
VirtualCamera.Transition.Time(2) // Time-based transition (2 seconds)将过渡速度保持在 0.5 到 3.0 之间,以确保摄像机移动舒适流畅。
Look-At Target
注视目标
Make the virtual camera track an entity:
typescript
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.virtualCameraEntityPBVirtualCamera26cb9251Switching between cameras / deactivating: with already present, mutate it via ; set to another VirtualCamera entity to cut/transition to it, or to to return to the player's normal camera (verified: ). Only one VirtualCamera is active at a time ( holds a single entity).
MainCameraMainCamera.getMutableOrNull(engine.CameraEntity)virtualCameraEntityundefined2,22-virtual-camerasMainCamera.virtualCameraEntityYou cannot move the player's real camera directly. To move a camera under scene control, drive the Transform of an active VirtualCamera entity each frame; while it is the , the player sees through it (verified: controllable camera). Pair with (advanced-input) to disable avatar movement so WASD drives the camera instead.
MainCamera.virtualCameraEntity2,22-virtual-camerasInputModifier让虚拟摄像机追踪一个实体:
typescript
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-camerasTransformQuaternion.fromLookAt(cameraPosition, targetPosition)VirtualCamera.create(entity)VirtualCamerafov?: numberMainCamera.virtualCameraEntityPBVirtualCamera26cb9251**切换摄像机/停用:**当 已存在时,通过 修改它;将 设为另一个 VirtualCamera 实体可切换或过渡到该摄像机,设为 则返回玩家的普通摄像机(已通过 验证)。同一时间只能有一个 VirtualCamera 处于活动状态( 仅保存一个实体)。
MainCameraMainCamera.getMutableOrNull(engine.CameraEntity)virtualCameraEntityundefined2,22-virtual-camerasMainCamera.virtualCameraEntity你无法直接移动玩家的真实摄像机。要在场景控制下移动摄像机,请每帧驱动活动的 VirtualCamera 实体的 Transform;在它是 期间,玩家将通过它观看场景(已在 的可控摄像机场景中验证)。将其与 InputModifier(advanced-input)配合使用,可禁用角色移动,让 WASD 改为控制摄像机。
MainCamera.virtualCameraEntity2,22-virtual-camerasTracking Camera Position
跟踪摄像机位置
Poll camera position each frame by reading inside a system. For a full worked zone-tracking system (), see → "Tracking Camera Position (camera zone system)".
Transform.get(engine.CameraEntity).positioncameraZoneSystem{baseDir}/references/camera-patterns.md在 system 内读取 ,即可每帧轮询摄像机位置。完整的区域跟踪系统()示例,请参阅 → "Tracking Camera Position (camera zone system)"。
Transform.get(engine.CameraEntity).positioncameraZoneSystem{baseDir}/references/camera-patterns.md## Camera and Colliders
## Camera 与碰撞体
When a player's camera moves in 3rd person mode, the camera might be blocked by colliders or not, depending on the collision layers assigned to the entities. To avoid the camera from going through walls, you must assign both the ColliderLayer.CL_PHYSICS and the ColliderLayer.CL_POINTER layers to the entities that you want to block the camera.
ts
// 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,
})当玩家的摄像机在第三人称模式下移动时,摄像机可能会被碰撞体阻挡,也可能不会,具体取决于分配给相关实体的碰撞层。要避免摄像机穿墙,必须为希望阻挡摄像机的实体同时分配 和 。
ColliderLayer.CL_PHYSICSColliderLayer.CL_POINTERts
// 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,
})Common Patterns
常见模式
For full worked patterns, see :
{baseDir}/references/camera-patterns.md- Camera-Triggered Events — use camera position/proximity to trigger actions when the player looks at an area.
- Following an NPC (camera-follows-NPC) — track an NPC by driving a VirtualCamera's Transform each frame (guardrail on why this works lives in the VirtualCamera section above).
- Mouselook Camera (FPS-style) — drive a VirtualCamera with (pixel delta per frame, keeps working while cursor is locked). Accumulate into yaw/pitch, clamp pitch [-85,+85], combine with PointerLock + InputModifier
PrimaryPointerInfo.screenDelta. Desktop only (screenDelta always 0 on mobile). SeedisableAll→ "Mouselook Camera".{baseDir}/references/camera-patterns.md
Freezing player during cutscenes? Combine VirtualCamera withfrom the advanced-input skill to prevent player movement during cinematic sequences.InputModifier
有关完整的可运行模式,请参阅 :
{baseDir}/references/camera-patterns.md- 摄像机触发的事件 — 使用摄像机的位置或距离,在玩家看向某个区域时触发操作。
- 跟随 NPC(camera-follows-NPC) — 通过每帧驱动 VirtualCamera 的 Transform 来追踪 NPC(其有效性的安全说明见上文的 VirtualCamera 部分)。
- 鼠标视角摄像机(FPS 风格) — 使用 驱动 VirtualCamera(每帧的像素增量,在光标锁定时仍然有效)。将增量累积到 yaw/pitch 中,将 pitch 限制在 [-85,+85],并结合 PointerLock + InputModifier
PrimaryPointerInfo.screenDelta。仅支持桌面端(移动端上 screenDelta 始终为 0)。参见disableAll→ "Mouselook Camera"。{baseDir}/references/camera-patterns.md
需要在过场动画期间冻结玩家? 将 VirtualCamera 与 advanced-input skill 中的 InputModifier 结合使用,以防止玩家在电影式序列期间移动。
Example scenes
示例场景
- https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/2,22-virtual-cameras — multiple VirtualCameras: static, /
Speedtransitions,Time, a Tween-driven moving camera, a WASD-controllable camera (driving the VirtualCamera Transform each frame), pluslookAtEntity: engine.PlayerEntityandCameraModeArea.AvatarModifierArea - https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/0,5-primary-cursor-info — activating/deactivating VirtualCameras with toggled by key input, combined with InputModifier.
MainCamera - https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/32,20-virtual-camera-mouse-look — mouselook camera: driving VirtualCamera yaw/pitch while pointer is locked, with
PrimaryPointerInfo.screenDeltadisableAll and PointerLock control. Reference implementation for the mouselook pattern.InputModifier
- https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/2,22-virtual-cameras — 多个 VirtualCamera:静态、/
Speed过渡、Time、由 Tween 驱动的移动摄像机、由 WASD 控制的摄像机(每帧驱动 VirtualCamera 的 Transform),以及lookAtEntity: engine.PlayerEntity和CameraModeArea。AvatarModifierArea - https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/0,5-primary-cursor-info — 通过按键输入切换 ,从而激活或停用 VirtualCamera,并与 InputModifier 结合使用。
MainCamera - https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/32,20-virtual-camera-mouse-look — mouselook 摄像机:使用 在指针锁定时驱动 VirtualCamera 的 yaw/pitch,同时使用 InputModifier
PrimaryPointerInfo.screenDelta和 PointerLock 控制。这是 mouselook 模式的参考实现。disableAll