camera-control について
Decentralandのシーン開発者向けカメラ制御スキルです。エリアごとのカメラモード設定、仮想カメラの実行時有効化、カメラ状態の取得、壁を通り抜けないためのコライダー設定を解説します。
主な機能
- 01エリアごとのカメラモード設定
- 02仮想カメラの作成と有効化
- 03カメラ位置とモードの取得
- 04カメラ遮蔽用コライダーの設定
活用シーン
- 01屋内で一人称視点を強制
- 02カットシーン用カメラの設定
- 03NPC追従やカメライベントの起動
インストール
コマンドをコピーしてターミナルで実行します。
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 を使用してください。
VirtualCamera.lookAtEntity は、main-entities.ts でエンティティの名前を受け取ります(Transform.parent と同様に、ビルド時に Entity 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 の値
| 値 | 列挙値 | 意味 |
|---|---|---|
| 0 | CT_FIRST_PERSON | ゾーン内で一人称視点を強制します |
| 1 | CT_THIRD_PERSON | ゾーン内で三人称視点を強制します |
| 2 | CT_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 が含まれていないと、プレイヤーのカメラが壁をすり抜けることがあります。カメラは遮蔽チェックに 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は 1 つだけにしてください。 - 狭い屋内空間では、
CameraModeAreaを使って一人称視点を強制してください。 - 快適なカメラ移動のため、トランジション速度は 0.5~3.0 にしてください。
- カメラの状態は
engine.CameraEntity経由で読み取ってください。直接書き込もうとしてはいけません。 - 注視対象の検出には、カメラ位置とレイキャスティングを組み合わせてください(
add-interactivityスキルを参照)。 VirtualCameraとCameraModeArea以外では、カメラ制御は読み取り専用です。プレイヤーのカメラを直接移動することはできません。