mcpField
C
camera-control

camera-control

github.com/dcl-regenesislabs开发1737 次安装21 周前更新
代码仓库

关于 camera-control

面向 Decentraland 场景开发者的相机控制技能,介绍如何设置区域相机模式、运行时启用虚拟相机并读取相机状态。还涵盖相机遮挡碰撞配置及常见镜头跟随模式。

主要功能

  1. 01设置区域相机模式
  2. 02创建并启用虚拟相机
  3. 03读取相机位置与模式
  4. 04配置相机遮挡碰撞

适用场景

  1. 01室内区域强制第一人称视角
  2. 02为过场动画设置镜头
  3. 03跟随 NPC 或触发相机事件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dcl-regenesislabs/opendcl --skill camera-control

安装后在 Claude Code 中输入/camera-control确认已加载。

详细介绍

Decentraland 中的摄像机控制

内容编写拆分

CameraModeArea 和 VirtualCamera 在 main-entities.ts 中受支持——这两个本质上是静态的组件都应放在这里。MainCamera 不受支持,因为它位于预留的 engine.CameraEntity 上;请在运行时于 src/index.ts 中激活虚拟摄像机。

在 main-entities.ts 中,VirtualCamera.lookAtEntity 接受实体名称(与 Transform.parent 相同,会在构建时解析为实体 ID)。

// main-entities.ts
cinematic_cam: {
  components: {
    Transform: {
      position: { x: 12, y: 4, z: 8 },
      rotation: { x: 0, y: 0.7071, z: 0, w: 0.7071 }
    },
    VirtualCamera: {
      defaultTransition: { transitionMode: { $case: 'time', time: 2 } },
      lookAtEntity: 'shopkeeper'  // name of another entity in this file
    }
  }
},
first_person_zone: {
  components: {
    Transform: { position: { x: 8, y: 1, z: 8 } },
    CameraModeArea: {
      area: { x: 16, y: 2, z: 16 },
      mode: 0  // CameraType.CT_FIRST_PERSON
    }
  }
}

在运行时激活电影摄像机:

// src/index.ts
import { engine, MainCamera } from '@dcl/sdk/ecs'

export function main() {
  const cam = engine.getEntityOrNullByName('cinematic_cam')
  if (cam) MainCamera.createOrReplace(engine.CameraEntity, { virtualCameraEntity: cam })
}

预留的 engine.CameraEntity 和 engine.PlayerEntity 由引擎管理,在 main-entities.ts 中没有对应项。

CameraModeArea.mode 的 CameraType 值
valueenummeaning
0CT_FIRST_PERSON在区域内强制使用第一人称视角
1CT_THIRD_PERSON在区域内强制使用第三人称视角
2CT_CINEMATIC在区域内强制使用电影摄像机

读取摄像机状态

通过预留的 engine.CameraEntity 获取摄像机的当前位置和旋转:

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)

检测摄像机模式

检查玩家处于第一人称还是第三人称:

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)
摄像机模式值
CameraType.CT_FIRST_PERSON  // First-person view
CameraType.CT_THIRD_PERSON  // Third-person view (default)

CameraModeArea(在区域内强制使用摄像机模式)

玩家进入某个区域时,强制使用指定的摄像机模式:

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(电影摄像机)

为过场动画或特殊视角创建脚本化摄像机位置:

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)
  }
})

// Activate the virtual camera
MainCamera.getMutable(engine.CameraEntity).virtualCameraEntity = cinematicCam

// Return to normal camera
MainCamera.getMutable(engine.CameraEntity).virtualCameraEntity = undefined
过渡模式
VirtualCamera.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.getMutable(engine.CameraEntity).virtualCameraEntity = cinematicCam

跟踪摄像机位置

每帧轮询摄像机位置,以触发摄像机相关事件:

import { engine, Transform } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

let lastNotifiedZone = ''

function cameraZoneSystem() {
  if (!Transform.has(engine.CameraEntity)) return

  const camPos = Transform.get(engine.CameraEntity).position
  let currentZone = ''

  if (camPos.y > 10) {
    currentZone = 'sky'
  } else if (camPos.x < 4) {
    currentZone = 'west'
  } else {
    currentZone = 'center'
  }

  if (currentZone !== lastNotifiedZone) {
    lastNotifiedZone = currentZone
    console.log('Camera entered zone:', currentZone)
  }
}

engine.addSystem(cameraZoneSystem)

常见模式

摄像机触发事件

利用摄像机位置,在玩家看向特定区域时触发操作:

function cameraLookTrigger() {
  const camTransform = Transform.get(engine.CameraEntity)
  const targetPos = Vector3.create(8, 2, 8)
  const distance = Vector3.distance(camTransform.position, targetPos)

  if (distance < 5) {
    // Player is close — check if camera is pointing at target
    // Use raycasting for precise look detection (see add-interactivity skill)
  }
}

engine.addSystem(cameraLookTrigger)
跟随 NPC

通过更新 VirtualCamera 的 Transform,移动摄像机以跟踪 NPC:

function followNpcCamera(dt: number) {
  const npcPos = Transform.get(npcEntity).position
  const camTransform = Transform.getMutable(cinematicCam)

  // Position camera behind and above the NPC
  camTransform.position = Vector3.create(
    npcPos.x - 2,
    npcPos.y + 3,
    npcPos.z - 2
  )
}

engine.addSystem(followNpcCamera)

在过场动画期间冻结玩家? 将 VirtualCamera 与 advanced-input 技能中的 InputModifier 结合使用,以防止玩家在电影序列期间移动。

摄像机与碰撞体(防止摄像机穿墙)

在第三人称模式下,如果墙壁的碰撞体掩码不包含 CL_POINTER,玩家的摄像机就可能穿过墙壁。摄像机使用 pointer 碰撞体掩码进行遮挡检测,而不是 CL_PHYSICS。请为摄像机应当碰撞反弹的墙壁和建筑结构设置这两个掩码:

// main-entities.ts
wall: {
  components: {
    Transform: { position: { x: 8, y: 1.5, z: 16 } },
    GltfContainer: {
      src: 'models/wall.glb',
      visibleMeshesCollisionMask: 3   // CL_PHYSICS | CL_POINTER
    }
  }
}

对于包含不可见碰撞网格的 GLB(Creator Hub 资源包),请改为设置 invisibleMeshesCollisionMask: 3。如果仅使用默认值 CL_PHYSICS,摄像机就会穿过。

最佳实践

  • 同一时间只能激活一个 VirtualCamera。
  • 在狭窄的室内空间中,使用 CameraModeArea 强制切换到第一人称视角。
  • 将过渡速度保持在 0.5 到 3.0 之间,以获得舒适的摄像机移动效果。
  • 通过 engine.CameraEntity 读取摄像机状态——切勿尝试直接写入。
  • 要检测注视目标,请结合摄像机位置和射线检测(参见 add-interactivity 技能)。
  • 除 VirtualCamera 和 CameraModeArea 外,摄像机控制均为只读——无法直接移动玩家的摄像机。

评论

支持 Markdown