Clicking UIBlocks
- Create a Component that calls the following in
Update()
:
// Pass mouse events to Nova's Interactions API.
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Interaction.Point(new Interaction.Update(ray), Input.GetMouseButton(0));
Add an Interactable to the same Gamebject as the UIBlock you want to click.
Create a Component that will subscribe to and handle click events.
// Assign to the Interactable added in step (2)
public Interactable Button = null;
private void Start()
{
// Listen for OnClick events and log "Clicked!" to the console whenever one occurs.
Button.UIBlock.AddGestureHandler<Gesture.OnClick>((evt) => Debug.Log("Clicked!"));
}
- Play
Input Overview
Nova's input system revolves around three primary concepts:
Concept | Description |
---|---|
Interactions | The entry-point of the input system. A user calls into the Interaction API per input-device-update (e.g. mouse move, mouse click, etc.), to route the raw input information to some UI content. |
GestureRecognizers | Interactable and Scroller components in a connected UIBlock hierarchy responsible for converting per-frame Interaction.Updates to a Nova-supported Gesture. |
Gestures | Events generated based on patterns of one or more consecutive Interaction.Updates. |
The following diagram illustrates the input pipeline, breaking it down into four stages (from left to right):
- A user-written component, e.g. InputManager, calls into Nova's Interaction API.
- Nova's Interaction API determines which GestureRecognizer (i.e. Interactable or Scroller) in the scene will receive the interaction update.
- The GestureRecognizer receiving the interaction update converts the event into one of the Nova-provided Gestures, based on its previous gesture state and the new interaction update, and fires the gesture event on its attached UIBlock. The gesture event propogates through the UIBlock event system.
- A user-written component, e.g. GestureHandler, receives the gesture event and reacts accordingly.
Sending Interactions
The Interaction.Update type represents the pointer state of an input control performing a gesture on a given frame. Some examples of what's meant by pointer and control:
Pointer | Control |
---|---|
Mouse Cursor | Left Mouse Button |
Mouse Cursor | Scroll Wheel |
Touch Point | Index Finger |
Left XR Device Ray | Left XR Device Trigger |
Right XR Device Ray | Right XR Device Joystick |
Every Interaction.Update contains:
Field | Description |
---|---|
ControlID | A unique identifier for the control attached to the input pointer. |
Ray | The current location and direction (in world space) of the input pointer. |
UserData | Any additional data the caller wishes to pass along to the gesture event handler listening for the interaction update. |
The user provides Interaction.Updates to the Interaction API, which will internally:
- Perform a hit-test against all active UIBlocks in the scene.
- Route the Interaction.Update to the appropriate GestureRecognizer (i.e. Interactable or Scroller) attached to a hit UIBlock.
using Nova;
using UnityEngine;
/// <summary>
/// A sample component responsible for sending mouse events through the Interaction API to the UI content.
/// </summary>
public class SimpleInputManager : MonoBehaviour
{
private const uint LeftMouseButtonID = 0;
// In Unity's legacy input system, '0' is the button ID for the left mouse button.
private const int LeftMouseButton = 0;
public void Update()
{
if (!Input.mousePresent)
{
// No mouse present, exit early.
return;
}
/*** Create Interaction.Update from mouse event ***/
// Convert the mouse position to a world-space ray.
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// Create a new Interaction.Update from the ray, and give it a control ID.
Interaction.Update update = new Interaction.Update(ray, controlID: LeftMouseButtonID);
// Is the left mouse button pressed?
bool mousePressed = Input.GetMouseButton(LeftMouseButton);
/*** Pass along to Nova's interaction system ***/
// Call Nova's Interaction.Point method with the interaction update and button state.
Interaction.Point(update, mousePressed);
}
}
Note
Nova uses UIBlock.CalculatedSize to determine the set of UIBlocks intersecting with a given Ray or Sphere and doesn't depend on nor make use of any Unity Physics APIs, including colliders.
Interactable & Scroller
Nova provides two components which process and handle Interaction.Updates passed into the Interaction API. Once the component receives an Interaction.Update, it will fire a corresponding gesture event.
Icon | Component | Description | Captures | Fires Events | Notes |
---|---|---|---|---|---|
Interactable | Processes Interaction.Point data and fires pointer-based events such as hover, click, drag (separate from scrolling), etc. | Doesn't fire Gesture.OnScroll events | |||
Scroller | Scrolls the attached UIBlock's content along its AutoLayout.Axis with an optional iOS-like inertia and bounce effect. Also fires most pointer-based events. | Doesn't fire Gesture.OnDrag events |
Only one Interactable or Scroller component will receive a given Interaction.Update. This means if a Ray or Sphere intersects with multiple UIBlocks, only one of them will fire the corresponding gesture event. For Gesture.OnHover and Gesture.OnPress events, the event will always be fired from the top-most-rendered UIBlock with an Interactable or Scroller component. Gesture.OnDrag and Gesture.OnScroll events may fire from UIBlocks behind other Interactables or Scrollers, depending on the configured drag thresholds and draggable axes.
Handling Gestures
With a system calling into the Interaction API in place and one or more Interactable or Scroller components in the scene prepared to receive interaction updates, the UI is ready to listen for and react to various gesture events.
All gesture events include a copy of the particular event data in the form of different gesture event struct types. All gesture event structs implement the IGestureEvent interface, allowing all Nova-supported gestures to provide:
Property | Description |
---|---|
Interaction | The Interaction.Update responsible for triggering the event. Either passed into or created by an Interaction method. |
Receiver | The UIBlock attached to the same Gamebject as the GestureRecognizer component capturing the interaction update and generating the gesture event. |
Target | A UIBlock ancestor of the Receiver (or the Receiver itself) holding information pertinent to the event handler. |
See Event System to learn more about hierarchical event propogation and handling.
using Nova;
using UnityEngine;
/// <summary>
/// A sample component responsible for subscribing to and handling hover, unhover, and click events.
/// </summary>
public class SimpleButtonHandler : MonoBehaviour
{
// Serialize and assign in Editor
public Interactable Button = null;
public Color DefaultColor = Color.grey;
public Color HoverColor = Color.white;
public void OnEnable()
{
/*** Subscribe to desired gesture events ***/
Button.UIBlock.AddGestureHandler<Gesture.OnHover>(HandleHoverEvent);
Button.UIBlock.AddGestureHandler<Gesture.OnUnhover>(HandleUnhoverEvent);
Button.UIBlock.AddGestureHandler<Gesture.OnClick>(HandleClickEvent);
}
private void HandleHoverEvent(Gesture.OnHover evt)
{
// Change the color of the UIBlock under the pointer.
Button.UIBlock.Color = HoverColor;
}
private void HandleUnhoverEvent(Gesture.OnUnhover evt)
{
// Change the color of the UIBlock exited by the pointer.
Button.UIBlock.Color = DefaultColor;
}
private void HandleClickEvent(Gesture.OnClick evt)
{
// Log a message each time the Button is clicked.
Debug.Log("Clicked!");
}
public void OnDisable()
{
/*** Unsubscribe from gesture events previously subscribed to in OnEnable ***/
Button.UIBlock.RemoveGestureHandler<Gesture.OnHover>(HandleHoverEvent);
Button.UIBlock.RemoveGestureHandler<Gesture.OnUnhover>(HandleUnhoverEvent);
Button.UIBlock.RemoveGestureHandler<Gesture.OnClick>(HandleClickEvent);
}
}