> 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()` 返回 *true*，系统就会响应并运行一些自定义逻辑。

```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()` 返回 *true* 只有当所指示的按钮在游戏循环的当前 tick 中被按下时才会返回 true。如果按钮没有被按下，或者它已经在之前的 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()` 返回 *true* 如果按钮当前正被按住，无论按钮是什么时候被按下的，也无论玩家光标指向哪里。否则返回 *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` *（可选）*：在悬停反馈提示中显示的字符串内容。默认值为“交互”。
  * `showFeedback` *（可选）*：如果为 false，则会同时隐藏该实体的悬停提示和边缘高亮。 *true* 。
  * `showHighlight` *（可选）*：如果为 true，当玩家将光标悬停在实体上时，会看到边缘高亮。 *true* 默认值。仅当 `showFeedback` 为 *true*.
  * `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 `的鼠标，以及一个鼠标用于`.

通过修改 `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.PROXIMITY`的实体上，即可无论光标瞄准哪里都检查接近按钮按下。将 `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 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/system-based-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.
