System Based Events

Learn how to handle user clicks in your scene.

If your scene has multiple similar entities that are all activated using the same logic, you can write a single system to iterates over all of them and describe that behavior only once. This is also the most performant and more data oriented approach.

You can also use a system to detect global input events, so that the scene reacts whenever a key is pressed, without consideration for where the player's cursor is aiming.

If all you want to do is click or push a button on a single entity to activate it, the easiest way is to use the Register a callback approach.

To set more specific custom logic, you might want to deal with the raw data and use the Advanced approach.

For an entity to be interactive, it must have a collider. See obstacles for more details.

Using a system

Check for button events by running one of the helper functions on the input inputSystem namespace on every tick within a system.

For example, the following system uses the inputSystem.isTriggered() function to check if the pointer was clicked. On every tick, it checks if the button was pressed. If inputSystem.isTriggered() returns true, the system runs some custom logic in response.

engine.addSystem(() => {
	if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_DOWN)) {
		// Logic in response to button press
	}
})

The following helper functions are available in the inputSystem namespace, and can be called similarly on every tick:

  • inputSystem.isTriggered: Returns true if an input action ocurred since the last tick.

  • inputSystem.isPressed: Returns true if an input is currently being pressed down. It will return true on every tick until the button goes up again.

  • inputSystem.getInputCommand: Returns an object with data about the input action.

See the sections below for more details on each.

When handling button events on an entity, always provide feedback to the player, so that the player is aware that an entity can be interacted with. If you add a PointerEvents component to an entity, players will see a hint while hovering their cursor on that entity. See Show feedback to learn how you can add hover hints on interactive entities.

Global input events

Use inputSystem.isTriggered to detect button down and button up events on any of the inputs tracked by the SDK.

The example above checks if the button was pressed, regardless of where the pointer is at, or what entities may be in its path.

inputSystem.isTriggered() returns true only if the indicated button was pressed down in the current tick of the game loop. If the button was not pushed down, or it was already down from the previous tick, it returns false.

The inputSystem.isTriggered function takes the following required arguments:

  • InputAction: Which input to listen for, as a value from the InputAction enum. See Pointer buttons for supported options.

  • PointerEventType: What type of event to listen for, as a value from the PointerEventType enum. See Types of pointer events for supported options.

Activate an entity

To detect button events while pointing at a particular entity, pass a third optional argument to inputSystem.isTriggered to specify what entity to check against.

The example above checks on every tick if a single hard-coded entity was pressed with the pointer button (left mouse button).

The inputSystem.isTriggered function takes the following arguments:

  • InputAction: Which input to listen for, as a value from the InputAction enum. See Pointer buttons for supported options.

  • PointerEventType: What type of event to listen for, as a value from the PointerEventType enum. See Types of pointer events for supported options.

  • Entity (optional): What entity to check these events on. If no value is provided, it will check for global presses of the button, regardless of where the player's cursor was pointing at.

Note that in this example we're also adding a PointerEvents component to the entity we want to interact with. This step is necessary, without this component the entity won't be detectable by any of the functions of inputSystem. see Show Feedback for more details on the PointerEvents component.

If there are multiple entities that the player can interact with in the same way, consider using inputSystem.getInputCommand. This command returns infor about a global click command, including the entity ID, you can use this to execute a single function that can handle them all.

See Data from input action for more info. This method also grants you more detailed data about the hit of the pointer event.

Input button up

You can also check for pointer up events in much the same way, by using PointerEventType.PET_UP.

Check for pressed buttons

Check if a button is currently being pressed down by using inputSystem.isPressed() within a system.

inputSystem.isPressed() returns true if the button is currently being held down, no matter when the button was pushed down, and no matter where the player's cursor is pointing at. Otherwise it returns false.

The inputSystem.isPressed function takes a single argument:

  • InputAction: Which input to listen for, as a value from the InputAction enum. See Pointer buttons for supported options.

Handle multiple entities

If your scene has multiple entities that are affected by pointer events in the same way, it makes sense to write a system that iterates over all of them.

This example uses a component query to iterate over all the entities with a PointerEvents component. It then checks each of these entities with inputSystem.isTriggered, iterating over them one by one. If an input action is detected on any of these entities, it carries out custom logic.

Instead of iterating over all the entities with a PointerEvents component in a single system, you might want to write different systems to handle entities that should behave in different ways. The recommended approach is to mark different types of entities with specific custom components, and iterate over them in separate systems.

This example has one system that iterates over all entities that have a custom component named IsDoor and another that iterates over all entities that have a custom component named isGem. In both systems, it checks every matching entity to see if they were activated with the pointer button.

This way of organizing your scene's code is very data oriented and should result in a very efficient use of memory resources.

Show feedback

To display UI hints while pointing at an entity, use the properties in the entity's PointerEvents component.

Whenever the player's cursor points at the colliders in this entity, the UI will display a hover hint to indicate that the entity can be interacted with. See the sections below for details on what you can configure.

The PointerEvents component requires at least one pointer event definition. Each pointer event definition can be configured with the following:

  • eventType: What type of event to listen for, as a value from the PointerEventType enum. See Types of pointer events for supported options.

  • 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 for supported options.

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

Players will see multiple labels, one for each pointer event, displayed radially around the cursor.

The example below combines using PointerEvents to show hover hints, together with a system that actually handles the player's action with custom logic.

Hover Feedback

When a player hovers the cursor over an item with an PointerEvents component, they see:

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

In the example above, the pointer event includes a value for hoverText. This field defines the string to display in the UI while the player points at the entity. By default, this string spells Interact.

💡 Tip: The hoverText string should describe the action that happens when interacting. For example Open, Activate, Grab, Select. These strings should be as short as possible, to avoid stealing too much focus from the player.

If an entity has multiple pointer events on it, the hover hints for each of these are displayed radially around the cursor.

The hoverText of an .UP pointer event is only displayed while the player is already holding down the corresponding key and pointing at the entity.

If an entity has both a DOWN pointer event and an UP pointer event, the hint for the DOWN action is shown while the button is not being pressed. The hint switches to the one from the UP event only when the button is pressed and remains pressed.

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

To hide the edge highlight but leave the hover hint, set showHighlight to 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.

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.

By default, entities are only clickable when the player is within a close range of the entity, at a maximum distance of 10 meters. You can change the maximum distance by setting the maxDistance property of a pointer event.

The example above sets the maximum distance for hover hints to 6 meters. Make sure that the logic for handling the input actions also follows the same rules. See Data from input action for how to obtain the distance of an input action.

Advanced custom hints

The PointerEvents component easily adds UI hints when the player's cursor starts hovering over an entity. It's generally a good thing that hints behave consistently with what players are used to seeing in other Decentraland scenes. However, in some cases you might want to signal that something is interactive in a custom way. For example, you could play a subtle sound when the player starts hovering over the entity. You could also show a glowing highlight around the entity while hovering, and hide it when no longer hovering. It could also be used for specific gameplay mechanics.

Use the inputSystem.isTriggered() function together with the PointerEventType.PET_HOVER_ENTER and PointerEventType.PET_HOVER_LEAVE events to carry out custom behaviors whenever the player's cursor starts pointing at the entity's collider, and whenever the cursor stops pointing at it.

The example below enlarges entities to a size of 1.5 when the cursor starts pointing at their collider, and sets them back at a size of 1 when the cursor leaves them.

Data from input action

Fetch data from an input action, such as the button that was pressed, the entity that was hit, the direction and length of the ray, etc. See (See documentation) for a description of all of the data available.

To fetch this data, use inputSystem.getInputCommand. This function returns the full data structure with data about the input event.

If there was no input action that matches the query, then inputSystem.getInputCommand returns undefined. Make sure that you handle this scenario in your logic.

Max click distance

To enforce a maximum distance, so that an entity is only clickable at close range, fetch hit.length property of the event data.

Different meshes inside a model

Often, .glTF 3D models are made up of multiple meshes, that each have an individual internal name. All button events events include the information of what specific mesh was clicked, so you can use this information to trigger different click behaviors in each case.

To see how the meshes inside the model are named, you must open the 3D model with an editing tool, like Blender for example.



💡 Tip: You can also learn the name of the clicked mesh by logging it and reading it off console.

You access the meshName property as part of the hit object, that's returned by the click event.

In the example below we have a house model that includes a mesh named firePlace. We want to turn on the fireplace only when its corresponding mesh is clicked.

Last updated