Skip to content

Commit

Permalink
hover feedback rules (#445)
Browse files Browse the repository at this point in the history
  • Loading branch information
nearnshaw authored Nov 26, 2024
1 parent e7935f3 commit ab81d94
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 43 deletions.
29 changes: 27 additions & 2 deletions content/creator/sdk7/interactivity/button-events/click-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,36 @@ There are several different ways to handle input actions, depending on the use c

The easiest way to handle click events on an entity is to use the [Scene Editor]({{< ref "/content/creator/scene-editor/about-editor.md" >}}). Use the no-code **On Click** or **On Input Action** Triggers on an item to call actions when clicking on it. Or use **On Global Click**, **On Global Primary** or **On Global Secondary** Triggers to react to global button events. See [Make any item smart]({{< ref "/content/creator/scene-editor/smart-items/make-any-item-smart.md" >}}).

## Simple example

To detect clicks on an entity, use `pointerEventsSystem.onPointerDown`.

```ts
pointerEventsSystem.onPointerDown(
{
entity: myEntity,
opts: { button: InputAction.IA_PRIMARY, hoverText: 'Click' },
},
function () {
console.log('clicked entity')
}
)
```

See [**Register a callback**]({{< ref "/content/creator/sdk7/interactivity/button-events/register-callback.md" >}}) for more information.

## Hover Feedback

Whichever method you use, it's important to make players aware that an entity is interactive. Otherwise, they might completely miss out on the experience you built. It's not a good experience to be clicking on every object hoping for one to respond. Users of Decentraland are used to the pattern that any interactive items offer feedback on hover, so they will discard an item with no feedback as non-interactive.
It's important to make players aware that an entity is interactive. Otherwise, they might completely miss out on the experience you built. It's not a good experience to be clicking on every object hoping for one to respond.

When you use the [**Register a callback**]({{< ref "/content/creator/sdk7/interactivity/button-events/register-callback.md" >}}) method, two kinds of feedback are displayed whenever the player passes their cursor over the object:

- The entity's edge is highlighted. The highlight is green if the entity is close enough to click, red if the entity is too far away.
- A hover hint appears near the cursor with UI text, signalling what will happen if they click.

When using the [**System-based**]({{< ref "/content/creator/sdk7/interactivity/button-events/system-based-events.md" >}}) method, you can achieve the same results by adding a `PointerEvents` component to the clickable entities.

The default way to add feedback is to display a hover hint on the UI whenever the player passes their cursor over the entity's collider. You can implement this behavior by adding a `PointerEvents` component to an entity. The [**Register a callback**]({{< ref "/content/creator/sdk7/interactivity/button-events/register-callback.md" >}}) approach makes this even easier, as you don't have to explicitly create this component.
Both the entity highlight and the hover hint can be disabled via properties in these methods and components.

You could also implement [advanced custom hints]({{< ref "/content/creator/sdk7/interactivity/button-events/system-based-events.md#advanced-custom-hints" >}}), for example you could play a sound, making the entity change color, spin or enlarge while being pointed at, etc. Whatever you do, make sure that it's a clear signifier.

Expand Down
111 changes: 74 additions & 37 deletions content/creator/sdk7/interactivity/button-events/register-callback.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,21 @@ This statement requires two parameters:
- `entity`: The entity to handle
- `opts`: An object with optional additional data:
- `button`: Which button to listen for. See [Pointer buttons]({{< ref "/content/creator/sdk7/interactivity/button-events/click-events.md#pointer-buttons" >}}) for supported options. If no button is specified, then all buttons are listened to, including movement buttons like forward and jump.
- `hoverText`: What string to display in the hover feedback hint. "Interact" by default.
- `hideFeedback`: If true, it hides the hover hint for this entity.
- `maxDistance`: How far away can the player be from the entity to be able to interact with this entity, in meters. If the player is too far, there will be no hover feedback and pointer events won't work.
- `hoverText`: What string to display in the hover feedback hint. "Interact" by default.
- `hideFeedback`: If true, it hides both the hover hint and the edge highlight for this entity. _false_ by default.
- `showHighlight`: If true, players will see the edge highlight when hovering the cursor on the entity. _true_ by default. This value is only considered if `hideFeedback` is _false_.
- `cb`: A callback function to run each time a button down event occurs while pointing at the entity

```ts
pointerEventsSystem.onPointerDown(
{
entity: myEntity,
opts: { button: InputAction.IA_PRIMARY, hoverText: 'Click' },
},
function () {
console.log('clicked entity')
}
{
entity: myEntity,
opts: { button: InputAction.IA_PRIMARY, hoverText: 'Click' },
},
function () {
console.log('clicked entity')
}
)
```

Expand All @@ -54,27 +55,63 @@ The above command leaves the callback function registered, and will be called as
Only one `pointerEventsSystem.onPointerDown` can be registered per entity. Once added, it will keep listening for events till the listener is removed. Do not run this recurrently within a system, as that would keep rewriting the pointer event behavior.
{{< /hint >}}

## Feedback
## Hover Feedback

It's very important to give players some kind of indication that an entity can be interacted with.

When registering an input action with the `EventsSystem`, by default players will see:

It's very important to give players some kind of indication that an entity can be interacted with. When registering an input action with the `EventsSystem`, by default players will see a hover feedback with an icon for the button they need to press and a string that reads "Interact". You can customize this.
- An edge highlight on the entity
- A hover hint near the cursor with an icon for the button they need to press and a string that reads "Interact".

These elements can be toggled and customized.

The hover feedback on the UI displays a different icon depending on what input you select in the `button` field. On PC, it displays an icon with an `E` for `InputAction.IA_PRIMARY`, an `F` for `InputAction.IA_SECONDARY`, and a mouse for `InputAction.IA_POINTER`.

Change the string by changing the `hoverText` value. Keep this string short, so that it's quick to read and isn't too intrusive on the screen.

```ts
pointerEventsSystem.onPointerDown(
{
entity: myEntity,
opts: { button: InputAction.IA_PRIMARY, hoverText: 'Open door' },
},
{
entity: myEntity,
opts: { button: InputAction.IA_PRIMARY, hoverText: 'Open door' },
},
function () {
// open door
}
)
```

To hide the hover hint, but leave the edge highlight, set the value of the `hoverText` to "".

```ts
pointerEventsSystem.onPointerDown(
{entity: myEntity, opts: { button: InputAction.IA_PRIMARY, hoverText: ''}},,
function () {
// open door
console.log("clicked on surprise interactive item")
}
)
```

To hide a hover feedback, set the `hideFeedback` to an true. When doing this, the cursor doesn't show any icons.
To hide the edge highlight but leave the hover hint, set `showHighlight` to _false_.

```ts
pointerEventsSystem.onPointerDown(
{
entity: myEntity,
opts: {
button: InputAction.IA_PRIMARY,
hoverText: 'Open door',
showHighlight: false,
},
},
function () {
console.log('opened secret door')
}
)
```

To hide both the hover hint and the edge highlight, set the `hideFeedback` to an true. When doing this, the cursor doesn't show any icons, text or any edge highlight.

```ts
pointerEventsSystem.onPointerDown(
Expand All @@ -101,13 +138,13 @@ Use `pointerEventsSystem.onPointerUp` to register a callback function that gets

```ts
pointerEventsSystem.onPointerUp(
{
entity: myEntity,
opts: { button: InputAction.IA_PRIMARY, hoverText: 'Button up' },
},
function () {
console.log('button up')
}
{
entity: myEntity,
opts: { button: InputAction.IA_PRIMARY, hoverText: 'Button up' },
},
function () {
console.log('button up')
}
)
```

Expand Down Expand Up @@ -148,10 +185,10 @@ To fetch this data, pass a parameter to the callback function. This parameter co

```ts
pointerEventsSystem.onPointerDown(
{ entity: myEntity, opts: { button: InputAction.IA_PRIMARY } },
function (cmd) {
console.log(cmd.hit.entityId)
}
{ entity: myEntity, opts: { button: InputAction.IA_PRIMARY } },
function (cmd) {
console.log(cmd.hit.entityId)
}
)
```

Expand Down Expand Up @@ -196,15 +233,15 @@ In the example below we have a house model that includes a mesh named `firePlace
```ts
pointerEventsSystem.onPointerDown(
{
entity: myEntity,
opts: { button: InputAction.IA_PRIMARY, hideFeedback: true },
},
function (cmd) {
if (cmd.hit.meshName === 'firePlace') {
// light fire
}
}
{
entity: myEntity,
opts: { button: InputAction.IA_PRIMARY, hideFeedback: true },
},
function (cmd) {
if (cmd.hit.meshName === 'firePlace') {
// light fire
}
}
)
```
-->
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,9 @@ The `PointerEvents` component requires at least one pointer event definition. Ea
- `eventInfo`: An object that can contain the following fields:

- `button` (_required_): Which input to listen for, as a value from the `InputAction` enum. See [Pointer buttons]({{< ref "/content/creator/sdk7/interactivity/button-events/click-events.md#pointer-buttons" >}}) for supported options.
- `hoverText` _(optional)_: What string to display in the UI.
- `hoverText` _(optional)_: What string to display in the hover feedback hint. "Interact" by default.
- `hideFeedback` _(optional)_: If true, it hides both the hover hint and the edge highlight for this entity. _false_ by default.
- `showHighlight` _(optional)_: If true, players will see the edge highlight when hovering the cursor on the entity. _true_ by default. This value is only considered if `hideFeedback` is _false_.
- `maxDistance` _(optional)_: Only show feedback when the player is closer than a certain distance from the entity. Default is _10 meters_.

A single `PointerEvents` component can hold multiple pointer events definitions, that can detect different events for different buttons. Each entity can only have _one_ `PointerEvents` component, but this component can include multiple objects in its `pointerEvents` array, one for each event to respond to.
Expand Down Expand Up @@ -382,11 +384,18 @@ engine.addSystem(() => {
})
```

### Hint messages
### Hover Feedback

When a player hovers the cursor over an item with an `PointerEvents` component, the cursor changes shape to hint to the player that the entity is interactive.
When a player hovers the cursor over an item with an `PointerEvents` component, they see:

You can also display a toast message in the UI that lets the player know what happens if they interact with the entity.
- An edge highlight on the entity
- A hover hint near the cursor with an icon for the button they need to press and a string that reads "Interact".

These elements can be toggled and customized.

The hover feedback on the UI displays a different icon depending on what input you select in the `button` field. On PC, it displays an icon with an `E` for `InputAction.IA_PRIMARY`, an `F` for `InputAction.IA_SECONDARY`, and a mouse for `InputAction.IA_POINTER`.

Change the string by changing the `hoverText` value. Keep this string short, so that it's quick to read and isn't too intrusive on the screen.

```ts
// create entity
Expand Down Expand Up @@ -443,6 +452,67 @@ PointerEvents.create(entity, {
})
```

To hide the hover hint, but leave the edge highlight, set the value of the `hoverText` to "".

```ts
// create entity
const chest = engine.addEntity()

// give entity a PointerEvents component
PointerEvents.create(chest, {
pointerEvents: [
{
eventType: PointerEventType.PET_DOWN,
eventInfo: {
button: InputAction.IA_POINTER,
hoverText: '',
},
},
],
})
```

To hide the edge highlight but leave the hover hint, set `showHighlight` to _false_.

```ts
// create entity
const chest = engine.addEntity()

// give entity a PointerEvents component
PointerEvents.create(chest, {
pointerEvents: [
{
eventType: PointerEventType.PET_DOWN,
eventInfo: {
button: InputAction.IA_POINTER,
hoverText: 'Open door',
showHighlight: false,
},
},
],
})
```

To hide both the hover hint and the edge highlight, set the `hideFeedback` to an true. When doing this, the cursor doesn't show any icons, text or any edge highlight. You could also just remove the `PointerEvents` component from the entity.

```ts
// create entity
const chest = engine.addEntity()

// give entity a PointerEvents component
PointerEvents.create(chest, {
pointerEvents: [
{
eventType: PointerEventType.PET_DOWN,
eventInfo: {
button: InputAction.IA_POINTER,
hideFeedback: true,
},
},
],
})
```

### Max distance

Some entities can be intentionally only interactive at a close range. If a player is too far away from an entity, the hover hint won't be displayed next to the cursor.
Expand Down

0 comments on commit ab81d94

Please sign in to comment.