> 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/interactivity/button-events/click-events.md).

# 클릭 이벤트

씬에서 사용자 클릭을 처리하는 방법을 알아봅니다.

디센트럴랜드 장면은 플레이어의 아바타를 제어하는 데 사용되는 모든 버튼에서 입력 동작을 감지할 수 있습니다. 여기에는 포인터 클릭, 여러 액션 버튼, 그리고 아바타를 이동하는 데 사용되는 키가 포함됩니다. 버튼 이벤트는 데스크톱 클라이언트의 마우스와 키보드, 화면상의 컨트롤, [모바일 앱](/creator/content-creator-ko/build-for-mobile/mobile-client/overview.md), VR 컨트롤러, 또는 다른 입력 장치에서 올 수 있으며 — 이들은 모두 SDK에서 동일하게 해석됩니다.

엔티티를 대상으로 입력 동작을 감지할 수 있습니다. 이는 플레이어의 커서가 해당 엔티티의 콜라이더를 가리키고 있을 때 버튼을 누르는 것을 의미합니다. 또한 감지할 수도 있습니다 *global* 입력 이벤트로, 포인터가 어디를 향하고 있는지와 관계없이 언제든지 입력을 누르거나 활성화하는 것을 포함합니다.

{% hint style="warning" %}
**📔 참고**: 엔티티에는 [collider](/creator/content-creator-ko/sdk7/3d/colliders.md) 입력 동작에 응답하려면 `MeshRenderer` 모델에도 또한 `MeshCollider` 컴포넌트가 필요합니다. 모델은 `GltfContainer` 자체적으로 내장된 충돌 기하를 가질 수 있으며, 보이는 기하를 사용하도록 구성할 수도 있고, 또한 `MeshCollider` 컴포넌트를 추가해야 합니다.
{% endhint %}

사용 사례에 따라 입력 동작을 처리하는 여러 가지 방법이 있습니다.

* [**콜백 등록**](/creator/content-creator-ko/sdk7/interactivity/button-events/register-callback.md): 단일 엔티티에 상호작용을 추가하는 가장 쉬운 방법입니다. 콜백 함수와 호버 피드백을 설정하는 한 줄만 작성하면 됩니다.
* [**시스템 기반**](/creator/content-creator-ko/sdk7/interactivity/button-events/system-based-events.md): 비슷한 동작을 하는 여러 엔티티를 처리하는 데 적합합니다. 시스템을 사용해 비슷한 엔티티들을 순회하며 각 엔티티의 입력 동작을 조회하고, 모두 동일한 로직으로 처리합니다. 호버 피드백은 별도로 설정해야 합니다. 이 방식은 전역 입력 동작을 처리할 때도 필요합니다.
* [**고급**](/creator/content-creator-ko/sdk7/interactivity/button-events/advanced-button-events.md): 각 엔티티의 타임스탬프와 입력 이벤트 기록을 포함한 원시 응답 데이터를 읽습니다. 이는 사용자 정의 상호작용 패턴을 정의하는 데 유용할 수 있습니다.

## Creator Hub의 Scene Editor를 사용하세요

엔티티의 클릭 이벤트를 처리하는 가장 쉬운 방법은 씬 에디터를 사용하는 것입니다. 코드 없이 **클릭 시** 또는 **입력 액션 시** 아이템을 클릭할 때 동작을 호출하는 트리거를 사용하세요. 또는 **전역 클릭 시**, **전역 기본 버튼 시** 또는 **전역 보조 버튼 시** 전역 버튼 이벤트에 반응하는 트리거를 사용하세요. 참조 [모든 항목을 스마트 아이템으로 만들기](/creator/content-creator-ko/scene-editor/interactivity/make-any-item-smart.md).

## 간단한 예시

엔티티의 클릭을 감지하려면 다음을 사용하세요 `pointerEventsSystem.onPointerDown`.

```ts
pointerEventsSystem.onPointerDown(
	{
		entity: myEntity,
		opts: { button: InputAction.IA_PRIMARY, hoverText: '클릭' },
	},
	function () {
		console.log('엔티티를 클릭함')
	}
)
```

다음 [**콜백 등록**](/creator/content-creator-ko/sdk7/interactivity/button-events/register-callback.md) 을(를) 참조하세요.

## 호버 피드백

엔티티가 상호작용 가능하다는 사실을 플레이어가 알아차리게 하는 것이 중요합니다. 그렇지 않으면 여러분이 만든 경험을 완전히 놓칠 수 있습니다. 모든 객체를 하나씩 클릭해 보며 하나가 반응하기를 기대하는 것은 좋은 경험이 아닙니다.

다음을 사용할 때 [**콜백 등록**](/creator/content-creator-ko/sdk7/interactivity/button-events/register-callback.md) 메서드, 플레이어가 객체 위로 커서를 이동할 때마다 두 종류의 피드백이 표시됩니다:

* 엔티티의 가장자리가 강조 표시됩니다(디센트럴랜드 2.0 데스크톱 클라이언트에서만). 엔티티가 클릭하기에 충분히 가까우면 강조 표시가 초록색이고, 너무 멀면 빨간색입니다.
* 커서 근처에 UI 텍스트가 있는 호버 힌트가 나타나며, 클릭하면 어떤 일이 일어날지를 알려줍니다.

다음을 사용할 때 [**시스템 기반**](/creator/content-creator-ko/sdk7/interactivity/button-events/system-based-events.md) 메서드에서는, 다음을 추가하여 같은 결과를 얻을 수 있습니다 `PointerEvents` 클릭 가능한 엔티티에 컴포넌트를 추가하면 됩니다.

엔티티 강조 표시와 호버 힌트는 이 메서드와 컴포넌트의 속성을 통해 둘 다 비활성화할 수 있습니다.

또한 구현할 수도 있습니다 [고급 사용자 정의 힌트](/creator/content-creator-ko/sdk7/interactivity/button-events/system-based-events.md#advanced-custom-hints), 예를 들어 소리를 재생하거나, 엔티티가 가리켜지는 동안 색이 바뀌거나, 회전하거나, 커지도록 할 수 있습니다. 무엇을 하든 그것이 명확한 신호가 되도록 하세요.

## 장애물

버튼 이벤트는 포인터 이벤트 충돌 레이어에 등록된 경로상 첫 번째 엔티티와만 상호작용하는 레이를 발사합니다. 이는 엔티티가 거리 제한 내에 있는 한 적용됩니다. 엔티티가 카메라와 아바타 모두에 대한 거리 제한을 정의한다면, 둘 중 하나만 충분히 가까우면 됩니다.

엔티티가 포인터 이벤트의 레이에 의해 가로채이려면, 다음 중 하나가 필요합니다:

* 모델에는 [충돌체 메쉬가 포함되어야 합니다](/creator/content-creator-ko/3d/colliders.md).
* 이 `GltfContainer` 는 다음을 사용하도록 구성되어야 합니다: [충돌 마스크가 적용된 보이는 지오메트리](/creator/content-creator-ko/sdk7/3d/colliders.md#colliders-on-3d-models).
* 엔티티에는 [MeshCollider 컴포넌트](/creator/content-creator-ko/sdk7/3d/colliders.md).

플레이어가 상호작용하려는 엔티티 앞에 다른 엔티티의 콜라이더가 가로막고 있으면, 해당 엔티티에 콜라이더가 없거나 이 콜라이더가 포인터 이벤트 충돌 레이어에 응답하지 않도록 설정되어 있지 않은 한 뒤에 있는 엔티티를 클릭할 수 없습니다.

```ts
// 클릭 가능한 엔티티
const clickableEntity = engine.addEntity()
MeshRenderer.setBox(clickableEntity)
MeshCollider.setBox(clickableEntity)
Transform.create(clickableEntity, { position: Vector3.create(8, 1, 8) })

pointerEventsSystem.onPointerDown(
	{
		entity: clickableEntity,
		opts: {
			button: InputAction.IA_POINTER,
			hoverText: 'Click',
		},
	},
	function () {
		console.log('엔티티를 클릭함')
		const t = Transform.getMutable(clickableEntity)
		t.scale.y += 0.2
	}
)

// 클릭을 방해하지 않는 요소
const nonBlocker = engine.addEntity()
MeshRenderer.setBox(nonBlocker)
MeshCollider.setBox(nonBlocker, ColliderLayer.CL_PHYSICS)
Transform.create(nonBlocker, { position: Vector3.create(10, 1, 8) })

// 클릭 차단 요소
const blocker = engine.addEntity()
MeshRenderer.setBox(blocker)
MeshCollider.setBox(blocker, ColliderLayer.CL_POINTER)
Transform.create(blocker, { position: Vector3.create(8, 1, 10) })
```

{% hint style="warning" %}
**📔 참고**: 엔티티가 포인터 이벤트를 가로채는 것뿐 아니라 데이터를 반환하려면, 엔티티에는 또한 `PointerEvents` 컴포넌트가 필요합니다.  `pointerEventsSystem` 헬퍼도 이 요구 사항을 처리해 줍니다.
{% endhint %}

## 포인터 버튼

다음 입력은 입력 이벤트를 감지하는 어떤 접근 방식으로도 처리할 수 있습니다.

* `InputAction.IA_POINTER`: **왼쪽 마우스 버튼** 컴퓨터에서는, **상호작용 버튼** 모바일에서는.
* `InputAction.IA_PRIMARY`: **E** 컴퓨터의 키, **E 버튼** 모바일에서는.
* `InputAction.IA_SECONDARY`: **F** 컴퓨터의 키, **F 버튼** 모바일에서는.
* `InputAction.IA_ACTION_3`: **1** 컴퓨터의 키, **1 버튼** 모바일에서는. *모바일에서는 쉽게 닿기 어렵습니다 — 모바일 상호작용에는 권장되지 않습니다.*
* `InputAction.IA_ACTION_4`: **2** 컴퓨터의 키, **2 버튼** 모바일에서는. *모바일에서는 쉽게 닿기 어렵습니다 — 모바일 상호작용에는 권장되지 않습니다.*
* `InputAction.IA_ACTION_5`: **3** 컴퓨터의 키, **3 버튼** 모바일에서는. *모바일에서는 쉽게 닿기 어렵습니다 — 모바일 상호작용에는 권장되지 않습니다.*
* `InputAction.IA_ACTION_6`: **4** 컴퓨터의 키, **4 버튼** 모바일에서는. *모바일에서는 쉽게 닿기 어렵습니다 — 모바일 상호작용에는 권장되지 않습니다.*
* `InputAction.IA_JUMP`: **Space** 컴퓨터의 키, **점프 버튼** 모바일에서는.
* `InputAction.IA_FORWARD`: **W** 컴퓨터의 키.
* `InputAction.IA_LEFT`: **하나의** 컴퓨터의 키.
* `InputAction.IA_RIGHT`: **D** 컴퓨터의 키.
* `InputAction.IA_BACKWARD`: **S** 컴퓨터의 키.
* `InputAction.IA_WALK`: **제어** 컴퓨터의 키.
* `InputAction.IA_MODIFIER`: **Shift** 컴퓨터의 키.

각 `InputAction` 키보드의 실제 입력에서 추상화되어 있으므로, 장치에 따라 다른 입력에 매핑될 수 있습니다. 같은 이유로 키보드의 모든 버튼이 버튼 이벤트로 추적될 수 있는 것은 아니며, 이동과 상호작용에 사용되는 버튼만 추적됩니다. 이러한 의도적인 제한은 콘텐츠가 데스크톱,  [모바일 클라이언트](/creator/content-creator-ko/build-for-mobile/mobile-client/overview.md)그리고 VR 컨트롤러 및 기타 게임 컨트롤러와 같은 미래의 장치 전반에서 호환되도록 보장합니다.

{% hint style="warning" %}
**📱 모바일**: ...에서는 [모바일 클라이언트](/creator/content-creator-ko/build-for-mobile/mobile-client/overview.md), 모든 입력 동작을 사용할 수 있지만 `IA_ACTION_3`–`IA_ACTION_6` (데스크톱의 `1`/`2`/`3`/`4` 버튼)은 보조 메뉴 뒤에 숨겨져 있어 게임 플레이 중에는 쉽게 닿기 어렵습니다. 모바일용으로 설계할 때는 다음을 선호하세요 `IA_POINTER` (상호작용 버튼), `IA_PRIMARY` (E 버튼), `IA_SECONDARY` (F 버튼)을 핵심 동작에 사용하세요. 참조 [모바일에서의 입력](/creator/content-creator-ko/build-for-mobile/develop/input-on-mobile.md).
{% endhint %}

## 포인터 이벤트의 종류

각 입력은 다음과 같은 종류의 포인터 이벤트를 생성할 수 있습니다. 아래 항목들은 모두 다음의 값입니다 `PointerEventType` 열거형.

* `PET_DOWN`: 플레이어가 커서로 엔티티의 콜라이더를 가리키고 있는 동안 특정 버튼을 누릅니다.
* `PET_UP`: 플레이어가 커서로 엔티티의 콜라이더를 가리키고 있는 동안 특정 버튼을 놓습니다.
* `PET_HOVER_ENTER`: 플레이어의 커서가 엔티티의 콜라이더를 가리키기 시작합니다.
* `PET_HOVER_LEAVE`: 플레이어의 커서가 엔티티의 콜라이더를 가리키는 것을 멈춥니다.
* `PET_PROXIMITY_ENTER`: 플레이어가 어디를 보고 있든 상관없이 엔티티의 근접 범위 안으로 들어옵니다.
* `PET_PROXIMITY_LEAVE`: 플레이어가 엔티티의 근접 범위 밖으로 나갑니다.

다음 [**근접 이벤트**](/creator/content-creator-ko/sdk7/interactivity/button-events/proximity-events.md) 근접 기반 상호작용을 사용하는 방법은

## 입력 동작의 데이터

모든 입력 동작에는 활성화된 버튼과 당시 포인터가 가리키고 있던 위치 등의 이벤트 데이터가 포함됩니다.

어떤 입력 이벤트에서도 다음 정보를 얻을 수 있습니다:

* `아날로그`: 선택적 숫자이며, 조이스틱 같은 아날로그 입력에서 발생한 이벤트에만 존재하고, 입력의 아날로그 값을 저장합니다.
* `button`: 어떤 버튼 ID가 눌렸는지 나타냅니다. 이 숫자는 다음과 대응합니다. `InputAction` 사용 가능한 모든 버튼을 나열한 enum입니다.
* `state`: 포인터 이벤트 유형이며, 다음 enum에서 가져옵니다. `PointerEventType`. *0* 다음을 의미합니다 `PointerEventType.PET_UP`, *1* 를 `PointerEventType.PET_DOWN`, *2* 를 `PointerEventType.PET_HOVER_ENTER`, *3* 를 `PointerEventType.PET_HOVER_LEAVE`, *4* 를 `PointerEventType.PET_PROXIMITY_ENTER`, *5* 를 `PointerEventType.PET_PROXIMITY_LEAVE`
* `타임스탬프`: 하나의 [램포트 타임스탬프](https://en.wikipedia.org/wiki/Lamport_timestamp) 각 버튼 이벤트를 식별하는 데 사용됩니다.

  > 참고: 이 타임스탬프는 현재 시간을 기준으로 번호가 매겨지는 것이 아닙니다. 0에서 시작해 각 이벤트마다 1씩 증가하는 카운터라고 생각하세요.
* `히트`: 히트 이벤트에 대한 다음 데이터를 포함하는 객체입니다:
  * `entityId`: 레이에 맞은 엔티티의 ID 번호입니다.
  * `meshName`: *문자열* 로, 맞은 3D 모델 내부의 특정 메쉬에 대한 내부 이름을 담습니다. 3D 모델이 여러 메쉬로 구성되어 있을 때 유용합니다.
  * `globalOrigin`: *Vector3* 레이가 시작되는 위치(씬 기준)용
  * `방향`: *Vector3* 전역 좌표계에서 레이의 방향 벡터를 사용하여
  * `위치`: *Vector3* 레이가 맞은 엔티티와 교차한 위치(씬 기준)용
  * `length`: 원점에서 엔티티와의 충돌이 발생한 위치까지의 레이 길이입니다.
  * `normalHit`: *Vector3* 정규화된 방향 벡터를 사용하여, 월드 공간에서 히트의 법선 각도를 설명합니다.

이 데이터는 입력 동작을 처리하는 데 사용하는 접근 방식에 따라 다른 방식으로 접근합니다.

다음을 사용할 때 [**콜백 등록**](/creator/content-creator-ko/sdk7/interactivity/button-events/register-callback.md) 접근 방식에서는 콜백 함수로 전달되는 첫 번째 매개변수에 이 전체 데이터 구조가 포함됩니다.

```ts
pointerEventsSystem.onPointerDown({ entity: myEntity }, function (cmd) {
	console.log(cmd.hit?.entityId)
})
```

다음을 사용할 때 [**시스템 기반**](/creator/content-creator-ko/sdk7/interactivity/button-events/system-based-events.md) 접근 방식에서는 다음을 사용하세요 `inputSystem.getInputCommand()` 이 데이터를 가져옵니다.

```ts
engine.addSystem(() => {
	const cmd = inputSystem.getInputCommand(
		InputAction.IA_POINTER,
		PointerEventType.PET_DOWN,
		myEntity
	)
	if (cmd) {
		console.log(cmd.hit?.entityId)
	}
})
```

{% hint style="warning" %}
**📔 참고**: 엔티티가 포인터 이벤트를 가로채는 것뿐 아니라 데이터를 반환하려면, 엔티티에는 또한 `PointerEvents` 컴포넌트가 필요합니다.  `pointerEventsSystem` 헬퍼도 이 요구 사항을 처리해 줍니다.
{% endhint %}

다음을 사용할 때 [**고급**](/creator/content-creator-ko/sdk7/interactivity/button-events/advanced-button-events.md) 접근 방식에서는 `PointerEventsResult` 해당 엔티티에 대한 모든 포인터 이벤트의 최근 기록 목록이 포함되어 있습니다.

```ts
engine.addSystem(() => {
	for (const [entity] of engine.getEntitiesWith(PointerEventsResult)) {
		const pointerEvents = PointerEventsResult.get(entity)

		for (const event of pointerEvents) {
			console.log(event.hit?.entityId)
		}
	}
})
```

## 커서 잠그기 또는 잠금 해제

{% hint style="info" %}
**💡 참고**: `PointerLock` 는 데스크톱 클라이언트 개념입니다(잠긴 마우스 커서 vs. 잠금 해제된 마우스 커서). 이는 다음에는 영향을 주지 않습니다 [모바일 앱](/creator/content-creator-ko/build-for-mobile/mobile-client/overview.md), 입력이 터치 기반인 곳에서는.
{% endhint %}

디센트럴랜드의 데스크톱 클라이언트에서 플레이어는 커서를 사용해 두 가지 서로 다른 상호작용 모드를 전환할 수 있습니다:

* 잠긴 커서: 마우스를 움직이면 카메라가 이동하고 클릭 이벤트는 화면 중앙의 십자선에서 발생합니다
* 잠금 해제된 커서: 커서는 카메라와 독립적으로 자유롭게 이동할 수 있으며, 플레이어는 화면 어디든 클릭해 3D 공간 또는 UI와 상호작용할 수 있습니다

이러한 상호작용 모드는 서로 다른 종류의 게임 메커니즘에 적합하며, 장면에 가장 잘 맞는 방식에 따라 플레이어가 둘 중 하나를 사용하도록 하고 싶을 수 있습니다. 또한 플레이어가 UI와 상호작용할 수 있도록 잠시 커서를 잠금 해제해 두고 싶을 수도 있는데, 이는 이 조작 방식에 익숙하지 않은 신규 플레이어에게 특히 도움이 됩니다.

플레이어의 커서 상태를 변경하려면 다음을 사용하세요 `PointerLock` 컴포넌트를 다음에 `engine.CameraEntity` 엔티티를 사용해 플레이어 카메라 위치에서 앞으로 레이를 추적합니다.

```ts
import {PointerLock} from '@dcl/sdk/ecs'
    
PointerLock.createOrReplace(engine.CameraEntity, {isPointerLocked: false})
```

{% hint style="warning" %}
**📔 참고**: 엔진은 기본적으로 ...를 제공합니다 `PointerLock` 컴포넌트를 다음에 `engine.CameraEntity` 기본적으로. 다음을 사용하세요 `createOrReplace()` 또는 `getMutable()` 대신 `create()`, 이는 해당 컴포넌트가 이미 존재하면 오류를 발생시킵니다.
{% endhint %}

또한 다음을 읽어 플레이어의 커서 상태를 조회할 수 있습니다 `PointerLock` 컴포넌트의 상태.

```ts
import {PointerLock} from '@dcl/sdk/ecs'

const isPointerLocked = PointerLock.getOrNull(engine.CameraEntity)?.isPointerLocked
```

다음을 사용하세요 `.onChange` 포인터 상태의 변경에 반응하려면 onChange 함수를 사용하세요. 다음 예시는 커서가 항상 잠금 해제되도록 강제합니다.

```ts
import {PointerLock} from '@dcl/sdk/ecs'

export function main() {

    PointerLock.createOrReplace(engine.CameraEntity, {isPointerLocked: false});

    PointerLock.onChange(engine.CameraEntity, (pointerLock) => {
		    if (!pointerLock) return
		    if(pointerLock.isPointerLocked){
			    PointerLock.getMutable(engine.CameraEntity).isPointerLocked = false
		   }
	})
}
```


---

# 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/interactivity/button-events/click-events.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.
