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

# 클릭 이벤트

씬에서 사용자 클릭을 처리하는 방법을 알아보세요.

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

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

{% hint style="warning" %}
**📔 참고**: 엔티티는 [콜라이더](/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를 사용하세요

엔티티의 클릭 이벤트를 처리하는 가장 쉬운 방법은 Scene Editor를 사용하는 것입니다. 코드 없이 사용할 수 있는 **클릭 시** 또는 **입력 동작 시** 아이템의 Triggers를 사용해 클릭했을 때 동작을 호출하세요. 또는 **전역 클릭 시**, **전역 Primary 시** 또는 **전역 Secondary 시** 전역 버튼 이벤트에 반응하는 Triggers를 사용하세요. 다음을 참조하세요 [어떤 항목이든 스마트 아이템으로 만들기](/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) 메서드, 플레이어가 오브젝트 위로 커서를 가져갈 때마다 두 종류의 피드백이 표시됩니다:

* 엔티티의 가장자리가 강조 표시됩니다(Decentraland 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: '클릭',
		},
	},
	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`: **스페이스** 컴퓨터의 키, **점프 버튼** 모바일에서.
* `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) 근접 기반 상호작용을 사용하는 방법은 다음을 참조하세요.

## 입력 동작의 데이터

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

모든 입력 이벤트에서 다음 정보를 얻을 수 있습니다:

* `아날로그`: 선택적 숫자이며, 조이스틱 같은 아날로그 입력에서 발생한 이벤트에만 존재하고, 입력의 아날로그 값을 저장합니다.
* `버튼`: 어떤 버튼 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`: *String* 맞은 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 %}

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

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

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

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

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

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

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

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

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

사용하세요 `.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 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/interactivity/button-events/click-events.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.
