Search Unity

Unity UI Sliders don't work with custom InputModule

Discussion in 'UGUI & TextMesh Pro' started by Saticmotion, Oct 10, 2019.

  1. Saticmotion

    Saticmotion

    Joined:
    Aug 27, 2014
    Posts:
    15
    I've been working on a custom InputModule that works with a canvas rendered on a sphere. It accepts multiple simultaneous inputs, such as mouse, VR gaze and VR controllers.

    Hovering and clicking works with most UI elements, but not with the standard Slider. I'm not trying to implement dragging, only clicking on a position of the slider, and the Handle moving there. The Handle is receiving some events. It changes color based on hovers and clicks. But clicking on the background does not move the Handle and does not trigger the onValueChanged event. The onValueChanged event itself is not the problem. It is triggered when I set Slider.value in code.

    My InputModule looks like this. The most relevant section starts on line 150:

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.EventSystems;
    4. using UnityEngine.XR;
    5.  
    6. public class SphereUIInputModule: StandaloneInputModule
    7. {
    8.     private new Camera camera;
    9.     private RenderTexture uiTexture;
    10.  
    11.     private Dictionary<int, PointerEventData> pointers;
    12.     private Dictionary<int, Vector3> directions;
    13.     private Dictionary<int, Vector2> positions;
    14.     private Dictionary<int, Vector2> positionResults;
    15.     private Dictionary<int, RaycastResult> raycastResults;
    16.     private Dictionary<int, PointerEventData.FramePressState> clickStates;
    17.     private Dictionary<int, GameObject> previousHovers;
    18.  
    19.     public Controller leftController;
    20.     public Controller rightController;
    21.  
    22.     private const int gazeId = 1;
    23.     private const int rightControllerId = 2;
    24.     private const int leftControllerId = 3;
    25.  
    26.  
    27.     protected override void Awake()
    28.     {
    29.         pointers = new Dictionary<int, PointerEventData>();
    30.         directions = new Dictionary<int, Vector3>();
    31.         positions = new Dictionary<int, Vector2>();
    32.         positionResults = new Dictionary<int, Vector2>();
    33.         raycastResults = new Dictionary<int, RaycastResult>();
    34.         clickStates = new Dictionary<int, PointerEventData.FramePressState>();
    35.         previousHovers = new Dictionary<int, GameObject>();
    36.  
    37.         camera = Camera.main;
    38.         uiTexture = GetComponent<Camera>().targetTexture;
    39.         base.Awake();
    40.     }
    41.  
    42.     //NOTE(Simon): Translate VR controller state to FramePressState
    43.     protected PointerEventData.FramePressState StateForControllerTrigger(Controller controller)
    44.     {
    45.         var pressed = controller.triggerPressed;
    46.         var released = controller.triggerReleased;
    47.  
    48.         if (pressed && released)
    49.         {
    50.             return PointerEventData.FramePressState.PressedAndReleased;
    51.         }
    52.         if (pressed)
    53.         {
    54.             return PointerEventData.FramePressState.Pressed;
    55.         }
    56.         if (released)
    57.         {
    58.             return PointerEventData.FramePressState.Released;
    59.         }
    60.  
    61.         return PointerEventData.FramePressState.NotChanged;
    62.     }
    63.  
    64.     public override void Process()
    65.     {
    66.         PointerEventData leftData;
    67.         GetPointerData(kMouseLeftId, out leftData, true);
    68.      
    69.         leftData.Reset();
    70.  
    71.         //NOTE(Simon): There could be more than 1 inputdevice (VR controllers for example), so store them all in a list
    72.         directions.Clear();
    73.         if (XRSettings.enabled)
    74.         {
    75.             if (VRDevices.loadedControllerSet == VRDevices.LoadedControllerSet.NoControllers)
    76.             {
    77.                 directions.Add(gazeId, camera.ScreenPointToRay(new Vector2(Screen.width, Screen.height)).direction);
    78.             }
    79.             else
    80.             {
    81.                 if (VRDevices.hasRightController)
    82.                 {
    83.                     directions.Add(rightControllerId, rightController.GetComponent<Controller>().CastRay().direction);
    84.                 }
    85.                 if (VRDevices.hasLeftController)
    86.                 {
    87.                     directions.Add(leftControllerId, leftController.GetComponent<Controller>().CastRay().direction);
    88.                 }
    89.             }
    90.         }
    91.         if (Input.mousePresent)
    92.         {
    93.             directions.Add(kMouseLeftId, camera.ScreenPointToRay((Vector2)Input.mousePosition).direction);
    94.         }
    95.  
    96.         //NOTE(Simon): Figure out position on canvas from rays.
    97.         positions.Clear();
    98.         foreach (var direction in directions)
    99.         {
    100.             positions.Add(direction.Key, new Vector2
    101.             {
    102.                 x = uiTexture.width * (0.5f - Mathf.Atan2(direction.Value.z, direction.Value.x) / (2f * Mathf.PI)),
    103.                 y = uiTexture.height * (Mathf.Asin(direction.Value.y) / Mathf.PI + 0.5f)
    104.             });
    105.         }
    106.  
    107.         //NOTE(Simon): Perform raycasts in second camera on the flat canvas, to get hitted gameobjects and positions on flat canvas.
    108.         raycastResults.Clear();
    109.         positionResults.Clear();
    110.         foreach (var position in positions)
    111.         {
    112.             var tempData = new PointerEventData(eventSystem);
    113.             tempData.Reset();
    114.  
    115.             tempData.position = position.Value;
    116.  
    117.             eventSystem.RaycastAll(tempData, m_RaycastResultCache);
    118.             var result = FindFirstRaycast(m_RaycastResultCache);
    119.             if (result.isValid)
    120.             {
    121.                 raycastResults.Add(position.Key, result);
    122.                 positionResults.Add(position.Key, position.Value);
    123.             }
    124.             m_RaycastResultCache.Clear();
    125.         }
    126.  
    127.         //NOTE(Simon): Put data in PointerEventData
    128.         pointers.Clear();
    129.         foreach (var kvp in raycastResults)
    130.         {
    131.             PointerEventData prevData;
    132.             GetPointerData(kvp.Key, out prevData, true);
    133.  
    134.             pointers.Add(kvp.Key, new PointerEventData(eventSystem)
    135.             {
    136.                 delta = positionResults[kvp.Key] - prevData.position,
    137.                 position = positionResults[kvp.Key],
    138.                 scrollDelta = Input.mouseScrollDelta,
    139.                 button = PointerEventData.InputButton.Left,
    140.                 pointerCurrentRaycast = raycastResults[kvp.Key],
    141.                 pointerId = kvp.Key,
    142.             });
    143.         }
    144.  
    145.         clickStates.Clear();
    146.         clickStates.Add(leftControllerId, StateForControllerTrigger(leftController));
    147.         clickStates.Add(rightControllerId, StateForControllerTrigger(rightController));
    148.         clickStates.Add(kMouseLeftId, StateForMouseButton(0));
    149.  
    150.         //NOTE(Simon): Send hover and click events to UI elements
    151.         foreach (var kvp in pointers)
    152.         {
    153.             if (!previousHovers.ContainsKey(kvp.Key))
    154.             {
    155.                 previousHovers.Add(kvp.Key, null);
    156.             }
    157.             if (kvp.Value.pointerCurrentRaycast.gameObject != previousHovers[kvp.Key])
    158.             {
    159.                 ExecuteEvents.ExecuteHierarchy(kvp.Value.pointerCurrentRaycast.gameObject, kvp.Value, ExecuteEvents.pointerEnterHandler);
    160.                 //NOTE(Simon): Check if any other pointers are hovering the object that's just been unhovered.
    161.                 var otherHovers = false;
    162.                 foreach (var currentHover in pointers)
    163.                 {
    164.                     if (currentHover.Value.pointerCurrentRaycast.gameObject == previousHovers[kvp.Key])
    165.                     {
    166.                         otherHovers = true;
    167.                     }
    168.                 }
    169.                 //NOTE(Simon): If no other hovers, send PointerExit Event.
    170.                 if (!otherHovers)
    171.                 {
    172.                     ExecuteEvents.ExecuteHierarchy(previousHovers[kvp.Key], kvp.Value, ExecuteEvents.pointerExitHandler);
    173.                 }
    174.                 previousHovers[kvp.Key] = kvp.Value.pointerCurrentRaycast.gameObject;
    175.             }
    176.             if (clickStates[kvp.Key] == PointerEventData.FramePressState.Pressed)
    177.             {
    178.                 ExecuteEvents.ExecuteHierarchy(kvp.Value.pointerCurrentRaycast.gameObject, kvp.Value, ExecuteEvents.pointerDownHandler);
    179.                 ExecuteEvents.ExecuteHierarchy(kvp.Value.pointerCurrentRaycast.gameObject, kvp.Value, ExecuteEvents.initializePotentialDrag);
    180.                 Debug.Log("Pointer down by " + kvp.Key + " on " + kvp.Value.pointerCurrentRaycast.gameObject);
    181.             }
    182.             if (clickStates[kvp.Key] == PointerEventData.FramePressState.Released || clickStates[kvp.Key] == PointerEventData.FramePressState.PressedAndReleased)
    183.             {
    184.                 ExecuteEvents.ExecuteHierarchy(kvp.Value.pointerCurrentRaycast.gameObject, kvp.Value, ExecuteEvents.pointerClickHandler);
    185.                 ExecuteEvents.ExecuteHierarchy(kvp.Value.pointerCurrentRaycast.gameObject, kvp.Value, ExecuteEvents.pointerUpHandler);
    186.                 Debug.Log("Click by " + kvp.Key + " on " + kvp.Value.pointerCurrentRaycast.gameObject);
    187.             }
    188.         }
    189.     }
    190. }
     
    Last edited: Oct 10, 2019
    Zatagado likes this.