> 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/system-based-events.md).

# 基于系统的事件

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

如果你的场景中有多个相似实体，并且它们都使用相同逻辑激活，你可以编写一个单独的系统来遍历它们所有，并且只描述一次这种行为。这也是性能最高、并且更 [数据导向的](/creator/content-creator-zh/chang-jing-sdk7/jia-gou/data-oriented-programming.md) 方法。

你也可以使用一个系统来检测 [全局输入事件](#global-input-events)，这样无论玩家光标瞄准哪里，只要按下某个按键，场景就会作出反应。

如果你只是想点击或按下单个实体上的按钮来激活它，最简单的方法是使用 [注册回调](/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/advanced-button-events.md) 方法。

要让实体可以交互，它必须具有 [碰撞体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md)。见 [障碍物](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md#obstacles) 以获取更多细节。这同样适用于近距离点击事件。

## 使用系统

通过在输入 `inputSystem` 命名空间中运行一个辅助函数来检查按钮事件，每个 tick 都执行一次，位于 [系统](/creator/content-creator-zh/chang-jing-sdk7/jia-gou/systems.md).

例如，下面的系统使用 `inputSystem.isTriggered()` 函数来检查指针是否被点击。每个 tick，它都会检查按钮是否被按下。如果 `inputSystem.isTriggered()` 返回 *真*，系统就会响应并执行一些自定义逻辑。

```ts
engine.addSystem(() => {
	if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_DOWN)) {
		// 响应按钮按下的逻辑
	}
})
```

以下辅助函数可在 `inputSystem` 命名空间中使用，并且同样可以在每个 tick 中调用：

* `inputSystem.isTriggered`：如果自上一次 tick 以来发生了某个输入动作，则返回 true。
* `inputSystem.isPressed`：如果某个输入当前正被按住，则返回 true。在按钮再次松开之前，每个 tick 都会返回 true。
* `inputSystem.getInputCommand`：返回一个包含输入动作数据的对象。

请参阅下面各部分以了解更多细节。

在处理实体上的按钮事件时，始终要向玩家提供反馈，让玩家知道这个实体可以交互。如果你添加一个 `PointerEvents` 组件到某个实体上，玩家在将光标悬停在该实体上时会看到提示。请参阅 [显示反馈](#show-feedback) 了解如何在可交互实体上添加悬停提示。

### 全局输入事件

使用 `inputSystem.isTriggered` 用于检测 SDK 跟踪的任意输入上的按下和松开事件。

```ts
engine.addSystem(() => {
	if (
		inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_DOWN)
	) {
		// 响应按钮按下的逻辑
	}
})
```

上面的示例会检查按钮是否被按下，而不管指针当前在哪里，或者路径上可能有哪些实体。

`inputSystem.isTriggered()` 返回 *真* 仅当当前游戏循环 tick 中指示的按钮被按下时才返回。如果按钮没有被按下，或者它已经在上一个 tick 中处于按下状态，它会返回 *false*.

{% hint style="warning" %}
**📔 注意**：玩家需要站在场景边界内，才能检测到指针事件。玩家的光标也需要被锁定；在自由光标状态下按下的按钮不会被检测到。

你想要与之交互的每个实体都必须同时具有 [`PointerEvents` 组件](#show-feedback) 和一个 [碰撞体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md).
{% endhint %}

该 `inputSystem.isTriggered` 函数需要以下必需参数：

* `InputAction`：要监听的输入，取值来自 `InputAction` 枚举。参见 [指针按钮](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md#pointer-buttons) 以了解支持的选项。
* `PointerEventType`：要监听的事件类型，取值来自 `PointerEventType` 枚举。参见 [指针事件的类型](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md#types-of-pointer-events) 以了解支持的选项。

### 激活实体

如果想在指向某个特定实体时检测按钮事件，可以向 `inputSystem.isTriggered` 传入第三个可选参数，以指定要检查的实体。

```ts
// 给实体添加一个 PointerEvents 组件
PointerEvents.create(myEntity, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_PRIMARY,
				showFeedback: false,
			},
		},
	],
})

// 创建系统
engine.addSystem(() => {
	if (
		inputSystem.isTriggered(
			InputAction.IA_POINTER,
			PointerEventType.PET_DOWN,
			myEntity
		)
	) {
		// 响应对 myEntity 的按钮按下逻辑
	}
})
```

上面的示例会在每个 tick 中检查一个硬编码实体是否被指针按钮（鼠标左键）按下。

该 `inputSystem.isTriggered` 函数接受以下参数：

* `InputAction`：要监听的输入，取值来自 `InputAction` 枚举。参见 [指针按钮](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md#pointer-buttons) 以了解支持的选项。
* `PointerEventType`：要监听的事件类型，取值来自 `PointerEventType` 枚举。参见 [指针事件的类型](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md#types-of-pointer-events) 以了解支持的选项。
* `Entity` *（可选）*：要检查哪个实体。如果未提供值，它将检查按钮的全局按下情况，而不考虑玩家光标指向哪里。

请注意，在这个示例中，我们还给想要交互的实体添加了一个 `PointerEvents` 组件。没有这个组件，实体就无法被 `inputSystem`的任何函数检测到。请参阅 [显示反馈](#show-feedback) 以了解更多关于 `PointerEvents` 组件。

{% hint style="warning" %}
**📔 注意**：以及一个 [碰撞体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md)。见 [障碍物](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md#obstacles) 了解更多详情。
{% endhint %}

如果有多个实体可以以相同方式与玩家交互，可以考虑使用 `inputSystem.getInputCommand`。该命令会返回有关全局点击命令的信息，包括实体 ID，你可以用它来执行一个能够处理所有实体的单一函数。

```ts
engine.addSystem(() => {
	const result = inputSystem.getInputCommand(
		InputAction.IA_POINTER,
		PointerEventType.PET_DOWN
	)
	if (result) {
		if (result.hit?.entityId === myEntity) {
			// 处理点击
		}
	}
})
```

{% hint style="warning" %}
**📔 注意**：你想要与之交互的每个实体都必须同时具有 [`PointerEvents` 组件](#show-feedback) 和一个 [碰撞体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md).
{% endhint %}

参见 [来自输入动作的数据](#data-from-input-action) 以获取更多信息。该方法还会为你提供关于指针事件命中的更详细数据。

### 输入按钮松开

你也可以用几乎相同的方式检查指针松开事件，使用 `PointerEventType.PET_UP`.

```ts
engine.addSystem(() => {
	if (
		inputSystem.isTriggered(
			InputAction.IA_POINTER,
			PointerEventType.PET_UP,
			myEntity
		)
	) {
		// 响应在指向 myEntity 时按钮松开的逻辑
	}
})
```

{% hint style="warning" %}
**📔 注意**：当针对某个特定实体检查指针松开事件时，它不会考虑按钮按下时光标指向哪里。它只会考虑按钮松开时光标指向哪里。

另外请记住，该实体必须同时具有一个 [`PointerEvents` 组件](#show-feedback) 和一个 [碰撞体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md).
{% endhint %}

### 检查按住的按钮

通过使用 `inputSystem.isPressed()` 在系统内部检查按钮当前是否被按住。

```ts
engine.addSystem(() => {
	if (inputSystem.isPressed(InputAction.IA_POINTER)) {
		// 响应按钮持续按住的逻辑
	}
})
```

`inputSystem.isPressed()` 返回 *真* 如果按钮当前正被按住，无论按钮是在什么时候按下的，也无论玩家光标指向哪里，都会返回 *false*.

该 `inputSystem.isPressed` 函数只接受一个参数：

* `InputAction`：要监听的输入，取值来自 `InputAction` 枚举。参见 [指针按钮](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md#pointer-buttons) 以了解支持的选项。

### 处理多个实体

如果你的场景中有多个会以相同方式受指针事件影响的实体，那么编写一个遍历它们所有的系统是合理的。

```ts
engine.addSystem(() => {
	const activatedEntites = engine.getEntitiesWith(PointerEvents)
	for (const [entity] of activatedEntites) {
		if (
			inputSystem.isTriggered(
				InputAction.IA_POINTER,
				PointerEventType.PET_DOWN,
				实体
			)
		) {
			// 响应与实体交互的逻辑
		}
	}
})
```

这个示例使用一个 [组件查询](/creator/content-creator-zh/chang-jing-sdk7/jia-gou/querying-components.md) 来遍历所有带有 `PointerEvents` 组件的实体。然后它会使用 `inputSystem.isTriggered`检查这些实体，一个一个地遍历它们。如果在这些实体中的任意一个上检测到输入动作，就会执行自定义逻辑。

与其在一个系统中遍历 *所有* 带有 `PointerEvents` 组件的实体，不如编写不同的系统来处理应该以不同方式行为的实体。推荐的方法是用特定的 [自定义组件](/creator/content-creator-zh/chang-jing-sdk7/jia-gou/custom-components.md)来标记不同类型的实体，并在独立的系统中遍历它们。

```ts
engine.addSystem(() => {
	const activatedDoors = engine.getEntitiesWith(IsDoor, PointerEvents)
	for (const [entity] of activatedDoors) {
		if (
			inputSystem.isTriggered(
				InputAction.IA_POINTER,
				PointerEventType.PET_DOWN,
				实体
			)
		) {
			openDoor(entity)
		}
	}
})

engine.addSystem(() => {
	const pickedUpGems = engine.getEntitiesWith(IsGem, PointerEvents)
	for (const [entity] of pickedUpGems) {
		if (
			inputSystem.isTriggered(
				InputAction.IA_POINTER,
				PointerEventType.PET_DOWN,
				实体
			)
		) {
			pickUp(entity)
		}
	}
})
```

这个示例有一个系统会遍历所有带有名为 `IsDoor` 自定义组件的实体，另一个系统会遍历所有带有名为 `isGem`的自定义组件的实体。在这两个系统中，它都会检查每个匹配的实体，看看它们是否被指针按钮激活。

这种组织场景代码的方式非常 [数据导向的](/creator/content-creator-zh/chang-jing-sdk7/jia-gou/data-oriented-programming.md) ，并且应该会非常高效地利用内存资源。

\## 实体上的多个按钮

使用像 `pointerEventsSystem.onPointerDown`这样的辅助工具时，每个实体都只能有一种事件类型。你不能注册两个不同的按钮事件。在使用基于系统的事件时就没有这个限制。例如，下面这个实体既可以通过按 E，又可以通过按 F 与玩家交互，并分别触发不同的功能。

```ts
    engine.addSystem(() => {
        if (inputSystem.isTriggered(InputAction.IA_PRIMARY, PointerEventType.PET_DOWN, myEntity)) {
            // 响应 E
        }
        if (inputSystem.isTriggered(InputAction.IA_SECONDARY, PointerEventType.PET_DOWN, myEntity)) {
            // 响应 F
        }
    })
```

## 显示反馈

要在指向实体时显示 UI 提示，请使用实体的 `PointerEvents` 组件。

```ts
// 创建实体
const myEntity = engine.addEntity()

// 给实体添加一个 PointerEvents 组件
PointerEvents.create(myEntity, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_POINTER,
			},
		},
	],
})
```

每当玩家的光标指向该实体中的碰撞体时，UI 就会显示一个悬停提示，以表明该实体可以交互。请参阅下面的部分以了解可配置项。

{% hint style="warning" %}
**📔 注意**： `PointerEvents` 组件只负责显示悬停反馈。要使用自定义逻辑处理按钮事件本身，请参阅 [使用系统](#check-for-events).
{% endhint %}

该 `PointerEvents` 组件至少需要一个指针事件定义。每个指针事件定义都可以通过以下内容进行配置：

* `eventType`：要监听的事件类型，取值来自 `PointerEventType` 枚举。参见 [指针事件的类型](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md#types-of-pointer-events) 以了解支持的选项。
* `eventInfo`：一个可以包含以下字段的对象：
  * `按钮连接您的钱包` (*必需*）：要监听的输入，取值来自 `InputAction` 枚举。参见 [指针按钮](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/click-events.md#pointer-buttons) 以了解支持的选项。
  * `hoverText` *（可选）*：在悬停反馈提示中显示的字符串。默认是“Interact”。
  * `showFeedback` *（可选）*：如果为 false，则会隐藏该实体的悬停提示和边缘高亮。 *真* 。
  * `showHighlight` *（可选）*：如果为 true，玩家将会在光标悬停在实体上时看到边缘高亮。 *真* 默认如此。只有在以下情况下才会考虑此值： `showFeedback` 为 *真*.
  * `maxDistance` *（可选）*：玩家的 **化身** 可以距离实体多远而仍然能够与之交互，单位为米。默认值为 *10 米*。见 [最大距离](#max-distance).
  * `maxCameraDistance` *（可选）*： **当前活动摄像机** 可以距离实体多远而仍然能够与之交互，单位为米。默认不设置。请参阅 [最大距离](#max-distance).
  * `优先级` *（可选）*：当多个重叠时，哪个实体胜出。数值更高的优先。 *0* 。

单个 `PointerEvents` 组件可以包含多个指针事件定义，从而检测不同按钮的不同事件。每个实体只能有一个 *一个* `PointerEvents` 组件，但该组件的 `pointerEvents` 数组中可以包含多个对象，每个对象对应一个要响应的事件。

```ts
PointerEvents.create(NPCEntity, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_POINTER,
				hoverText: '问候',
			},
		},
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_PRIMARY,
				hoverText: '掐住',
			},
		},
		{
			eventType: PointerEventType.PET_UP,
			eventInfo: {
				button: InputAction.IA_PRIMARY,
				hoverText: '停止掐住',
			},
		},
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_SECONDARY,
				hoverText: '赠送宝石',
			},
		},
	],
})
```

玩家会看到多个标签，每个指针事件一个，围绕光标径向显示。

下面的示例将使用 `PointerEvents` 来显示悬停提示，并结合一个实际用自定义逻辑处理玩家动作的系统。

```ts
// 创建实体
const myEntity = engine.addEntity()

// 给实体提供悬停反馈
PointerEvents.create(myEntity, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_POINTER,
				hoverText: '打开',
			},
		},
	],
})

// 处理实体上的点击事件
engine.addSystem(() => {
	if (
		inputSystem.isTriggered(
			InputAction.IA_POINTER,
			PointerEventType.PET_DOWN,
			myEntity
		)
	) {
		// 响应输入动作的自定义逻辑
	}
})
```

### 悬停反馈

当玩家将光标悬停在带有一个 `PointerEvents` 组件的物品上时，他们会看到：

* 实体上的边缘高亮
* 光标附近会出现一个悬停提示，其中带有他们需要按下的按钮图标，以及显示“Interact”的字符串。

这些元素可以切换显示，也可以自定义。

UI 上的悬停反馈会根据你在 `按钮连接您的钱包` 字段中选择的输入显示不同的图标。在 PC 上，它会显示一个带有 `E` 用于 `InputAction.IA_PRIMARY`，一个 `F` 用于 `InputAction.IA_SECONDARY`，以及一个鼠标图标，用于 `InputAction.IA_POINTER`.

通过修改 `hoverText` 值来更改字符串。请保持这个字符串简短，这样便于快速阅读，也不会对屏幕造成太大干扰。

```ts
// 创建实体
const chest = engine.addEntity()

// 给实体添加一个 PointerEvents 组件
PointerEvents.create(chest, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_POINTER,
				hoverText: '打开',
			},
		},
	],
})
```

在上面的示例中，指针事件包含一个 `hoverText`的值。此字段定义了当玩家指向实体时在 UI 中显示的字符串。默认情况下，这个字符串会拼写为 *交互*.

{% hint style="info" %}
**💡 提示**： `hoverText` 字符串应描述交互时发生的动作。例如 `打开`, `激活`, `拾取`, `选择`。这些字符串应尽可能简短，以避免过多分散玩家注意力。
{% endhint %}

如果一个实体上有多个指针事件，这些事件的悬停提示会围绕光标径向显示。

该 `hoverText` 一个 `.UP` 指针事件只有在玩家已经按住对应按键并指向该实体时才会显示。

如果一个实体同时具有 `DOWN` 指针事件和一个 `UP` 指针事件时， `DOWN` 动作的提示会在按钮未被按下时显示。提示会切换为 `UP` 事件对应的提示，只有在按钮被按下并保持按下时才会显示。

```ts
// 创建实体
const entity = engine.addEntity()

// 给实体添加一个 PointerEvents 组件
PointerEvents.create(entity, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_POINTER,
				hoverText: '拖动',
			},
		},
		{
			eventType: PointerEventType.PET_UP,
			eventInfo: {
				button: InputAction.IA_POINTER,
				hoverText: '放下',
			},
		},
	],
})
```

要隐藏悬停提示，但保留边缘高亮，请将 `hoverText` 的值设置为“”。

```ts
// 创建实体
const chest = engine.addEntity()

// 给实体添加一个 PointerEvents 组件
PointerEvents.create(chest, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_POINTER,
				hoverText: '',
			},
		},
	],
})
```

要隐藏边缘高亮但保留悬停提示，请设置 `showHighlight` 为 *false*.

```ts
// 创建实体
const chest = engine.addEntity()

// 给实体添加一个 PointerEvents 组件
PointerEvents.create(chest, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_POINTER,
				hoverText: '打开门',
				showHighlight: false,
			},
		},
	],
})
```

要同时隐藏悬停提示和边缘高亮，请设置 `showFeedback` 为 *false*。这样做时，光标不会显示任何图标、文本或边缘高亮。你也可以直接移除该 `PointerEvents` 组件。

```ts
// 创建实体
const chest = engine.addEntity()

// 给实体添加一个 PointerEvents 组件
PointerEvents.create(chest, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_POINTER,
				showFeedback: false,
			},
		},
	],
})
```

### 最大距离

有些实体可以有意只在近距离内可交互。如果玩家离实体太远，悬停提示就不会显示在光标旁边。

默认情况下，只有当玩家的 **化身** 处于 *10 米* 实体的范围内时，实体才可点击。你可以通过设置指针事件的 `maxDistance` 属性来更改该范围。

```ts
// 创建实体
const myEntity = engine.addEntity()

// 给实体添加一个 PointerEvents 组件
PointerEvents.create(myEntity, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_POINTER,
				maxDistance: 6,
			},
		},
	],
})

// 实现一个系统来处理该指针事件
engine.addSystem(() => {
	// 获取关于指针事件的数据
	const cmd = inputSystem.getInputCommand(
		InputAction.IA_POINTER,
		PointerEventType.PET_DOWN,
		myEntity
	)

	// 检查点击是否足够近
	if (cmd && cmd.hit && cmd.hit.length < 6) {
		// 执行某项操作
	}
})
```

上面的示例将悬停提示的最大距离设置为 *6 米*。确保处理输入动作的逻辑也遵循相同规则。请参阅 [来自输入动作的数据](#data-from-input-action) 了解如何获取输入动作的距离。

`maxDistance` 是从玩家的 **化身**测量的，因此在第一人称和第三人称中表现相同。

要通过 **当前活动摄像机** 与实体的距离来限制交互，请改用 `maxCameraDistance`。设置两者意味着任一条件通过即可。请参阅 [距离限制](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/register-callback.md#distance-limits) 以获取完整规则集。

{% hint style="warning" %}
**📔 注意**: `maxPlayerDistance` 是以下项的已弃用别名： `maxDistance`。两者都是从角色身上测量距离，因此请使用 `maxDistance` 新的场景中使用
{% endhint %}

## 高级自定义提示

该 `PointerEvents` 组件会在玩家的光标开始悬停在实体上时轻松添加 UI 提示。通常，让提示的行为与玩家在其他 Decentraland 场景中习惯看到的方式保持一致是件好事。不过，在某些情况下，你可能希望以自定义方式提示某物可交互。例如，当玩家开始悬停在实体上时，你可以播放一个轻微的声音。你也可以在悬停时显示实体周围的发光高亮，并在不再悬停时将其隐藏。它还可以用于特定的游戏玩法机制。

使用 `inputSystem.isTriggered()` 函数与 `PointerEventType.PET_HOVER_ENTER` 和 `PointerEventType.PET_HOVER_LEAVE` 事件一起使用，以便在玩家的光标开始指向实体的碰撞体时，以及当光标停止指向它时，执行自定义行为。

下面的示例会将实体放大到 *1.5* 当光标开始指向其碰撞体时，并在光标离开它们时将其恢复到 *1* 大小。

```ts
engine.addSystem(() => {
	const meshEntities = engine.getEntitiesWith(MeshRenderer)
	for (const [entity] of meshEntities) {
		if (
			inputSystem.isTriggered(
				InputAction.IA_POINTER,
				PointerEventType.PET_HOVER_ENTER,
				实体
			)
		) {
			Transform.getMutable(entity).scale = Vector3.create(1.5, 1.5, 1.5)
		}

		if (
			inputSystem.isTriggered(
				InputAction.IA_POINTER,
				PointerEventType.PET_HOVER_LEAVE,
				实体
			)
		) {
			Transform.getMutable(entity).scale = Vector3.create(1, 1, 1)
		}
	}
})
```

{% hint style="warning" %}
**📔 注意**：你想要与之交互的每个实体都必须同时具有 [`PointerEvents` 组件](#show-feedback) 和一个 [碰撞体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md).
{% endhint %}

## 接近事件

通过使用 `PET_DOWN` 来检查接近按钮按下情况，而不管他们的光标瞄准哪里 `作用在使用`的实体上 `interactionType` 字段，紧挨着 `eventInfo`，即可将事件标记为基于接近。

参见 [**接近事件**](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/proximity-events.md) 以了解更多详情，包括可用的辅助函数。

```ts
PointerEvents.create(myEntity, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_DOWN,
			eventInfo: {
				button: InputAction.IA_PRIMARY,
				hoverText: '按 E',
				maxDistance: 5
			},
			interactionType: InteractionType.PROXIMITY,
		},
	],
})

engine.addSystem(() => {
	if (inputSystem.isTriggered(InputAction.IA_PRIMARY, PointerEventType.PET_DOWN, myEntity)) {
		console.log('玩家在实体附近按下了按钮')
	}
})
```

使用像 `pointerEventsSystem.onProximityDown`这样的辅助工具时，一个实体可以同时保留一个光标处理器和一个接近处理器，而且两个回调都会运行。它不能做的是为同一个处理器注册两个不同的按钮。基于系统的事件没有这些限制：你为每种行为定义一条记录。例如，下面是一个实体，它既可以通过光标指向并按 E 进行交互，也可以在靠近它并按 E、且不需要光标指向它的情况下进行交互。

```ts
	PointerEvents.create(myEntity, {
        pointerEvents: [
            {
                eventType: PointerEventType.PET_DOWN,
                eventInfo: {
                    button: InputAction.IA_POINTER,
                    hoverText: '点击',
                    maxDistance: 15,
                },
                interactionType: InteractionType.CURSOR,
            },
            {
                eventType: PointerEventType.PET_DOWN,
                eventInfo: {
                    button: InputAction.IA_PRIMARY,
                    hoverText: '按 E',
                    maxDistance: 15,
                },
                interactionType: InteractionType.PROXIMITY,
            },
        ],
    })
    
    engine.addSystem(() => {
        if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_DOWN, myEntity)) {
            console.log('玩家点击了实体')
        }
        if (inputSystem.isTriggered(InputAction.IA_PRIMARY, PointerEventType.PET_DOWN, myEntity)) {
            console.log('玩家在实体附近按下了按钮')
        }
    })
```

你也可以随时使用 `inputSystem.isTriggered()` 配合 `PET_PROXIMITY_ENTER` 和 `PET_PROXIMITY_LEAVE` 事件类型，用于在玩家进入或离开实体的接近范围时作出反应。这对于为通过接近激活的实体显示额外反馈很有用，例如播放声音或动画。

要使其工作，实体必须具有一个 `PointerEvents` 组件，并定义相应的接近事件类型。

```ts
PointerEvents.create(myEntity, {
	pointerEvents: [
		{
			eventType: PointerEventType.PET_PROXIMITY_ENTER,
			eventInfo: {
				button: InputAction.IA_POINTER,
				maxDistance: 5,
			},
		},
		{
			eventType: PointerEventType.PET_PROXIMITY_LEAVE,
			eventInfo: {
				button: InputAction.IA_POINTER,
				maxDistance: 5,
				showFeedback: false,
			},
		},
	],
})

engine.addSystem(() => {
	if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_PROXIMITY_ENTER, myEntity)) {
		console.log('玩家进入接近范围')
	}
	if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_PROXIMITY_LEAVE, myEntity)) {
		console.log('玩家离开接近范围')
	}
})
```

## 来自输入动作的数据

获取输入动作的数据，例如按下的按钮、命中的实体、射线的方向和长度等。参见（[查看文档](https://github.com/decentraland/docs/tree/main/README.md)）了解所有可用数据的说明。

要获取这些数据，请使用 `inputSystem.getInputCommand`。此函数会返回包含输入事件数据的完整数据结构。

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

如果没有与查询匹配的输入动作，那么 `inputSystem.getInputCommand` 返回 *null*。确保在逻辑中处理这种情况。

### 最大点击距离

要强制设置最大距离，以便实体只能在近距离可点击，请获取 `hit.length` 属性。

```ts
// 实现一个系统来处理该指针事件
engine.addSystem(() => {
	// 获取关于指针事件的数据
	const cmd = inputSystem.getInputCommand(
		InputAction.IA_POINTER,
		PointerEventType.PET_DOWN,
		myEntity
	)

	// 检查点击是否足够近
	if (cmd && cmd.hit && cmd.hit.length < 6) {
		// 执行某项操作
	}
})
```

{% hint style="warning" %}
**📔 注意**：如果你忽略任何距离较远的事件，请确保设置 `maxDistance` 参数在 `PointerEvents` 组件上，以保持行为一致。
{% endhint %}

### 模型中的不同网格

通常， *.glTF* 3D 模型由多个网格组成，每个网格都有各自的内部名称。所有按钮事件都包含被点击的具体网格信息，因此你可以利用这些信息在不同情况下触发不同的点击行为。

要查看模型内部网格的命名，你必须使用编辑工具打开这个 3D 模型，例如 [Blender](https://www.blender.org/) 。

![](https://2460066822-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-c0c8238fe31b8eb541ccb25d29459670a994f03e%2Fmesh-names%20\(1\).png?alt=media)

{% hint style="info" %}
**💡 提示**：你也可以通过记录日志并在控制台中读取，来获知被点击网格的名称。
{% endhint %}

你可以访问 `meshName` 属性，它是 `命中` 对象的一部分，而该对象是由点击事件返回的。

在下面的示例中，我们有一个房屋模型，其中包含一个名为 `firePlace`的网格。我们希望只在其对应网格被点击时才打开壁炉。

```ts
engine.addSystem(() => {
	const cmd = inputSystem.getInputCommand(
		InputAction.IA_POINTER,
		PointerEventType.PET_DOWN,
		myEntity
	)
	if (cmd && cmd.hit?.meshName === 'firePlace') {
		// 点燃壁炉
	}
})
```

{% hint style="warning" %}
**📔 注意**：这只适用于基于指针的事件，不适用于基于接近的事件，因为在那种情况下，模型中的所有网格都可能同时处于玩家附近。
{% 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-zh/chang-jing-sdk7/jiao-hu-xing/an-niu-shi-jian/system-based-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.
