> 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-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md).

# 点击事件

了解如何处理场景中的用户点击。

Decentraland 场景可以检测用于控制玩家化身的所有按钮所触发的输入操作。这些包括指针点击、若干操作按钮，以及用于移动化身的按键。按钮事件可以来自桌面客户端上的鼠标和键盘、来自 [移动应用中](/creator/content-creator-zh/mian-xiang-yi-dong-duan-gou-jian/yi-dong-ke-hu-duan/overview.md)上的屏幕控件、来自 VR 控制器，或来自其他输入设备——SDK 会将这些都以相同方式解释。

你可以检测针对某个实体的输入操作。这涉及在玩家的光标指向该实体的碰撞体时按下一个按钮。你也可以检测 *global* 输入事件，这类事件是在任何时候激活输入时发生的，不考虑指针当前瞄准的位置。

{% hint style="warning" %}
**📔 注意**：实体必须具有一个 [碰撞体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md) 组件，才能响应输入操作。 `MeshRenderer` 模型也必须赋予一个 `MeshCollider` 组件。来自 `GltfContainer` 的模型可能拥有它们自己嵌入的碰撞几何体，或者可以配置为使用其可见几何体，它们也可以赋予一个 `MeshCollider` 组件。
{% endhint %}

根据使用场景的不同，有几种不同的方式来处理输入操作。

* [**注册回调**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/register-callback.md)：为单个实体添加交互的最简单方式。写一条语句来设置回调函数和悬停反馈。
* [**基于系统的**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/system-based-events.md)：非常适合处理多个具有相似行为的实体。使用系统遍历相似实体，并查询每个实体的输入操作，用相同的逻辑处理它们。悬停反馈需要单独设置。这种方法也适用于处理全局输入操作。
* [**高级**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/advanced-button-events.md)：读取每个实体的原始响应数据，包括时间戳和输入事件历史记录。这对于定义自定义交互模式很有用。

## 在 Creator Hub 中使用 Scene Editor

处理实体上的点击事件最简单的方法是使用场景编辑器。使用无代码 **点击时** 或 **在输入操作时** 在项目上触发器，在点击时调用操作。或者使用 **全局点击时**, **全局主键时** 或 **全局次键时** 触发器来响应全局按钮事件。参见 [让任意项目变为智能项目](/creator/content-creator-zh/chang-jing-bian-ji-qi/jiao-hu-xing/make-any-item-smart.md).

## 简单示例

要检测实体上的点击，请使用 `pointerEventsSystem.onPointerDown`.

```ts
pointerEventsSystem.onPointerDown(
	{
		entity: myEntity,
		opts: { button: InputAction.IA_PRIMARY, hoverText: '点击' },
	},
	function () {
		console.log('已点击实体')
	}
)
```

参见 [**注册回调**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/register-callback.md) 以获取更多信息。

## 悬停反馈

让玩家知道某个实体可交互非常重要。否则，他们可能会完全错过你构建的体验。让玩家挨个点击每个对象，只期待其中一个有反应，这种体验并不好。

当你使用 [**注册回调**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/register-callback.md) 方法时，只要玩家将光标移到对象上方，就会显示两种反馈：

* 实体边缘会被高亮显示（仅在 Decentraland 2.0 桌面客户端上）。如果实体足够近，可以点击，高亮为绿色；如果实体太远，高亮为红色。
* 光标附近会出现一个带有 UI 文本的悬停提示，表明如果点击将会发生什么。

当使用 [**基于系统的**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/system-based-events.md) 方法时，你可以通过添加一个 `PointerEvents` 组件到可点击实体上来达到相同效果。

实体高亮和悬停提示都可以通过这些方法和组件中的属性来禁用。

你也可以实现 [高级自定义提示](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/system-based-events.md#advanced-custom-hints)，例如你可以播放声音，让实体在被指向时变色、旋转或放大等。无论你做什么，都要确保它是一个清晰的提示标识。

## 障碍

按钮事件会发射射线，只与路径上订阅了指针事件碰撞层的第一个实体发生交互。只要实体处于其距离限制范围内，这一点就成立。如果实体同时为摄像机和化身定义了距离限制，那么两者中任意一个足够近就可以。

要让实体被指针事件的射线拦截，必须满足以下任一条件：

* 该模型必须包含 [碰撞网格](/creator/content-creator-zh/3d-jian-mo-yu-dong-hua/colliders.md).
* 该 `GltfContainer` 必须配置为使用 [带碰撞遮罩的可见几何体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md#colliders-on-3d-models).
* 该实体必须有一个 [MeshCollider 组件](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/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_PRIMARY`: **E** 在电脑上的按键， **E 按钮** 在移动端。
* `用于`: **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-zh/mian-xiang-yi-dong-duan-gou-jian/yi-dong-ke-hu-duan/overview.md)以及未来的设备（如 VR 控制器和其他游戏控制器）兼容。

{% hint style="warning" %}
**📱 移动端**：在 [移动客户端](/creator/content-creator-zh/mian-xiang-yi-dong-duan-gou-jian/yi-dong-ke-hu-duan/overview.md)上，所有输入操作都可用，但是 `IA_ACTION_3`—`IA_ACTION_6` （桌面端的 `1`/`2`/`3`/`4` 按钮）被放在二级菜单后面，在游戏过程中不容易触及。为移动端设计时，请优先使用 `IA_POINTER` （交互按钮）、 `IA_PRIMARY` （E 键）以及 `IA_SECONDARY` （F 键）作为你的关键操作。参见 [移动端输入](/creator/content-creator-zh/mian-xiang-yi-dong-duan-gou-jian/kai-fa/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-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/proximity-events.md) 了解如何使用基于接近距离的交互。

## 来自输入操作的数据

所有输入操作都包含事件数据，包括被激活的按钮以及当时指针指向的位置等信息。

可从任何输入事件中获得以下信息：

* `模拟`：可选数字，仅出现在来自模拟输入（如摇杆）的事件中，用于存储输入的模拟值。
* `按钮连接您的钱包`：按下的是哪个按钮 ID。该数字对应于 `InputAction` 枚举，其中列出了所有可用按钮。
* `state`：指针事件类型，来自枚举 `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`
* `时间戳`：一个 [Lamport 时间戳](https://en.wikipedia.org/wiki/Lamport_timestamp) 用于标识每个按钮事件。

  > 注意：此时间戳并不是基于当前时间编号的。可以将其视为一个从 0 开始、每发生一个事件就加 1 的计数器。
* `命中`：一个包含有关命中事件以下数据的对象：
  * `entityId`：被射线命中的实体的 ID 数字。
  * `meshName`: *字符串* ，包含被命中的 3D 模型中具体网格的内部名称。当一个 3D 模型由多个网格组成时，这很有用。
  * `globalOrigin`: *Vector3* 用于表示射线起点的位置（相对于场景）
  * `方向`: *Vector3* 与射线的方向向量一起，位于全局坐标中
  * `位置`: *Vector3* 用于表示射线与命中实体相交的位置（相对于场景）
  * `length`：射线从起点到命中实体发生位置的长度。
  * `normalHit`: *Vector3* 带有一个归一化方向向量，用于描述世界空间中命中法线的角度。

根据你使用的输入操作处理方式，这些数据的访问方法不同。

使用 [**注册回调**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/register-callback.md) 方法时，传入回调函数的第一个参数包含整个数据结构。

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

使用 [**基于系统的**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/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-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/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-zh/mian-xiang-yi-dong-duan-gou-jian/yi-dong-ke-hu-duan/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" %}
**📔 注意**：引擎默认提供一个 `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 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-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/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.
