Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Navigating the UI via code

Discussion in 'UI Toolkit' started by TheFellhuhn, Nov 14, 2021.

  1. TheFellhuhn

    TheFellhuhn

    Joined:
    Feb 3, 2017
    Posts:
    42
    My game uses SteamInput to get the commands from gamepads and that works quite well. Except that I can't navigate the UI in any way as the XInput gets hijacked by Steam and therefore doesn't reach the InputManager. That is okay as it wouldn't use the controller configurations anyway (mouse and keyboard still works).

    So now I need a way to navigate the UI via code (as response to SteamInput). I need a way to set and get the currently focused UI element. And a way to trigger the submit, cancel and navigation (up, down etc.) events.

    I haven't found anything in the API or documentation. What is currently the best way to achieve this? Can the inputs be faked through the InputManager (that currently ignores all gamepads) via code? Can I trigger the navigation commands of the UI directly?
     
    achimmihca likes this.
  2. uBenoitA

    uBenoitA

    Unity Technologies

    Joined:
    Apr 15, 2020
    Posts:
    198
    Yes you could send the NavigationMoveEvent, NavigationSubmitEvent, NavigationCancelEvent events yourself, indeed, just make sure you set the event's target to be the panel.focusController.focusedElement, and send them using GetComponent<UIDocument>().rootVisualElement.SendEvent(myEvent);

    However, it would be much better if you used Unity's new Input System to remap your SteamInput commands to reusable input mappings that would be sent to UI Toolkit via the InputSystemUIInputModule.
     
  3. TheFellhuhn

    TheFellhuhn

    Joined:
    Feb 3, 2017
    Posts:
    42
    Thanks for your response. That sounds like a good solution. But how can that be done? I can't find anything about that in the sparse documentation. I would assume one would set them in the controller script similar to the InputActionReferences in the InputSystemEventSystem but those don't seem to have any trigger methods. I also can't find anything in the InputSystem that would allow that.
     
  4. TheFellhuhn

    TheFellhuhn

    Joined:
    Feb 3, 2017
    Posts:
    42
    Small update:

    I managed to create a custom device for the new InputSystem which received the input via SteamInput. But the UI completely ignores it. Created a layout which matches the input to the corresponding UI/* actions. The UI ignores it. The device is enabled. Still doesn't work. Tried adding a PlayerInput component which uses the input scheme that only uses the custom device. The UI ignores it. The PlayerInput also only seems to work with the old UI.

    It is a bit frustrating wasting so much time just to trigger a simple UI. :(
    I will experiment further and update if I find a solution. Perhaps someone else will profit from my suffering...
     
  5. TheFellhuhn

    TheFellhuhn

    Joined:
    Feb 3, 2017
    Posts:
    42
    After wasting another day on this I will most likely ditch the UI Toolkit and the new input system. They are just too complicated and/or don't really work.
     
  6. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    700
    The UI is controlled by another action asset, here's a tutorial:

     
  7. TheFellhuhn

    TheFellhuhn

    Joined:
    Feb 3, 2017
    Posts:
    42
    That's the old UI system. Not the UI Toolkit.
     
    reza_b_mirzaei likes this.
  8. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    700
    It works with UI Toolkit. For example you use the scrollwheel to navigate through a scrollview scrollbar, and you can disable it like so:

    upload_2021-11-22_14-57-15.png


    An alternative would be to edit or remove the DefaultInputActions.
     
  9. TheFellhuhn

    TheFellhuhn

    Joined:
    Feb 3, 2017
    Posts:
    42
    Doesn't work with a custom controller though.

    A custom input device which maps to UI/Submit for example and whose south button gets pressed (via code) doesn't trigger the UI at all even though the correct state of the device gets queued to the input system.
     
  10. nikescar

    nikescar

    Joined:
    Nov 16, 2011
    Posts:
    163
    I know, old, but anyone that is looking to do this, here is my solution. Pretty much a cut and pasted from the InputSystemUIInputModule.cs.

    Send your Vector2 from the New Input System navigate Input Action into this:
    Code (CSharp):
    1. private MoveDirection lastMoveDirection;
    2.     private int consecutiveMoveCount;
    3.     private float lastMoveTime;
    4.     private float moveRepeatRate = .1f;
    5.     private float moveRepeatDelay = .5f;
    6.     private void ProcessNavigation(Vector2 movement)
    7.     {
    8.         var usedSelectionChange = false;
    9.         if (EventSystem.current.currentSelectedGameObject == null)
    10.         {
    11.             return;
    12.         }
    13.  
    14.         if (!usedSelectionChange && (!Mathf.Approximately(movement.x, 0f) || !Mathf.Approximately(movement.y, 0f)))
    15.         {
    16.             var time = Time.unscaledTime;
    17.             var moveVector = movement;
    18.  
    19.             var moveDirection = MoveDirection.None;
    20.             if (moveVector.sqrMagnitude > 0)
    21.             {
    22.                 if (Mathf.Abs(moveVector.x) > Mathf.Abs(moveVector.y))
    23.                     moveDirection = moveVector.x > 0 ? MoveDirection.Right : MoveDirection.Left;
    24.                 else
    25.                     moveDirection = moveVector.y > 0 ? MoveDirection.Up : MoveDirection.Down;
    26.             }
    27.  
    28.             if (moveDirection != lastMoveDirection)
    29.             {
    30.                 consecutiveMoveCount = 0;
    31.             }
    32.  
    33.             if (moveDirection != MoveDirection.None)
    34.             {
    35.                 var allow = true;
    36.                 if (consecutiveMoveCount != 0)
    37.                 {
    38.                     if (consecutiveMoveCount > 1)
    39.                         allow = time > lastMoveTime + moveRepeatRate;
    40.                     else
    41.                         allow = time > lastMoveTime + moveRepeatDelay;
    42.                 }
    43.  
    44.                 if (allow)
    45.                 {
    46.                     AxisEventData eventData = new AxisEventData(EventSystem.current);
    47.                     eventData.Reset();
    48.  
    49.                     eventData.moveVector = moveVector;
    50.                     eventData.moveDir = moveDirection;
    51.  
    52.  
    53.                     ExecuteEvents.Execute(EventSystem.current.currentSelectedGameObject, eventData, ExecuteEvents.moveHandler);
    54.                     usedSelectionChange = eventData.used;
    55.  
    56.                     consecutiveMoveCount = consecutiveMoveCount + 1;
    57.                     lastMoveTime = time;
    58.                     lastMoveDirection = moveDirection;
    59.                 }
    60.             }
    61.             else
    62.             {
    63.                 consecutiveMoveCount = 0;
    64.             }
    65.         }
    66.         else
    67.         {
    68.             consecutiveMoveCount = 0;
    69.         }
    70.     }
    Submit:
    Code (CSharp):
    1. private void ProcessSubmit()
    2.     {
    3.         BaseEventData eventData = new BaseEventData(EventSystem.current);
    4.         eventData.Reset();
    5.         ExecuteEvents.Execute(EventSystem.current.currentSelectedGameObject, eventData, ExecuteEvents.submitHandler);
    6.     }
    Cancel:
    Code (CSharp):
    1. private void ProcessCancel()
    2.     {
    3.         BaseEventData eventData = new BaseEventData(EventSystem.current);
    4.         eventData.Reset();
    5.         ExecuteEvents.Execute(EventSystem.current.currentSelectedGameObject, eventData, ExecuteEvents.cancelHandler);
    6.     }
     
    tomtefar and dlorre like this.