> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-ko/sdk7/3d/camera.md).

# 카메라

플레이어의 카메라를 제어하는 방법을 알아보세요

크리에이터로서 플레이어의 카메라를 완전히 제어할 수 있습니다. 기본적으로 플레이어는 씬을 탐색하는 동안 1인칭 또는 3인칭 카메라 모드 중에서 자유롭게 선택할 수 있지만, 다른 카메라 모드를 강제할 수도 있습니다.

가상 카메라는 고정될 수도 있고, 플레이어나 다른 엔티티를 항상 바라보도록 회전할 수도 있으며, 플레이어나 다른 엔티티에 부착되어 항상 함께 이동하도록 할 수도 있습니다.

{% hint style="warning" %}
**📔 참고**: 기본 1인칭 및 3인칭 카메라를 전환하려면 다음을 참조하세요 [카메라 수정 영역](#1st-and-3rd-person-camera-modes).
{% endhint %}

## 1인칭 및 3인칭 카메라 모드

플레이어는 보통 키보드의 V 키를 눌러 1인칭과 3인칭 카메라를 전환할 수 있습니다. 다음을 사용하세요 `CameraModeArea` 을 사용하면 씬의 특정 영역 안에 서 있는 모든 플레이어에게 카메라 모드를 1인칭 또는 3인칭으로 강제할 수 있습니다.

```ts
const entity = engine.addEntity()

CameraModeArea.create(entity, {
	area: Vector3.create(4, 3, 4),
	mode: CameraType.CT_FIRST_PERSON,
})
```

플레이어의 현재 카메라 모드가 다음의 모드와 일치하지 않으면 `CameraModeArea`, 해당 카메라 모드로 전환됩니다. 이 변경이 씬에 의한 것임을 알리기 위해 화면에 토스트가 표시됩니다. 영역 안에 있는 동안 플레이어는 카메라 모드를 변경할 수 없습니다. 플레이어가 다음을 벗어나면 `CameraModeArea`, 카메라 모드는 들어오기 전에 사용하던 상태로 복원됩니다.

다음을 사용하세요 `CameraModeArea` 플레이어가 특정 카메라 모드를 사용했을 때 훨씬 더 좋은 경험을 할 수 있는 영역에서 사용하세요. 예를 들어, 플레이어가 작은 객체를 클릭해야 한다면 1인칭이 이상적일 수 있고, 플레이어가 씬에서 머리 위에 부착된 특정 엔티티를 알아차려야 한다면 3인칭이 유용할 수 있습니다. 플레이어가 카메라 모드를 전환하는 방법을 알고 있다고 가정하지 마세요. 처음 플레이하는 많은 사람들은 그런 옵션이 있다는 것을 모르거나, 전환 키를 기억하지 못할 수 있습니다.

{% hint style="warning" %}
**📔 참고**: 카메라 수정 영역은 다음의 영향을 받습니다 *위치* 과 *회전* 호스트 엔티티의 Transform 컴포넌트의 영향을 받지만, 다음의 영향은 받지 않습니다 *크기*.
{% endhint %}

{% hint style="warning" %}
**📔 참고**: 여러 카메라 수정 영역이 겹치면, 씬 코드에 의해 마지막으로 인스턴스화된 영역이 다른 영역보다 우선합니다.
{% endhint %}

다음의 `CameraModeArea` 컴포넌트를 사용하려면 다음 항목을 제공해야 합니다:

* `area`: 수정 영역의 크기
* `mode`: 이 영역에서 강제할 카메라 모드, 다음에서 `CameraType` 열거형.

지원되는 카메라 모드는 다음과 같습니다:

* `CameraType.CT_FIRST_PERSON`
* `CameraType.CT_THIRD_PERSON`

### 카메라 모드 조회

다음을 사용하여 플레이어의 카메라 모드를 조회할 수 있습니다 `CameraMode` 컴포넌트를 다음에 `engine.CameraEntity`.

```ts
const cameraMode = CameraMode.get(engine.CameraEntity)
if (cameraMode.mode === CameraType.CT_FIRST_PERSON) {
	플레이어가 1인칭 카메라를 사용 중입니다
} else {
	플레이어가 3인칭 카메라를 사용 중입니다
}
```

다음을 사용하여 카메라 모드 변경 사항도 구독할 수 있습니다 `onChange` 다음의 함수 `CameraMode` 컴포넌트를 추가해야 합니다.

```ts
CameraMode.onChange(engine.CameraEntity, (cameraMode) => {
	if (!cameraMode) return
	플레이어의 카메라 모드가 다음으로 변경됨
})
```

## 가상 카메라 사용

씬에서 사용자 지정 카메라 동작을 사용하려면 두 가지가 필요합니다:

* 가상 카메라 만들기: 씬에 엔티티를 만들고 다음을 부여하세요 `VirtualCamera`.
* 해당 가상 카메라를 할당하기: 다음을 추가하세요 `MainCamera` 컴포넌트를 [예약된 엔티티](/creator/content-creator-ko/sdk7/architecture/entities-components.md#reserved-entities) `engine.CameraEntity`, 다음을 가진 엔티티를 참조하여 `VirtualCamera` 컴포넌트를 추가해야 합니다.

그러면 카메라는 다음을 가진 엔티티에 부착됩니다 `VirtualCamera` 컴포넌트. 엔티티가 이동하거나 회전하면 카메라도 함께 이동합니다.

```ts
function main() {
	const myCustomCamera = engine.addEntity()
	Transform.create(myCustomCamera, {
		position: Vector3.create(1, 2, 1),
	})
	VirtualCamera.create(myCustomCamera, {})

	const mainCamera = MainCamera.createOrReplace(engine.CameraEntity, {
		virtualCameraEntity: myCustomCamera,
	})
}
```

이 예시에서 카메라는 플레이어가 씬 경계 안에 있는 한 씬의 고정된 위치에 항상 있게 됩니다. 플레이어가 씬 경계를 벗어나는 순간 기본 카메라 동작이 복원됩니다.

### 시야각

가상 카메라의 Explorer 기본 시야각(FOV)은 다음을 설정하여 재정의할 수 있습니다 `fov` 속성(도 단위)입니다. 이 재정의는 이 가상 카메라가 활성화되어 있는 동안에만 적용됩니다. 다음을 생략하면 `fov` Explorer의 기본값(일반적으로 60도)이 사용됩니다.

```ts
const cinematicCamera = engine.addEntity()
Transform.create(cinematicCamera, {
	position: Vector3.create(8, 3, 2),
})
VirtualCamera.create(cinematicCamera, {
	fov: 45,
})
```

더 좁은 FOV(더 낮은 값)는 확대되어 컷신이나 조준에 유용합니다. 더 넓은 FOV(더 높은 값)는 씬을 더 많이 보여 주며, 파노라마 보기 등에 유용합니다.

씬에는 다음을 가진 엔티티를 원하는 만큼 포함할 수 있습니다 `VirtualCamera`컴포넌트를 원하는 만큼 포함할 수 있으며, 플레이어가 이동하거나 특정 동작을 수행할 때 여러 가상 카메라 간에 동적으로 전환할 수 있습니다. 어떤 시점에도 활성화되는 가상 카메라는 하나뿐이며, 이는 다음에 의해 할당됩니다 `MainCamera` 의 컴포넌트 `engine.CameraEntity`.

기본 카메라 동작으로 되돌리려면 값을 다음으로 설정하세요 `undefined` 에서 `MainCamera.virtualCameraEntity`. 그러면 플레이어는 1인칭과 3인칭 카메라를 자유롭게 전환할 수 있습니다. 플레이어가 이 두 모드 중 하나만 사용하도록 하려면 다음을 사용할 수 있습니다 [카메라 수정 영역](#1st-and-3rd-person-camera-modes) 둘 중 하나를 강제하는 것입니다.

{% hint style="warning" %}
**📔 참고**가상 카메라가 활성화되어 있지 않을 때만 카메라 수정 영역이 플레이어에게 영향을 줍니다. 씬이 현재 가상 카메라를 사용 중인데 플레이어가 카메라 수정 영역에 들어가면 아무 일도 일어나지 않습니다.

3D 모델에 다음이 포함되어 있다면 `camera` 노드가 내용의 일부로 포함되어 있다면, 이는 SDK에서 사용할 수 없습니다. 모든 카메라는 SDK로 엔티티로 만들어야 합니다.
{% endhint %}

```ts
function main() {
	// 사용자 지정 가상 카메라
	const myCustomCamera = engine.addEntity()
	Transform.create(myCustomCamera, {
		position: Vector3.create(1, 2, 1),
	})
	VirtualCamera.create(myCustomCamera, {})

	const mainCamera = MainCamera.createOrReplace(engine.CameraEntity, {
		virtualCameraEntity: myCustomCamera,
	})

	// 클릭 가능한 큐브
	const clickCube = engine.addEntity()
	Transform.create(clickCube, { position: Vector3.create(8, 0, 8) })
	MeshRenderer.setBox(clickCube)
	MeshCollider.setBox(clickCube)
	pointerEventsSystem.onPointerDown(
		{
			entity: clickCube,
			opts: { button: InputAction.IA_POINTER, hoverText: '카메라 초기화' },
		},
		() => {
			// 카메라를 기본 동작으로 재설정
			const mainCamera = MainCamera.getMutable(engine.CameraEntity)
			mainCamera.virtualCameraEntity = undefined
		}
	)
}
```

{% hint style="info" %}
**💡 팁**: 카메라가 아바타에서 돌아서 있을 때는 아바타의 움직임도 멈추는 것이 좋은 관행입니다. 그래야 플레이어가 장애물로 무작정 이동하지 않습니다. 다음을 참조하세요 [입력 수정자](/creator/content-creator-ko/sdk7/interactivity/avatars/locomotion.md#freeze-the-player)
{% endhint %}

플레이어가 마우스로 가상 카메라를 조작할 수 있게 하려면 다음을 읽어 `screenDelta` 의 속성을 `PrimaryPointerInfo` 컴포넌트를 사용해 커서가 각 프레임마다 얼마나 이동했는지 확인한 다음, 그 이동을 카메라 회전에 적용하세요. 이 방식은 커서가 잠겨 있는 동안에도 작동합니다. 다음을 참조하세요 [마우스 이동](/creator/content-creator-ko/sdk7/interactivity/mouse-movement.md) 전체 마우스 룩 예제를 보세요.

## 조감도

가상 카메라를 사용해 씬을 하향식으로 볼 수 있습니다. 이는 아바타의 일반적인 시점을 재미있게 변형할 수 있으며, 다른 게임 메커니즘을 가능하게 합니다.

카메라를 완전히 수직으로 아래를 향하도록 배치하는 것은 피해야 합니다. 대신 항상 카메라를 약간 기울이세요. 1도만 있어도 충분합니다. 이는 플레이어 방향 조작이 아바타의 방향이 아니라 카메라의 시점에 기반하기 때문입니다. 카메라가 완벽한 조감도로 아래를 바라보면 어느 방향이 어느 쪽인지 명확하게 정의할 수 없습니다. 눈에 띄지 않을 정도의 1도 기울기만으로도 전방 방향을 설정하기에 충분합니다.

```ts
function main() {
	// 조감도 카메라
	const myCustomCamera = engine.addEntity()
	Transform.create(myCustomCamera, {
		position: Vector3.create(8, 5, 8),
		rotation: Quaternion.fromEulerDegrees(91, 0, 0),
		// 회전값은 90º가 아니라 91º라는 점에 유의하세요
	})
	VirtualCamera.create(myCustomCamera, {})

	const mainCamera = MainCamera.createOrReplace(engine.CameraEntity, {
		virtualCameraEntity: myCustomCamera,
	})
}
```

## 시야각

다음을 사용해 가상 카메라에 사용자 지정 시야각(FOV)을 설정할 수 있습니다 `fov` 속성입니다. 값은 도 단위입니다. 생략하면 기본값은 60입니다.

```ts
VirtualCamera.create(myCustomCamera, {
	fov: 90,
})
```

더 넓은 FOV(더 높은 값)는 한 번에 씬을 더 많이 보여 주고 속도감을 만들어 내며, 레이싱 게임에 유용할 수 있습니다. 더 좁은 FOV(더 낮은 값)는 확대되어 조준이나 시네마틱 샷에 유용합니다.

## 카메라 전환

씬이 가상 카메라 간에, 또는 기본 카메라 동작과 가상 카메라 간에 전환될 때마다 플레이어는 전환 효과를 보게 됩니다. 가상 카메라의 위치, 회전 및 기타 매개변수는 일정 시간 동안 부드럽게 변경됩니다.

가상 카메라의 전환 설정은 어떻게 전환할지 결정합니다 *로* 해당 카메라로, 기본 카메라를 포함한 씬의 다른 어떤 카메라에서든 전환합니다. 이 설정은 전환 방식에는 영향을 주지 않습니다 *out* 그 카메라에서.

```ts
VirtualCamera.create(myCustomCamera1, {
	defaultTransition: { transitionMode: VirtualCamera.Transition.Time(6) },
})
```

{% hint style="info" %}
**💡 팁**: 전환 없이 카메라로 즉시 전환하려면 전환 시간 또는 속도를 0으로 설정하세요.
{% endhint %}

사용 사례에 따라 지속 시간 대신 전환 속도를 설정하는 것이 더 좋을 수 있습니다:

* **고정 시간**: 전환 지속 시간을 설정하면, 카메라는 해당 시간 안에 경로를 완료하는 데 필요한 만큼 빠르게 이동합니다.
* **고정 속도**: 전환 동안 가상 카메라가 얼마나 빨리 이동할지 설정하면, 지속 시간은 거리에 따라 달라집니다. 속도에 사용되는 값은 다음과 같이 해석됩니다 **미터/초**.

아래는 이 두 전환 모드의 예시입니다:

```ts
// 고정 지속 시간
VirtualCamera.create(myCustomCamera1, {
	defaultTransition: { transitionMode: VirtualCamera.Transition.Time(6) },
})

// 고정 속도
VirtualCamera.create(myCustomCamera1, {
	defaultTransition: { transitionMode: VirtualCamera.Transition.Speed(3) },
})
```

아래는 두 개의 가상 카메라와 그 사이의 전환을 포함한 전체 예시입니다:

```ts
function main() {
	// 사용자 지정 가상 카메라 1
	const myCustomCamera1 = engine.addEntity()
	Transform.create(myCustomCamera1, {
		position: Vector3.create(1, 2, 1),
	})
	VirtualCamera.create(myCustomCamera1, {
		defaultTransition: { transitionMode: VirtualCamera.Transition.Time(1) },
	})

	// 사용자 지정 가상 카메라 2
	const myCustomCamera2 = engine.addEntity()
	Transform.create(myCustomCamera2, {
		position: Vector3.create(1, 2, 1),
	})
	VirtualCamera.create(myCustomCamera2, {
		defaultTransition: { transitionMode: VirtualCamera.Transition.Time(3) },
	})

	const mainCamera = MainCamera.createOrReplace(engine.CameraEntity, {
		virtualCameraEntity: myCustomCamera1,
	})

	// 클릭 가능한 큐브
	const clickCube = engine.addEntity()
	Transform.create(clickCube, { position: Vector3.create(8, 0, 8) })
	MeshRenderer.setBox(clickCube)
	MeshCollider.setBox(clickCube)
	pointerEventsSystem.onPointerDown(
		{
			entity: clickCube,
			opts: { button: InputAction.IA_POINTER, hoverText: '카메라 초기화' },
		},
		() => {
			// 카메라를 기본 동작으로 재설정
			const mainCamera = MainCamera.getMutable(engine.CameraEntity)
			mainCamera.virtualCameraEntity =
				mainCamera.virtualCameraEntity == myCustomCamera1
					? myCustomCamera2
					: myCustomCamera1
		}
	)
}
```

전환은 경로상의 장애물을 고려하지 않고 항상 직선으로 이동합니다. 대신 다른 가상 카메라를 중간 단계로 사용해 전환을 수동으로 만들 수 있으며, 그렇게 하면 이동을 완전히 제어할 수 있습니다. 이 중간 가상 카메라는 다음을 수행할 수 있습니다 [트윈](/creator/content-creator-ko/sdk7/3d/move-entities.md#move-between-two-points) 첫 번째 카메라의 위치에서 두 번째 카메라의 위치까지 이동하거나, 장애물을 피하거나 시네마틱한 우회 경로를 따르는 더 사용자 지정된 경로를 따를 수 있습니다.

## 카메라 추적

가상 카메라를 플레이어의 방향이나 씬의 특정 엔티티를 항상 바라보도록 구성할 수 있습니다. 카메라의 위치는 고정되지만, 회전은 이 엔티티가 항상 중앙에 오도록 변경됩니다.

이는 다음으로 구현할 수 있습니다 `lookAtEntity` 의 속성 `VirtualCamera` 컴포넌트. 플레이어를 따라가려면 다음을 사용하세요 [예약된 엔티티](/creator/content-creator-ko/sdk7/architecture/entities-components.md#reserved-entities) `engine.PlayerEntity`.

```ts
const myCustomCamera1 = engine.addEntity()
Transform.create(myCustomCamera1, {
	position: Vector3.create(1, 2, 1),
})
VirtualCamera.create(myCustomCamera1, {
	lookAtEntity: engine.PlayerEntity,
})
```

엔티티가 카메라에 의해 따라가고 있는 경우, 이는 카메라의 위치가 아니라 회전만 변경합니다.

카메라가 회전해도 다음을 가진 엔티티의 Transform은 `VirtualCamera` 컴포넌트는 변경되지 않습니다. 그러나 다음의 Transform에서 카메라의 회전을 읽을 수 있습니다 `engine.CameraEntity`. 이 엔티티의 회전과 위치는 절대값이며, 다음을 가진 엔티티의 값에 의해 조건지어지지 않습니다 `VirtualCamera` 컴포넌트. 이 Transform의 회전은 다음의 영향도 받습니다 `lookAtEntity` 동작.

{% hint style="warning" %}
**📔 참고**: 가상 카메라를 다음과 함께 구성하면 `lookAtEntity` 가상 카메라를 보유한 동일한 엔티티를 참조하거나 `engine.CameraEntity` 엔티티를 참조하면, 결과 동작은 어떤 엔티티도 할당하지 않은 것과 같습니다.
{% endhint %}

## 플레이어에 부착

가상 카메라의 또 다른 용도는 가상 카메라를 플레이어 엔티티에 부착하여 사용자 지정 거리나 각도에서 플레이어를 따라가는 것입니다. 플레이어는 카메라의 회전을 자유롭게 변경할 수 없으므로, 이 경우 카메라의 회전은 가상 카메라의 회전으로 고정됩니다. 이는 예를 들어 플레이어가 항상 앞을 바라보도록 기대되는 레이싱 게임에 유용할 수 있습니다.

```ts
function main() {
	const myCustomCamera = engine.addEntity()
	Transform.create(myCustomCamera, {
		position: Vector3.create(0, 1, 5),
		parent: engine.PlayerEntity,
	})
	VirtualCamera.create(myCustomCamera, {
		defaultTransition: { transitionMode: VirtualCamera.Transition.Time(2) },
	})

	const mainCamera = MainCamera.createOrReplace(engine.CameraEntity, {
		virtualCameraEntity: myCustomCamera,
	})
}
```

## 카메라와 콜라이더

플레이어의 카메라가 3인칭 모드에서 움직일 때, 엔티티에 할당된 충돌 레이어에 따라 카메라가 콜라이더에 막힐 수도 있고 그렇지 않을 수도 있습니다. 씬을 설계할 때 이 점을 유의하세요. 카메라가 벽이나 다른 엔티티를 통과하지 못하게 하고 싶을 수 있습니다.

다음 [콜라이더](/creator/content-creator-ko/sdk7/3d/colliders.md#cameras-and-colliders) 씬의 콜라이더를 구성하는 방법에 대한 자세한 내용은

## 관전 모드(관찰자 카메라)

플레이어를 일반 아바타 이동에서 자유 이동 또는 플레이어 추적 카메라로 전환하는 관전 모드를 만들 수 있습니다. 이는 경쟁 게임의 관전자 역할, 라이브 이벤트의 디렉터 카메라, 또는 리플레이 시스템에 유용합니다.

이 패턴은 여러 SDK 기능을 결합합니다:

| 기능           | SDK API                                   | 목적                         |
| ------------ | ----------------------------------------- | -------------------------- |
| 사용자 지정 카메라 뷰 | `VirtualCamera` + `MainCamera`            | 플레이어의 카메라를 대체합니다           |
| 아바타 움직임 고정   | `InputModifier` (`disableAll: true`)      | WASD를 카메라 조작에 사용할 수 있게 합니다 |
| 씬의 플레이어 추적   | `onEnterScene` / `onLeaveScene`           | 추적 대상 목록을 만듭니다             |
| 카메라 조작       | `inputSystem.isPressed(InputAction.IA_*)` | 피치/요에는 WASD, 줌에는 E/F       |
| 마우스 룩        | `PrimaryPointerInfo.screenDelta`          | 마우스로 카메라 회전                |

### 카메라 리그 아키텍처

다음을 사용하세요 **두 엔티티 리그** 따라서 요와 피치가 서로 독립적으로 유지됩니다:

```
rigRoot (entity)           -- 월드 위치 + 요 회전
└── rigCamera (child)      -- 피치 회전 + 궤도 오프셋
    └── VirtualCamera
```

`rigRoot` 요(좌우 회전)를 보유하며, 추적 대상 또는 자유 카메라 피벗 쪽으로 lerp됩니다. `rigCamera` 피치(위/아래 기울기)와 루트로부터의 궤도 거리를 처리합니다. 요와 피치를 두 개의 Transform으로 분리하면 오일러 수학이 단순해집니다.

### 활성화 및 비활성화

```ts
// 관전 모드 활성화
const rigRoot = engine.addEntity()
Transform.create(rigRoot, {
  position: Vector3.create(8, 8, 8),
  rotation: Quaternion.fromEulerDegrees(0, 0, 0),
})

const rigCamera = engine.addEntity()
Transform.create(rigCamera, { parent: rigRoot })
VirtualCamera.create(rigCamera, {})

MainCamera.createOrReplace(engine.CameraEntity, { virtualCameraEntity: rigCamera })
InputModifier.createOrReplace(engine.PlayerEntity, {
  mode: InputModifier.Mode.Standard({ disableAll: true }),
})
```

```ts
// 관전 모드 비활성화
// 중요: VirtualCamera 엔티티를 제거하기 전에 MainCamera를 먼저 지우세요.
// 엔티티를 먼저 제거하면 엔진이 죽은 엔티티에 계속 바인딩합니다
// 그러면 시점이 플레이어의 발밑으로 떨어집니다.
const mainCamera = MainCamera.getMutableOrNull(engine.CameraEntity)
if (mainCamera) mainCamera.virtualCameraEntity = undefined

engine.removeEntity(rigCamera)
engine.removeEntity(rigRoot)

InputModifier.createOrReplace(engine.PlayerEntity, {
  mode: InputModifier.Mode.Standard({ disableAll: false }),
})
```

{% hint style="danger" %}
**경고:** 항상 지우세요 `MainCamera.virtualCameraEntity` 카메라 엔티티를 제거하기 전에. 엔티티를 먼저 제거하면 엔진이 죽은 참조를 가리키게 되어 시점이 깨집니다.
{% endhint %}

### 카메라 경계

엔진은 다음을 비활성화합니다 `VirtualCamera` 씬의 parcel 경계를 벗어나 이동하는 엔티티. 카메라가 씬 범위를 벗어나면 조용히 작동을 멈춥니다. 카메라 위치가 씬의 축 정렬 경계 상자(AABB) 안에 있도록 매 프레임 작은 여유를 두고 제한하세요.

```ts
// 1x1 parcel 씬의 예시 경계 (16m x 16m)
const BOUNDS_MIN = Vector3.create(0, 0, 0)
const BOUNDS_MAX = Vector3.create(16, 20, 16)
const BOUNDS_MARGIN = 0.5
```

더 큰 씬의 경우, 경계를 다음과 일치하도록 설정하세요 `scene.json` parcel. 씬의 높이 제한은 parcel 수와 관계없이 330미터입니다. 4x4 parcel 씬에서는 다음을 사용할 수 있습니다 `Vector3.create(64, 330, 64)`.

### 플레이어 추적

다음을 사용하세요 `onEnterScene` 과 `onLeaveScene` 을 사용해 씬의 플레이어 목록을 실시간으로 만들 수 있습니다. 그러면 플레이어는 키로 추적 대상을 순환할 수 있습니다(예: `IA_ACTION_3` 과 `IA_ACTION_4`). 플레이어를 추적할 때 리그 루트는 추적 대상 플레이어의 Transform 위치 쪽으로 lerp되며, 자식 카메라는 설정 가능한 거리에서 공전합니다.

```ts
import { onEnterScene, onLeaveScene } from '@dcl/sdk/src/players'

const playerEntities = new Map<string, Entity>()
let playerIds: string[] = []

onEnterScene((player) => {
  if (!player) return
  playerIds.push(player.userId)
  playerEntities.set(player.userId, player.entity)
})

onLeaveScene((userId) => {
  if (!userId) return
  playerIds = playerIds.filter((id) => id !== userId)
  playerEntities.delete(userId)
})
```

매 프레임 플레이어를 추적하려면 리그 루트를 그 위치 쪽으로 lerp하세요:

```ts
const followEntity = playerEntities.get(followTargetId)
if (followEntity) {
  const targetPos = Transform.get(followEntity).position
  const rootTransform = Transform.getMutable(rigRoot)
  rootTransform.position = Vector3.lerp(
    rootTransform.position,
    Vector3.add(targetPos, Vector3.create(0, 1, 0))이
    0.1
  )
}
```

다음 [플레이어가 씬에 들어오거나 나갑니다](/creator/content-creator-ko/sdk7/interactivity/event-listeners.md#player-enters-or-leaves-scene) 자세한 내용은 `onEnterScene` 과 `onLeaveScene`.

### 관전 중 마우스 룩

읽기 `PrimaryPointerInfo.screenDelta` 매 프레임 마우스로 카메라를 회전하세요. 이 방식은 포인터가 잠겨 있는 동안에도 작동합니다. 다음을 참조하세요 [마우스 이동](/creator/content-creator-ko/sdk7/interactivity/mouse-movement.md) 전체 마우스 룩 예제를 보세요.

```ts
// 픽셀당 0.15도
let yaw = 0
let pitch = 45

function spectateMouseLook() {
  const isLocked = PointerLock.getOrNull(engine.CameraEntity)?.isPointerLocked ?? false
  if (!isLocked) return

  const delta = PrimaryPointerInfo.getOrNull(engine.RootEntity)?.screenDelta
  if (!delta) return

  yaw = (yaw + delta.x * MOUSE_SENSITIVITY) % 360
  // delta.y는 아래쪽으로 증가하므로, 마우스 위쪽은 음수입니다. 이를 더하면 카메라가 위로 기울어집니다. 뒤집힘을 방지하려면 제한하세요
  pitch = Math.max(-25, Math.min(80, pitch + delta.y * MOUSE_SENSITIVITY))
}
```

{% hint style="warning" %}
**참고:** `screenDelta` 데스크톱 전용입니다. 모바일에서는 항상 다음을 반환합니다 `0`.
{% endhint %}

### 전체 참고 구현

이 [`33,20-spectate-mode`](https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/33,20-spectate-mode) 테스트 씬에는 다음에 포함된 자체 완결형 관전 모듈이 있습니다 `src/spectate.ts`. free-cam 및 follow-cam 모드, WASD + 마우스 룩 조작, 플레이어 순환, 궤도 거리 줌, 경계 제한, 그리고 조작법과 현재 추적 대상을 보여 주는 화면 HUD를 지원합니다.

자신의 프로젝트에서 사용하려면:

1. 복사 `src/spectate.ts` 를 씬에 넣으세요.
2. 업데이트 `PIVOT`, `BOUNDS_MIN`, 그리고 `BOUNDS_MAX` 를 파일 상단에서 씬의 parcel에 맞게 설정하세요. 이것이 가장 흔한 통합 실수입니다.
3. 연결하세요 `toggleSpectate()` 를 어떤 트리거에도 연결할 수 있습니다. 예를 들어 클릭 가능한 엔티티, UI 버튼 또는 키 입력.

{% hint style="info" %}
**💡 팁**: 카메라 제어의 동작 예시는 다음을 참조하세요 [`2,22-virtual-cameras`](https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/2,22-virtual-cameras) 테스트 씬입니다. 이 씬은 여러 `VirtualCamera` 엔티티를 다음을 통해 순환하며 `MainCamera` 포함: `lookAtEntity` 플레이어를 향한 카메라와 트윈 처리된 카메라; [`32,20-virtual-camera-mouse-look`](https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/32,20-virtual-camera-mouse-look), 포인터가 잠겨 있는 동안 마우스 룩 카메라를 작동시키는 씬; [`33,20-spectate-mode`](https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/33,20-spectate-mode), follow-cam 및 free-cam 모드를 갖춘 완전한 관전/관찰자 카메라를 구현한 씬; 그리고 [`9,99-modifier-areas`](https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/9,99-modifier-areas), 볼륨 내부에서 카메라 모드를 강제하는 씬입니다 `CameraModeArea`.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/creator/content-creator-ko/sdk7/3d/camera.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
