> 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) {
	console.log('플레이어가 1인칭 카메라를 사용 중입니다')
} else {
	console.log('플레이어가 3인칭 카메라를 사용 중입니다')
}
```

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

```ts
CameraMode.onChange(engine.CameraEntity, (cameraMode) => {
	if (!cameraMode) return
	console.log('플레이어의 카메라 모드가 다음으로 변경되었습니다', cameraMode.mode)
})
```

## 가상 카메라 사용하기

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

* 가상 카메라 만들기: 씬에 엔티티를 만들고 다음을 부여하세요. `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 모델에 다음이 포함되어 있다면 `카메라` 노드가 콘텐츠의 일부로 포함되어 있더라도 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/player-avatar.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 (엔티티)           -- 월드 위치 + 요우 회전
└── rigCamera (자식)      -- 피치 회전 + 궤도 오프셋
    └── 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. side당 N개 parcel의 최대 높이는 대략 `log2(N+1) * 20` 미터이므로, 4x4 parcel 씬에서는 다음을 사용합니다. `Vector3.create(64, 80, 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
const MOUSE_SENSITIVITY = 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) test scene에는 다음 위치에 독립형 관전 모듈이 포함되어 있습니다. `src/spectate.ts`. 자유 카메라와 추적 카메라 모드, WASD + 마우스 룩 컨트롤, 플레이어 순환, 궤도 거리 줌, 경계 클램핑, 그리고 컨트롤과 현재 추적 대상을 보여 주는 온스크린 HUD를 지원합니다.

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

1. Copy `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) test scene으로, 여러 `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), 자유 카메라 및 추적 카메라 모드를 갖춘 완전한 관전/관찰자 카메라를 구현하는 것; 그리고 [`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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
